Add jobs context (#160)

This commit is contained in:
Laura Yu
2023-02-23 11:19:07 -08:00
committed by GitHub
parent e8902e78d3
commit 6843c36809
5 changed files with 202 additions and 0 deletions
@@ -729,6 +729,107 @@ jobs:
});
});
describe("jobs context", () => {
it("includes defined jobs", async () => {
const input = `
on:
workflow_call:
# Map the workflow outputs to job outputs
outputs:
result:
description: "one or two"
value: \${{ jobs.| }}
jobs:
one:
runs-on: ubuntu-latest
steps:
- id: a
run: echo hello a
two:
runs-on: ubuntu-latest
steps:
- id: b
run: echo hello b
`;
const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["one", "two"]);
});
it("jobs.<job_id>.result only", async () => {
const input = `
on:
workflow_call:
# Map the workflow outputs to job outputs
outputs:
result:
description: "The result"
value: \${{ jobs.one.| }}
jobs:
one:
runs-on: ubuntu-latest
steps:
- id: a
run: echo hello a
`;
const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["result"]);
});
it("jobs.<job_id>", async () => {
const input = `
on:
workflow_call:
outputs:
output1:
description: "A greeting"
value: \${{ jobs.example_job.| }}
jobs:
example_job:
name: Generate output
runs-on: ubuntu-latest
# Map the job outputs to step outputs
outputs:
output1: "\${{ steps.a.outputs.greeting }}"
steps:
- id: a
run: echo "greeting=hello" >> $GITHUB_OUTPUT
`;
const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["outputs", "result"]);
});
it("jobs.<job_id>.outputs", async () => {
const input = `
on:
workflow_call:
outputs:
output1:
description: "A greeting"
value: \${{ jobs.example_job.outputs.| }}
jobs:
example_job:
name: Generate output
runs-on: ubuntu-latest
# Map the job outputs to step outputs
outputs:
output1: "\${{ steps.a.outputs.greeting }}"
output2: "\${{ steps.b.outputs.greeting }}"
steps:
- id: a
run: echo "greeting=hello" >> $GITHUB_OUTPUT
- id: b
run: echo "greeting=hello" >> $GITHUB_OUTPUT
`;
const result = await complete(...getPositionFromCursor(input), {contextProviderConfig});
expect(result.map(x => x.label)).toEqual(["output1", "output2"]);
});
});
describe("strategy context", () => {
it("strategy is not suggested when outside of a matrix job", async () => {
const input = `
@@ -7,6 +7,7 @@ import {getEnvContext} from "./env";
import {getGithubContext} from "./github";
import {getInputsContext} from "./inputs";
import {getJobContext} from "./job";
import {getJobsContext} from "./jobs";
import {getMatrixContext} from "./matrix";
import {getNeedsContext} from "./needs";
import {getStepsContext} from "./steps";
@@ -61,6 +62,9 @@ function getDefaultContext(name: string, workflowContext: WorkflowContext, mode:
case "job":
return getJobContext(workflowContext);
case "jobs":
return getJobsContext(workflowContext);
case "matrix":
return getMatrixContext(workflowContext, mode);
@@ -172,6 +172,14 @@
"description": "`GITHUB_TOKEN` is a secret that is automatically created for every workflow run, and is always included in the secrets context. For more information, see [Automatic token authentication](https://docs.github.com/actions/security-guides/automatic-token-authentication)."
}
},
"jobs": {
"outputs": {
"description": "The set of outputs of a job in a reusable workflow."
},
"result": {
"description": "The result of a job in the reusable workflow. Possible values are `success`, `failure`, `cancelled`, or `skipped`."
}
},
"steps": {
"outputs": {
"description": "The set of outputs defined for the step."
@@ -0,0 +1,40 @@
import {data, DescriptionDictionary} from "@github/actions-expressions";
import {StringData} from "@github/actions-expressions/data/string";
import {MappingToken} from "@github/actions-workflow-parser/templates/tokens/mapping-token";
import {WorkflowContext} from "../context/workflow-context";
import {getDescription} from "./descriptions";
export function getJobsContext(workflowContext: WorkflowContext): DescriptionDictionary {
// https://docs.github.com/en/actions/learn-github-actions/contexts#jobs-context
const jobsContext = new DescriptionDictionary();
const jobs = workflowContext.template?.jobs;
if (!jobs) {
return jobsContext;
}
for (const job of jobs) {
const jobContext = new DescriptionDictionary();
jobContext.add("result", new data.Null(), getDescription("jobs", "result"));
const outputs = job.outputs || new data.Null();
if (outputs instanceof MappingToken) {
jobContext.add("outputs", createOutputsContext(outputs as MappingToken), getDescription("jobs", "outputs"));
}
jobsContext.add(job.id.toString(), jobContext);
}
return jobsContext;
}
function createOutputsContext(outputs: MappingToken): DescriptionDictionary {
const outputsContext = new DescriptionDictionary();
for (const output of outputs) {
outputsContext.add(
output.key.toString(),
new StringData(output.value.toString()),
getDescription(output.key.toString(), "output")
);
}
return outputsContext;
}
@@ -404,6 +404,55 @@ jobs:
});
});
describe("jobs context", () => {
it("jobs.<job_id>.result", async () => {
const input = `
on:
workflow_call:
# Map the workflow outputs to job outputs
outputs:
successful:
description: "Was job successful"
value: \${{ jobs.example_job.result }}
jobs:
example_job:
runs-on: ubuntu-latest
steps:
- id: a
run: echo hello world`;
const result = await validate(createDocument("wf.yaml", input));
expect(result).toEqual([]);
});
it("jobs.<job_id>.outputs.<output_name>", async () => {
const input = `
on:
workflow_call:
outputs:
output1:
description: "A greeting"
value: \${{ jobs.example_job.outputs.output1 }}
jobs:
example_job:
name: Generate output
runs-on: ubuntu-latest
# Map the job outputs to step outputs
outputs:
output1: "\${{ steps.a.outputs.greeting }}"
steps:
- id: a
run: echo "greeting=hello" >> $GITHUB_OUTPUT`;
const result = await validate(createDocument("wf.yaml", input));
expect(result).toEqual([]);
});
});
describe("env context", () => {
it("references env within scope", async () => {
const input = `