Merge branch 'thyeggman/job-output-steps-context' of https://github.com/github/actions-languageservices into thyeggman/job-output-steps-context
This commit is contained in:
@@ -8,6 +8,14 @@ export type ActionInput = {
|
||||
|
||||
export type ActionInputs = Record<string, ActionInput>;
|
||||
|
||||
// https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#outputs-for-docker-container-and-javascript-actions
|
||||
export type ActionOutput = {
|
||||
description: string;
|
||||
value?: string;
|
||||
};
|
||||
|
||||
export type ActionOutputs = Record<string, ActionOutput>;
|
||||
|
||||
export type ActionReference = {
|
||||
owner: string;
|
||||
name: string;
|
||||
|
||||
@@ -311,6 +311,35 @@ jobs:
|
||||
expect(result.map(x => x.label)).toEqual(["build_id"]);
|
||||
});
|
||||
|
||||
it("env steps context only for current step and job", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
env:
|
||||
envwf: workflow_env
|
||||
jobs:
|
||||
a:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
envjoba: job_a_env
|
||||
b:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
envjobb: job_b_env
|
||||
steps:
|
||||
- name: step a
|
||||
run: echo "hello"
|
||||
env:
|
||||
envstepa: step_a_env
|
||||
- name: step b
|
||||
run: echo "hello \${{ env.|
|
||||
env:
|
||||
envstepb: step_b_env
|
||||
`;
|
||||
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig);
|
||||
|
||||
expect(result.map(x => x.label)).toEqual(["envjobb", "envstepb", "envwf"]);
|
||||
});
|
||||
|
||||
it("inputs", async () => {
|
||||
const input = `
|
||||
on:
|
||||
@@ -409,7 +438,7 @@ jobs:
|
||||
const result = await complete(...getPositionFromCursor(input), undefined, undefined);
|
||||
|
||||
expect(result.map(x => x.label)).not.toContain("inputs");
|
||||
expect(result.map(x => x.label)).not.toContain("cron");
|
||||
expect(result.map(x => x.label)).not.toContain("schedule");
|
||||
});
|
||||
|
||||
it("includes cron schedules", async () => {
|
||||
@@ -428,7 +457,7 @@ jobs:
|
||||
|
||||
const result = await complete(...getPositionFromCursor(input), undefined, undefined);
|
||||
|
||||
expect(result.map(x => x.label)).toContain("cron");
|
||||
expect(result.map(x => x.label)).toContain("schedule");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import {data} from "@github/actions-expressions";
|
||||
import {Dictionary} from "@github/actions-expressions/data/dictionary";
|
||||
import {ExpressionData, Pair} from "@github/actions-expressions/data/expressiondata";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
|
||||
export type ContextProviderConfig = {
|
||||
getContext: (name: string, defaultContext: data.Dictionary | undefined) => Promise<data.Dictionary | undefined>;
|
||||
getContext: (
|
||||
name: string,
|
||||
defaultContext: data.Dictionary | undefined,
|
||||
workflowContext: WorkflowContext
|
||||
) => Promise<data.Dictionary | undefined>;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@ import {data} from "@github/actions-expressions";
|
||||
import {Kind} from "@github/actions-expressions/data/expressiondata";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
import {ContextProviderConfig} from "./config";
|
||||
import {getEnvContext} from "./env";
|
||||
import {getGithubContext} from "./github";
|
||||
import {getInputsContext} from "./inputs";
|
||||
import {getJobContext} from "./job";
|
||||
@@ -36,7 +37,7 @@ export async function getContext(
|
||||
continue;
|
||||
}
|
||||
|
||||
value = (await config?.getContext(contextName, value)) || value;
|
||||
value = (await config?.getContext(contextName, value, workflowContext)) || value;
|
||||
|
||||
context.add(contextName, value);
|
||||
}
|
||||
@@ -78,6 +79,9 @@ function getDefaultContext(name: string, workflowContext: WorkflowContext, mode:
|
||||
|
||||
case "job":
|
||||
return getJobContext(workflowContext);
|
||||
|
||||
case "env":
|
||||
return getEnvContext(workflowContext);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import {data} from "@github/actions-expressions";
|
||||
import {isScalar, isString} from "@github/actions-workflow-parser";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
import {MappingToken} from "@github/actions-workflow-parser/templates/tokens/mapping-token";
|
||||
|
||||
export function getEnvContext(workflowContext: WorkflowContext): data.Dictionary {
|
||||
const d = new data.Dictionary();
|
||||
|
||||
//step env
|
||||
if (workflowContext.step?.env) {
|
||||
envContext(workflowContext.step.env, d);
|
||||
}
|
||||
|
||||
//job env
|
||||
if (workflowContext.job && workflowContext.job.env) {
|
||||
envContext(workflowContext.job.env, d);
|
||||
}
|
||||
|
||||
//workflow env
|
||||
if (workflowContext.template && workflowContext.template.env) {
|
||||
const wfEnv = workflowContext.template.env.assertMapping("workflow env");
|
||||
envContext(wfEnv, d);
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
function envContext(envMap: MappingToken, d: data.Dictionary) {
|
||||
for (const env of envMap) {
|
||||
if (!isString(env.key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const value = isScalar(env.value) ? new data.StringData(env.value.toDisplayString()) : new data.Null();
|
||||
d.add(env.key.value, value);
|
||||
}
|
||||
}
|
||||
@@ -70,7 +70,7 @@ function getEventContext(workflowContext: WorkflowContext): ExpressionData {
|
||||
if (schedule && schedule.length > 0) {
|
||||
const default_cron = schedule[0].cron;
|
||||
// For now, default to the first cron expression only
|
||||
d.add("cron", new data.StringData(default_cron));
|
||||
d.add("schedule", new data.StringData(default_cron));
|
||||
}
|
||||
|
||||
return d;
|
||||
|
||||
@@ -139,7 +139,7 @@ describe("find-token", () => {
|
||||
testFindToken(`on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: [ubuntu-latest, self|`)
|
||||
runs-on: [ubuntu-latest, self|]`)
|
||||
).toEqual({
|
||||
path: [
|
||||
["workflow-root-strict", TokenType.Mapping],
|
||||
|
||||
@@ -350,6 +350,82 @@ jobs:
|
||||
});
|
||||
});
|
||||
|
||||
describe("env context", () => {
|
||||
it("references env within scope", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
jobs:
|
||||
a:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: step a
|
||||
env:
|
||||
step_env: job_a_env
|
||||
run: echo "hello \${{ env.step_env }}
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it("inherits parent env", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
env:
|
||||
envwf: workflow_env
|
||||
jobs:
|
||||
a:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
envjoba: job_a_env
|
||||
steps:
|
||||
- name: step a
|
||||
run: echo "hello \${{ env.envwf }}
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it("references env outside of scope", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
env:
|
||||
envwf: workflow_env
|
||||
jobs:
|
||||
a:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
envjoba: job_a_env
|
||||
steps:
|
||||
- name: step a
|
||||
run: echo "hello"
|
||||
env:
|
||||
envstepa: step_a_env
|
||||
- name: step b
|
||||
run: echo "hello \${{ env.envstepa }}
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", input));
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
message: "Context access might be invalid: envstepa",
|
||||
range: {
|
||||
end: {
|
||||
character: 42,
|
||||
line: 15
|
||||
},
|
||||
start: {
|
||||
character: 23,
|
||||
line: 15
|
||||
}
|
||||
},
|
||||
severity: DiagnosticSeverity.Warning
|
||||
}
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("strategy context", () => {
|
||||
it("reference within a matrix job", async () => {
|
||||
const input = `
|
||||
|
||||
Reference in New Issue
Block a user