Support needs for reusable jobs

This commit is contained in:
Christopher Schleiden
2023-02-07 14:52:12 -08:00
parent 05605c80c7
commit cd38b2fe76
5 changed files with 110 additions and 31 deletions
@@ -0,0 +1,75 @@
import {DescriptionDictionary} from "@github/actions-expressions/.";
import {WorkflowContext} from "../context/workflow-context";
import {testGetWorkflowContext} from "../test-utils/test-workflow-context";
import {getNeedsContext} from "./needs";
describe("needs context", () => {
describe("invalid workflow context", () => {
it("jobs not defined", () => {
const workflowContext = {} as WorkflowContext;
expect(workflowContext.job).toBeUndefined();
expect(workflowContext.reusableWorkflowJob).toBeUndefined();
const context = getNeedsContext(workflowContext);
expect(context).toEqual(new DescriptionDictionary());
});
});
it("job without needs", async () => {
const workflowContext = await testGetWorkflowContext(`on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: ec|ho`);
const context = getNeedsContext(workflowContext);
expect(context).toEqual(new DescriptionDictionary());
});
it("job with needs", async () => {
const workflowContext = await testGetWorkflowContext(`on: push
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: echo
build:
runs-on: ubuntu-latest
needs: [test]
steps:
- run: ec|ho`);
const context = getNeedsContext(workflowContext);
expect(context.pairs().map(x => x.key)).toEqual(["test"]);
});
it("reusable job without needs", async () => {
const workflowContext = await testGetWorkflowContext(`on: push
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: echo
build:
uses: ./.github/workflows/some-reusable-wor|kflow.yml`);
const context = getNeedsContext(workflowContext);
expect(context).toEqual(new DescriptionDictionary());
});
it("reusable job with needs", async () => {
const workflowContext = await testGetWorkflowContext(`on: push
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: echo
build:
uses: ./.github/workflows/some-reusable-wor|kflow.yml
needs: [test]`);
const context = getNeedsContext(workflowContext);
expect(context.pairs().map(x => x.key)).toEqual(["test"]);
});
});
@@ -6,11 +6,14 @@ import {WorkflowContext} from "../context/workflow-context";
export function getNeedsContext(workflowContext: WorkflowContext): DescriptionDictionary {
const d = new DescriptionDictionary();
if (!workflowContext.job || !workflowContext.job.needs) {
const job = workflowContext.job || workflowContext.reusableWorkflowJob;
if (!job?.needs) {
return d;
}
for (const jobID of workflowContext.job.needs) {
for (const jobID of job.needs) {
const job = workflowContext.template?.jobs.find(job => job.id.value === jobID.value);
d.add(jobID.value, needsJobContext(job));
}
@@ -1,30 +1,5 @@
import {convertWorkflowTemplate, parseWorkflow, WorkflowTemplate} from "@github/actions-workflow-parser";
import {ActionStep, RunStep} from "@github/actions-workflow-parser/model/workflow-template";
import {nullTrace} from "../nulltrace";
import {getPositionFromCursor} from "../test-utils/cursor-position";
import {findToken} from "../utils/find-token";
import {getWorkflowContext, WorkflowContext} from "./workflow-context";
async function testGetWorkflowContext(input: string): Promise<WorkflowContext> {
const [textDocument, pos] = getPositionFromCursor(input);
const result = parseWorkflow(
{
content: textDocument.getText(),
name: "wf.yaml"
},
nullTrace
);
let template: WorkflowTemplate | undefined;
if (result.value) {
template = await convertWorkflowTemplate(result.context, result.value);
}
const {path} = findToken(pos, result.value);
return getWorkflowContext(textDocument.uri, template, path);
}
import {testGetWorkflowContext} from "../test-utils/test-workflow-context";
describe("getWorkflowContext", () => {
it("context for workflow", async () => {
@@ -0,0 +1,26 @@
import {convertWorkflowTemplate, parseWorkflow, WorkflowTemplate} from "@github/actions-workflow-parser";
import {getWorkflowContext, WorkflowContext} from "../context/workflow-context";
import {nullTrace} from "../nulltrace";
import {findToken} from "../utils/find-token";
import {getPositionFromCursor} from "./cursor-position";
export async function testGetWorkflowContext(input: string): Promise<WorkflowContext> {
const [textDocument, pos] = getPositionFromCursor(input);
const result = parseWorkflow(
{
content: textDocument.getText(),
name: "wf.yaml"
},
nullTrace
);
let template: WorkflowTemplate | undefined;
if (result.value) {
template = await convertWorkflowTemplate(result.context, result.value);
}
const {path} = findToken(pos, result.value);
return getWorkflowContext(textDocument.uri, template, path);
}
@@ -1,7 +1,7 @@
import {TemplateContext} from "../../templates/template-context";
import {StringToken, MappingToken, BasicExpressionToken, TemplateToken, ScalarToken} from "../../templates/tokens";
import {BasicExpressionToken, MappingToken, ScalarToken, StringToken, TemplateToken} from "../../templates/tokens";
import {isSequence, isString} from "../../templates/tokens/type-guards";
import {WorkflowJob, Step} from "../workflow-template";
import {Step, WorkflowJob} from "../workflow-template";
import {convertConcurrency} from "./concurrency";
import {convertToJobContainer, convertToJobServices} from "./container";
import {handleTemplateTokenErrors} from "./handle-errors";
@@ -103,7 +103,7 @@ export function convertJob(context: TemplateContext, jobKey: StringToken, token:
type: "reusableWorkflowJob",
id: jobKey,
name: jobName(name, jobKey),
needs: needs ?? [],
needs,
if: new BasicExpressionToken(undefined, undefined, "success()", undefined, undefined, undefined),
ref: workflowJobRef,
"input-definitions": undefined,