Support reusable workflow outputs in expressions

This commit is contained in:
Josh Gross
2023-02-07 19:43:42 -05:00
parent 63fdcf143f
commit 2358166326
4 changed files with 79 additions and 5 deletions
@@ -72,4 +72,57 @@ jobs:
const context = getNeedsContext(workflowContext);
expect(context.pairs().map(x => x.key)).toEqual(["test"]);
});
describe("outputs", () => {
it("regular job with outputs", async () => {
const workflowContext = await testGetWorkflowContext(`
on: push
jobs:
a:
outputs:
build_id: my-build-id
runs-on: ubuntu-latest
steps:
- run: echo
b:
uses: ./.github/workflows/some-reusable-wor|kflow.yml
needs: [a]
`);
const context = getNeedsContext(workflowContext);
const needs = context.get("a") as DescriptionDictionary;
expect(needs).toBeDefined();
const outputs = needs.get("outputs") as DescriptionDictionary;
expect(outputs).toBeDefined();
expect(outputs.pairs().map(x => x.key)).toEqual(["build_id"]);
});
it("reusable job with outputs", async () => {
const workflowContext = await testGetWorkflowContext(`
on: push
jobs:
a:
uses: ./reusable-workflow-with-outputs.yaml
b:
needs: [a]
runs-on: ubuntu-latest
steps:
- run: ec|ho
`);
const context = getNeedsContext(workflowContext);
const needs = context.get("a") as DescriptionDictionary;
expect(needs).toBeDefined();
const outputs = needs.get("outputs") as DescriptionDictionary;
expect(outputs).toBeDefined();
expect(outputs.pairs().map(x => x.key)).toEqual(["build_id"]);
});
});
});
@@ -1,7 +1,6 @@
import {data, DescriptionDictionary} from "@github/actions-expressions";
import {isScalar, isString} from "@github/actions-workflow-parser";
import {isJob} from "@github/actions-workflow-parser/model/type-guards";
import {Job, WorkflowJob} from "@github/actions-workflow-parser/model/workflow-template";
import {WorkflowJob} from "@github/actions-workflow-parser/model/workflow-template";
import {WorkflowContext} from "../context/workflow-context";
export function getNeedsContext(workflowContext: WorkflowContext): DescriptionDictionary {
@@ -25,7 +24,7 @@ function needsJobContext(job?: WorkflowJob): DescriptionDictionary {
// https://docs.github.com/en/actions/learn-github-actions/contexts#needs-context
const d = new DescriptionDictionary();
if (job && isJob(job)) {
if (job) {
d.add("outputs", jobOutputs(job));
}
@@ -34,7 +33,7 @@ function needsJobContext(job?: WorkflowJob): DescriptionDictionary {
return d;
}
function jobOutputs(job?: Job): DescriptionDictionary {
function jobOutputs(job?: WorkflowJob): DescriptionDictionary {
const d = new DescriptionDictionary();
if (!job?.outputs) {
return d;
@@ -68,6 +68,25 @@ jobs:
`
};
case "./reusable-workflow-with-outputs.yaml":
return {
name: "reusable-workflow-with-outputs.yaml",
content: `
on:
workflow_call:
outputs:
build_id:
description: 'The resulting build ID'
value: 123
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
`
};
default:
throw new Error("File not found");
}
@@ -3,6 +3,7 @@ import {getWorkflowContext, WorkflowContext} from "../context/workflow-context";
import {nullTrace} from "../nulltrace";
import {findToken} from "../utils/find-token";
import {getPositionFromCursor} from "./cursor-position";
import {testFileProvider} from "./test-file-provider";
export async function testGetWorkflowContext(input: string): Promise<WorkflowContext> {
const [textDocument, pos] = getPositionFromCursor(input);
@@ -17,7 +18,9 @@ export async function testGetWorkflowContext(input: string): Promise<WorkflowCon
let template: WorkflowTemplate | undefined;
if (result.value) {
template = await convertWorkflowTemplate(result.context, result.value);
template = await convertWorkflowTemplate(result.context, result.value, undefined, testFileProvider, {
fetchReusableWorkflowDepth: 1
});
}
const {path} = findToken(pos, result.value);