Add jobs context (#160)
This commit is contained in:
@@ -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 = `
|
||||
|
||||
Reference in New Issue
Block a user