Support descriptions for contexts
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import {complete, CompletionItem, trimTokenVector} from "./completion";
|
||||
import {complete, CompletionItem, DescriptionDictionary, trimTokenVector} from "./completion";
|
||||
import {BooleanData} from "./data/boolean";
|
||||
import {Dictionary} from "./data/dictionary";
|
||||
import {StringData} from "./data/string";
|
||||
@@ -19,6 +19,14 @@ const testContext = new Dictionary(
|
||||
}
|
||||
)
|
||||
},
|
||||
{
|
||||
key: "github",
|
||||
value: new DescriptionDictionary({
|
||||
key: "actor",
|
||||
value: new StringData(""),
|
||||
description: "The name of the person or app that initiated the workflow. For example, octocat."
|
||||
})
|
||||
},
|
||||
{
|
||||
key: "secrets",
|
||||
value: new Dictionary({
|
||||
@@ -79,6 +87,14 @@ describe("auto-complete", () => {
|
||||
expect(testComplete("env.FOO")).toEqual(expected);
|
||||
});
|
||||
|
||||
it("includes descriptions", () => {
|
||||
expect(testComplete("github.")).toContainEqual<CompletionItem>({
|
||||
label: "actor",
|
||||
function: false,
|
||||
description: "The name of the person or app that initiated the workflow. For example, octocat."
|
||||
});
|
||||
});
|
||||
|
||||
it("provides suggestions for secrets", () => {
|
||||
const expected = completionItems("AWS_TOKEN");
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {Dictionary, isDictionary} from "./data/dictionary";
|
||||
import {ExpressionData} from "./data/expressiondata";
|
||||
import {ExpressionData, Kind, Pair} from "./data/expressiondata";
|
||||
import {Evaluator} from "./evaluator";
|
||||
import {wellKnownFunctions} from "./funcs";
|
||||
import {FunctionInfo} from "./funcs/info";
|
||||
@@ -12,6 +12,36 @@ export type CompletionItem = {
|
||||
function: boolean;
|
||||
};
|
||||
|
||||
export type DescriptionPair = Pair & {description?: string};
|
||||
|
||||
export class DescriptionDictionary extends Dictionary {
|
||||
private readonly descriptions = new Map<string, string>();
|
||||
|
||||
constructor(...pairs: DescriptionPair[]) {
|
||||
super();
|
||||
|
||||
for (const p of pairs) {
|
||||
this.add(p.key, p.value, p.description);
|
||||
}
|
||||
}
|
||||
|
||||
override add(key: string, value: ExpressionData, description?: string): void {
|
||||
super.add(key, value);
|
||||
if (description) {
|
||||
this.descriptions.set(key, description);
|
||||
}
|
||||
}
|
||||
|
||||
override pairs(): DescriptionPair[] {
|
||||
const pairs = super.pairs();
|
||||
return pairs.map(p => ({...p, description: this.descriptions.get(p.key)}));
|
||||
}
|
||||
}
|
||||
|
||||
export function isDescriptionDictionary(x: ExpressionData): x is DescriptionDictionary {
|
||||
return x.kind === Kind.Dictionary && x instanceof DescriptionDictionary;
|
||||
}
|
||||
|
||||
// Complete returns a list of completion items for the given expression.
|
||||
//
|
||||
// The main functionality is auto-completing functions and context access:
|
||||
@@ -97,7 +127,7 @@ function contextKeys(context: ExpressionData): CompletionItem[] {
|
||||
return (
|
||||
context
|
||||
.pairs()
|
||||
.map(x => completionItemFromContext(x.key))
|
||||
.map(x => completionItemFromContext(x))
|
||||
// Sort contexts
|
||||
.sort((a, b) => a.label.localeCompare(b.label))
|
||||
);
|
||||
@@ -106,12 +136,14 @@ function contextKeys(context: ExpressionData): CompletionItem[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
function completionItemFromContext(context: string): CompletionItem {
|
||||
function completionItemFromContext(pair: DescriptionPair): CompletionItem {
|
||||
const context = pair.key.toString();
|
||||
const parenIndex = context.indexOf("(");
|
||||
const isFunc = parenIndex >= 0 && context.indexOf(")") >= 0;
|
||||
|
||||
return {
|
||||
label: isFunc ? context.substring(0, parenIndex) : context,
|
||||
description: pair.description,
|
||||
function: isFunc
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export {Expr} from "./ast";
|
||||
export {complete, CompletionItem} from "./completion";
|
||||
export {complete, CompletionItem, DescriptionDictionary, DescriptionPair, isDescriptionDictionary} from "./completion";
|
||||
export * as data from "./data";
|
||||
export {ExpressionError} from "./errors";
|
||||
export {Evaluator} from "./evaluator";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {data} from "@github/actions-expressions";
|
||||
import {data, DescriptionDictionary} from "@github/actions-expressions";
|
||||
import {ContextProviderConfig} from "@github/actions-languageservice";
|
||||
import {WorkflowContext} from "@github/actions-languageservice/context/workflow-context";
|
||||
import {isMapping, isString} from "@github/actions-workflow-parser";
|
||||
@@ -23,7 +23,7 @@ export function contextProviders(
|
||||
|
||||
const getContext = async (
|
||||
name: string,
|
||||
defaultContext: data.Dictionary | undefined,
|
||||
defaultContext: DescriptionDictionary | undefined,
|
||||
workflowContext: WorkflowContext
|
||||
) => {
|
||||
switch (name) {
|
||||
@@ -46,8 +46,13 @@ export function contextProviders(
|
||||
|
||||
const secrets = await getSecrets(octokit, cache, repo, environmentName);
|
||||
|
||||
defaultContext = defaultContext || new data.Dictionary();
|
||||
secrets.forEach(secret => defaultContext!.add(secret.value, new data.StringData("***")));
|
||||
defaultContext = defaultContext || new DescriptionDictionary();
|
||||
secrets.repoSecrets.forEach(secret =>
|
||||
defaultContext!.add(secret.value, new data.StringData("***"), "Repository secret")
|
||||
);
|
||||
secrets.environmentSecrets.forEach(secret =>
|
||||
defaultContext!.add(secret.value, new data.StringData("***"), `Secret for environment \`${environmentName}\``)
|
||||
);
|
||||
return defaultContext;
|
||||
}
|
||||
case "steps": {
|
||||
|
||||
@@ -8,28 +8,21 @@ export async function getSecrets(
|
||||
cache: TTLCache,
|
||||
repo: RepositoryContext,
|
||||
environmentName?: string
|
||||
): Promise<StringData[]> {
|
||||
const secrets: StringData[] = [];
|
||||
|
||||
// Repo secrets
|
||||
const repoSecrets = await cache.get(`${repo.owner}/${repo.name}/secrets`, undefined, () =>
|
||||
fetchSecrets(octokit, repo.owner, repo.name)
|
||||
);
|
||||
|
||||
secrets.push(...repoSecrets);
|
||||
|
||||
// Environment secrets
|
||||
if (environmentName) {
|
||||
const envSecrets = await cache.get(
|
||||
`${repo.owner}/${repo.name}/secrets/environment/${environmentName}`,
|
||||
undefined,
|
||||
() => fetchEnvironmentSecrets(octokit, repo.id, environmentName)
|
||||
);
|
||||
|
||||
secrets.push(...envSecrets);
|
||||
}
|
||||
|
||||
return secrets.sort();
|
||||
): Promise<{
|
||||
repoSecrets: StringData[];
|
||||
environmentSecrets: StringData[];
|
||||
}> {
|
||||
return {
|
||||
repoSecrets: await await cache.get(`${repo.owner}/${repo.name}/secrets`, undefined, () =>
|
||||
fetchSecrets(octokit, repo.owner, repo.name)
|
||||
),
|
||||
environmentSecrets:
|
||||
(environmentName &&
|
||||
(await cache.get(`${repo.owner}/${repo.name}/secrets/environment/${environmentName}`, undefined, () =>
|
||||
fetchEnvironmentSecrets(octokit, repo.id, environmentName)
|
||||
))) ||
|
||||
[]
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchSecrets(octokit: Octokit, owner: string, name: string): Promise<StringData[]> {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {data} from "@github/actions-expressions";
|
||||
import {data, DescriptionDictionary} from "@github/actions-expressions";
|
||||
import {getStepsContext as getDefaultStepsContext} from "@github/actions-languageservice/context-providers/steps";
|
||||
import {Octokit} from "@octokit/rest";
|
||||
import nock from "nock";
|
||||
@@ -80,18 +80,29 @@ it("adds action outputs", async () => {
|
||||
expect(stepsContext).toBeDefined();
|
||||
|
||||
expect(stepsContext).toEqual(
|
||||
new data.Dictionary({
|
||||
new DescriptionDictionary({
|
||||
key: "cache-primes",
|
||||
value: new data.Dictionary(
|
||||
value: new DescriptionDictionary(
|
||||
{
|
||||
key: "outputs",
|
||||
value: new data.Dictionary({
|
||||
value: new DescriptionDictionary({
|
||||
key: "cache-hit",
|
||||
value: new data.StringData("A boolean value to indicate an exact match was found for the primary key")
|
||||
value: new data.StringData("A boolean value to indicate an exact match was found for the primary key"),
|
||||
description: "A boolean value to indicate an exact match was found for the primary key"
|
||||
})
|
||||
},
|
||||
{key: "conclusion", value: new data.Null()},
|
||||
{key: "outcome", value: new data.Null()}
|
||||
{
|
||||
key: "conclusion",
|
||||
value: new data.Null(),
|
||||
description:
|
||||
"The result of a completed step after `continue-on-error` is applied. Possible values are `success`, `failure`, `cancelled`, or `skipped`. When a `continue-on-error` step fails, the `outcome` is `failure`, but the final conclusion is `success`."
|
||||
},
|
||||
{
|
||||
key: "outcome",
|
||||
value: new data.Null(),
|
||||
description:
|
||||
"The result of a completed step before `continue-on-error` is applied. Possible values are `success`, `failure`, `cancelled`, or `skipped`. When a `continue-on-error` step fails, the `outcome` is `failure`, but the final conclusion is `success`."
|
||||
}
|
||||
)
|
||||
})
|
||||
);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {data} from "@github/actions-expressions";
|
||||
import {isDictionary} from "@github/actions-expressions/data/dictionary";
|
||||
import {data, DescriptionDictionary, isDescriptionDictionary} from "@github/actions-expressions";
|
||||
import {parseActionReference} from "@github/actions-languageservice/action";
|
||||
import {WorkflowContext} from "@github/actions-languageservice/context/workflow-context";
|
||||
import {isActionStep} from "@github/actions-workflow-parser/model/type-guards";
|
||||
@@ -10,9 +9,9 @@ import {getActionOutputs} from "./action-outputs";
|
||||
export async function getStepsContext(
|
||||
octokit: Octokit,
|
||||
cache: TTLCache,
|
||||
defaultContext: data.Dictionary | undefined,
|
||||
defaultContext: DescriptionDictionary | undefined,
|
||||
workflowContext: WorkflowContext
|
||||
): Promise<data.Dictionary | undefined> {
|
||||
): Promise<DescriptionDictionary | undefined> {
|
||||
if (!defaultContext || !workflowContext.job) {
|
||||
return defaultContext;
|
||||
}
|
||||
@@ -26,7 +25,7 @@ export async function getStepsContext(
|
||||
|
||||
// Copy the default context for each step
|
||||
// If the step is an action, add the action outputs to the context
|
||||
const stepsContext = new data.Dictionary();
|
||||
const stepsContext = new DescriptionDictionary();
|
||||
for (const step of workflowContext.job.steps) {
|
||||
if (!contextSteps.has(step.id)) {
|
||||
continue;
|
||||
@@ -38,7 +37,7 @@ export async function getStepsContext(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isActionStep(step) || !isDictionary(defaultStepContext)) {
|
||||
if (!isActionStep(step) || !isDescriptionDictionary(defaultStepContext)) {
|
||||
stepsContext.add(step.id, defaultStepContext);
|
||||
continue;
|
||||
}
|
||||
@@ -49,23 +48,23 @@ export async function getStepsContext(
|
||||
continue;
|
||||
}
|
||||
|
||||
const stepContext = new data.Dictionary();
|
||||
for (const {key, value} of defaultStepContext.pairs()) {
|
||||
const stepContext = new DescriptionDictionary();
|
||||
for (const {key, value, description} of defaultStepContext.pairs()) {
|
||||
switch (key) {
|
||||
case "outputs":
|
||||
const outputs = await getActionOutputs(octokit, cache, action);
|
||||
if (!outputs) {
|
||||
stepContext.add(key, value);
|
||||
stepContext.add(key, value, description);
|
||||
continue;
|
||||
}
|
||||
const outputsDict = new data.Dictionary();
|
||||
const outputsDict = new DescriptionDictionary();
|
||||
for (const [key, value] of Object.entries(outputs)) {
|
||||
outputsDict.add(key, new data.StringData(value.description));
|
||||
outputsDict.add(key, new data.StringData(value.description), value.description);
|
||||
}
|
||||
stepContext.add("outputs", outputsDict);
|
||||
break;
|
||||
default:
|
||||
stepContext.add(key, value);
|
||||
stepContext.add(key, value, description);
|
||||
}
|
||||
}
|
||||
stepsContext.add(step.id, stepContext);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {data} from "@github/actions-expressions";
|
||||
import {CompletionItemKind} from "vscode-languageserver-types";
|
||||
import {data, DescriptionDictionary} from "@github/actions-expressions";
|
||||
import {CompletionItem, CompletionItemKind} from "vscode-languageserver-types";
|
||||
import {complete, getExpressionInput} from "./complete";
|
||||
import {ContextProviderConfig} from "./context-providers/config";
|
||||
import {registerLogger} from "./log";
|
||||
@@ -10,9 +10,10 @@ const contextProviderConfig: ContextProviderConfig = {
|
||||
getContext: async (context: string) => {
|
||||
switch (context) {
|
||||
case "github":
|
||||
return new data.Dictionary({
|
||||
return new DescriptionDictionary({
|
||||
key: "event",
|
||||
value: new data.StringData("push")
|
||||
value: new data.StringData("push"),
|
||||
description: "The event that triggered the workflow"
|
||||
});
|
||||
}
|
||||
|
||||
@@ -57,6 +58,20 @@ describe("expressions", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("contains description", async () => {
|
||||
const input = "run-name: ${{ github.| }}";
|
||||
const result = await complete(...getPositionFromCursor(input), undefined, undefined);
|
||||
|
||||
expect(result).toContainEqual<CompletionItem>({
|
||||
label: "api_url",
|
||||
documentation: {
|
||||
kind: "markdown",
|
||||
value: "The URL of the GitHub Actions REST API."
|
||||
},
|
||||
kind: CompletionItemKind.Variable
|
||||
});
|
||||
});
|
||||
|
||||
it("single region with existing input", async () => {
|
||||
const input = "run-name: ${{ g| }}";
|
||||
const result = await complete(...getPositionFromCursor(input), undefined, contextProviderConfig);
|
||||
@@ -319,11 +334,11 @@ env:
|
||||
jobs:
|
||||
a:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
env:
|
||||
envjoba: job_a_env
|
||||
b:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
env:
|
||||
envjobb: job_b_env
|
||||
steps:
|
||||
- name: step a
|
||||
@@ -387,7 +402,7 @@ jobs:
|
||||
it("includes expected keys", async () => {
|
||||
const input = `
|
||||
on: push
|
||||
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -452,7 +467,7 @@ jobs:
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -469,7 +484,7 @@ jobs:
|
||||
it("includes event payload", async () => {
|
||||
const input = `
|
||||
on: [push, pull_request]
|
||||
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -1,31 +1,10 @@
|
||||
import {data} from "@github/actions-expressions";
|
||||
import {Dictionary} from "@github/actions-expressions/data/dictionary";
|
||||
import {ExpressionData, Pair} from "@github/actions-expressions/data/expressiondata";
|
||||
import {DescriptionDictionary} from "@github/actions-expressions";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
|
||||
export type ContextProviderConfig = {
|
||||
getContext: (
|
||||
name: string,
|
||||
defaultContext: data.Dictionary | undefined,
|
||||
defaultContext: DescriptionDictionary | undefined,
|
||||
workflowContext: WorkflowContext
|
||||
) => Promise<data.Dictionary | undefined>;
|
||||
) => Promise<DescriptionDictionary | undefined>;
|
||||
};
|
||||
|
||||
/**
|
||||
* DynamicDictionary is a dictionary that returns an empty DynamicDictionary (or other given type)
|
||||
* for any key that is not present.
|
||||
*/
|
||||
export class DynamicDictionary<T extends ExpressionData = Dictionary> extends data.Dictionary {
|
||||
constructor(pairs: Pair[], private creator: () => T = () => new data.Dictionary() as T) {
|
||||
super(...pairs);
|
||||
}
|
||||
|
||||
get(key: string): ExpressionData | undefined {
|
||||
const value = super.get(key);
|
||||
if (value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return this.creator();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import {data} from "@github/actions-expressions";
|
||||
import {data, DescriptionDictionary} from "@github/actions-expressions";
|
||||
import {Kind} from "@github/actions-expressions/data/expressiondata";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
import {ContextProviderConfig} from "./config";
|
||||
import {getDescription} from "./descriptions";
|
||||
import {getEnvContext} from "./env";
|
||||
import {getGithubContext} from "./github";
|
||||
import {getInputsContext} from "./inputs";
|
||||
@@ -13,7 +14,7 @@ import {getStrategyContext} from "./strategy";
|
||||
|
||||
// ContextValue is the type of the value returned by a context provider
|
||||
// Null indicates that the context provider doesn't have any value to provide
|
||||
export type ContextValue = data.Dictionary | data.Null;
|
||||
export type ContextValue = DescriptionDictionary | data.Null;
|
||||
|
||||
export enum Mode {
|
||||
Completion,
|
||||
@@ -26,12 +27,12 @@ export async function getContext(
|
||||
config: ContextProviderConfig | undefined,
|
||||
workflowContext: WorkflowContext,
|
||||
mode: Mode
|
||||
): Promise<data.Dictionary> {
|
||||
const context = new data.Dictionary();
|
||||
): Promise<DescriptionDictionary> {
|
||||
const context = new DescriptionDictionary();
|
||||
|
||||
const filteredNames = filterContextNames(names, workflowContext);
|
||||
for (const contextName of filteredNames) {
|
||||
let value = getDefaultContext(contextName, workflowContext, mode) || new data.Dictionary();
|
||||
let value = getDefaultContext(contextName, workflowContext, mode) || new DescriptionDictionary();
|
||||
if (value.kind === Kind.Null) {
|
||||
context.add(contextName, value);
|
||||
continue;
|
||||
@@ -75,7 +76,11 @@ function getDefaultContext(name: string, workflowContext: WorkflowContext, mode:
|
||||
});
|
||||
|
||||
case "secrets":
|
||||
return objectToDictionary({GITHUB_TOKEN: "***"});
|
||||
return new DescriptionDictionary({
|
||||
key: "GITHUB_TOKEN",
|
||||
value: new data.StringData("***"),
|
||||
description: getDescription("secrets", "GITHUB_TOKEN")
|
||||
});
|
||||
|
||||
case "steps":
|
||||
return getStepsContext(workflowContext);
|
||||
@@ -87,8 +92,9 @@ function getDefaultContext(name: string, workflowContext: WorkflowContext, mode:
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function objectToDictionary(object: {[key: string]: string}): data.Dictionary {
|
||||
const dictionary = new data.Dictionary();
|
||||
function objectToDictionary(object: {[key: string]: string}): DescriptionDictionary {
|
||||
const dictionary = new DescriptionDictionary();
|
||||
|
||||
for (const key in object) {
|
||||
dictionary.add(key, new data.StringData(object[key]));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
|
||||
{
|
||||
"$schema": "./descriptionsSchema.json",
|
||||
"github": {
|
||||
"action": {
|
||||
"description": "The name of the action currently running, or the [`id`](https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions#jobsjob_idstepsid) of a step. GitHub Actions removes special characters, and uses the name `__run` when the current step runs a script without an `id`. If you use the same action more than once in the same job, the name will include a suffix with the sequence number with underscore before it. For example, the first script you run will have the name `__run`, and the second script will be named `__run_2`. Similarly, the second invocation of `actions/checkout` will be `actionscheckout2`."
|
||||
},
|
||||
"action_path": {
|
||||
"description": "The path where an action is located. This property is only supported in composite actions. You can use this path to access files located in the same repository as the action."
|
||||
},
|
||||
"action_ref": {
|
||||
"description": "For a step executing an action, this is the ref of the action being executed. For example, `v2`."
|
||||
},
|
||||
"action_repository": {
|
||||
"description": "For a step executing an action, this is the owner and repository name of the action. For example, `actions/checkout`."
|
||||
},
|
||||
"action_status": {
|
||||
"description": "For a composite action, the current result of the composite action."
|
||||
},
|
||||
"actor": {
|
||||
"description": "The username of the user that triggered the initial workflow run. If the workflow run is a re-run, this value may differ from `github.triggering_actor`. Any workflow re-runs will use the privileges of `github.actor`, even if the actor initiating the re-run (`github.triggering_actor`) has different privileges."
|
||||
},
|
||||
"api_url": {
|
||||
"description": "The URL of the GitHub Actions REST API."
|
||||
},
|
||||
"base_ref": {
|
||||
"description": "The `base_ref` or target branch of the pull request in a workflow run. This property is only available when the event that triggers a workflow run is either `pull_request` or `pull_request_target`."
|
||||
},
|
||||
"env": {
|
||||
"description": "Path on the runner to the file that sets environment variables from workflow commands. This file is unique to the current step and is a different file for each step in a job. For more information, see [Workflow commands](https://docs.github.com/en/actions/learn-github-actions/workflow-commands-for-github-actions#setting-an-environment-variable)."
|
||||
},
|
||||
"event": {
|
||||
"description": "The full event webhook payload. You can access individual properties of the event using this context. This object is identical to the webhook payload of the event that triggered the workflow run, and is different for each event. The webhooks for each GitHub Actions event is linked in [Event that trigger workflows](/articles/events-that-trigger-workflows/). For example, for a workflow run triggered by the [`push` event](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push), this object contains the contents of the [push webhook payload](https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#push)."
|
||||
},
|
||||
"event_name": {
|
||||
"description": "The name of the event that triggered the workflow run."
|
||||
},
|
||||
"event_path": {
|
||||
"description": "The path to the file on the runner that contains the full event webhook payload."
|
||||
},
|
||||
"graphql_url": {
|
||||
"description": "The URL of the GitHub Actions GraphQL API."
|
||||
},
|
||||
"head_ref": {
|
||||
"description": "The `head_ref` or source branch of the pull request in a workflow run. This property is only available when the event that triggers a workflow run is either `pull_request` or `pull_request_target`."
|
||||
},
|
||||
"job": {
|
||||
"description": "The [`job_id`](/actions/reference/workflow-syntax-for-github-actions#jobsjob_id) of the current job. <br /> Note: This context property is set by the Actions runner, and is only available within the execution `steps` of a job. Otherwise, the value of this property will be `null`."
|
||||
},
|
||||
"ref": {
|
||||
"description": "The fully-formed ref of the branch or tag that triggered the workflow run. For workflows triggered by `push`, this is the branch or tag ref that was pushed. For workflows triggered by `pull_request`, this is the pull request merge branch. For workflows triggered by `release`, this is the release tag created. For other triggers, this is the branch or tag ref that triggered the workflow run. This is only set if a branch or tag is available for the event type. The ref given is fully-formed, meaning that for branches the format is `refs/heads/<branch_name>`, for pull requests it is `refs/pull/<pr_number>/merge`, and for tags it is `refs/tags/<tag_name>`. For example, `refs/heads/feature-branch-1`.",
|
||||
"versions": {
|
||||
"ghes": "3.3",
|
||||
"ghae": "3.3"
|
||||
}
|
||||
},
|
||||
"ref_name": {
|
||||
"description": "The short ref name of the branch or tag that triggered the workflow run. This value matches the branch or tag name shown on GitHub. For example, `feature-branch-1`.",
|
||||
"versions": {
|
||||
"ghes": "3.3",
|
||||
"ghae": "3.3"
|
||||
}
|
||||
},
|
||||
"ref_protected": {
|
||||
"description": "`true` if branch protections are configured for the ref that triggered the workflow run.",
|
||||
"versions": {
|
||||
"ghes": "3.3",
|
||||
"ghae": "3.3"
|
||||
}
|
||||
},
|
||||
"ref_type": {
|
||||
"description": "The type of ref that triggered the workflow run. Valid values are `branch` or `tag`.",
|
||||
"versions": {
|
||||
"ghes": "3.3",
|
||||
"ghae": "3.3"
|
||||
}
|
||||
},
|
||||
"path": {
|
||||
"description": "Path on the runner to the file that sets system `PATH` variables from workflow commands. This file is unique to the current step and is a different file for each step in a job. For more information, see [Workflow commands](https://docs.github.com/en/actions/learn-github-actions/workflow-commands-for-github-actions#adding-a-system-path)."
|
||||
},
|
||||
"repository": {
|
||||
"description": "The owner and repository name. For example, `Codertocat/Hello-World`."
|
||||
},
|
||||
"repository_owner": {
|
||||
"description": "The repository owner's name. For example, `Codertocat`."
|
||||
},
|
||||
"repositoryUrl": {
|
||||
"description": "The Git URL to the repository. For example, `git://github.com/codertocat/hello-world.git`."
|
||||
},
|
||||
"retention_days": {
|
||||
"description": "The number of days that workflow run logs and artifacts are kept."
|
||||
},
|
||||
"run_id": {
|
||||
"description": "A unique number for each workflow run within a repository. This number does not change if you re-run the workflow run."
|
||||
},
|
||||
"run_number": {
|
||||
"description": "A unique number for each run of a particular workflow in a repository. This number begins at 1 for the workflow's first run, and increments with each new run. This number does not change if you re-run the workflow run."
|
||||
},
|
||||
"run_attempt": {
|
||||
"description": "A unique number for each attempt of a particular workflow run in a repository. This number begins at 1 for the workflow run's first attempt, and increments with each re-run.",
|
||||
"versions": {
|
||||
"ghes": "3.5",
|
||||
"ghae": "3.4"
|
||||
}
|
||||
},
|
||||
"secret_source": {
|
||||
"description": "The source of a secret used in a workflow. Possible values are `None`, `Actions`, `Dependabot`, or `Codespaces`.",
|
||||
"versions": {
|
||||
"ghes": "3.3",
|
||||
"ghae": "3.3"
|
||||
}
|
||||
},
|
||||
"server_url": {
|
||||
"description": "The URL of the GitHub server. For example: `https://github.com`."
|
||||
},
|
||||
"sha": {
|
||||
"description": "The commit SHA that triggered the workflow. The value of this commit SHA depends on the event that triggered the workflow. For more information, see [Events that trigger workflows.](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows) For example, `ffac537e6cbbf934b08745a378932722df287a53`."
|
||||
},
|
||||
"token": {
|
||||
"description": "A token to authenticate on behalf of the GitHub App installed on your repository. This is functionally equivalent to the `GITHUB_TOKEN` secret. For more information, see [Automatic token authentication](https://docs.github.com/en/actions/security-guides/automatic-token-authentication).\nNote: This context property is set by the Actions runner, and is only available within the execution `steps` of a job. Otherwise, the value of this property will be `null`."
|
||||
},
|
||||
"triggering_actor": {
|
||||
"description": "The username of the user that initiated the workflow run. If the workflow run is a re-run, this value may differ from `github.actor`. Any workflow re-runs will use the privileges of `github.actor`, even if the actor initiating the re-run (`github.triggering_actor`) has different privileges."
|
||||
},
|
||||
"workflow": {
|
||||
"description": "The name of the workflow. If the workflow file doesn't specify a `name`, the value of this property is the full path of the workflow file in the repository."
|
||||
},
|
||||
"workspace": {
|
||||
"description": "The default working directory on the runner for steps, and the default location of your repository when using the [`checkout`](https://github.com/actions/checkout) action."
|
||||
}
|
||||
},
|
||||
"secrets": {
|
||||
"GITHUB_TOKEN": {
|
||||
"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/en/actions/security-guides/automatic-token-authentication)."
|
||||
}
|
||||
},
|
||||
"steps": {
|
||||
"outputs": {
|
||||
"description": "The set of outputs defined for the step."
|
||||
},
|
||||
"conclusion": {
|
||||
"description": "The result of a completed step after `continue-on-error` is applied. Possible values are `success`, `failure`, `cancelled`, or `skipped`. When a `continue-on-error` step fails, the `outcome` is `failure`, but the final conclusion is `success`."
|
||||
},
|
||||
"outcome": {
|
||||
"description": "The result of a completed step before `continue-on-error` is applied. Possible values are `success`, `failure`, `cancelled`, or `skipped`. When a `continue-on-error` step fails, the `outcome` is `failure`, but the final conclusion is `success`."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import descriptions from "./descriptions.json";
|
||||
|
||||
/**
|
||||
* Get a description for a built-in context
|
||||
* @param context Name of the context, for example `github`
|
||||
* @param key Key of the context, for example `actor`
|
||||
* @returns Description if one is found, otherwise undefined
|
||||
*/
|
||||
export function getDescription(context: string, key: string): string | undefined {
|
||||
// The inferred type doesn't quite match the actual type, use any to work around that
|
||||
return (descriptions as any)[context]?.[key]?.description;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"$comment": "Ignore this, just to make VS Code happy"
|
||||
}
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"versions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"description"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import {data} from "@github/actions-expressions";
|
||||
import {data, DescriptionDictionary} 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";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
|
||||
export function getEnvContext(workflowContext: WorkflowContext): data.Dictionary {
|
||||
const d = new data.Dictionary();
|
||||
export function getEnvContext(workflowContext: WorkflowContext): DescriptionDictionary {
|
||||
const d = new DescriptionDictionary();
|
||||
|
||||
//step env
|
||||
if (workflowContext.step?.env) {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import {data} from "@github/actions-expressions";
|
||||
import {data, DescriptionDictionary} from "@github/actions-expressions";
|
||||
import {ExpressionData} from "@github/actions-expressions/data/expressiondata";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
import {getDescription} from "./descriptions";
|
||||
import {eventPayloads} from "./events/eventPayloads";
|
||||
import {getInputsContext} from "./inputs";
|
||||
|
||||
export function getGithubContext(workflowContext: WorkflowContext): data.Dictionary {
|
||||
// https://docs.github.com/en/actions/learn-github-actions/contexts#github-context
|
||||
export function getGithubContext(workflowContext: WorkflowContext): DescriptionDictionary {
|
||||
// https://docs.github.com/en/actions/learn-github-actions/contexts#github-cwontext
|
||||
const keys = [
|
||||
"action",
|
||||
"action_path",
|
||||
@@ -43,13 +44,23 @@ export function getGithubContext(workflowContext: WorkflowContext): data.Diction
|
||||
"workspace"
|
||||
];
|
||||
|
||||
return new data.Dictionary(
|
||||
return new DescriptionDictionary(
|
||||
...keys.map(key => {
|
||||
const description = getDescription("github", key);
|
||||
|
||||
if (key == "event") {
|
||||
return {key, value: getEventContext(workflowContext)};
|
||||
return {
|
||||
key,
|
||||
value: getEventContext(workflowContext),
|
||||
description
|
||||
};
|
||||
}
|
||||
|
||||
return {key, value: new data.Null()};
|
||||
return {
|
||||
key,
|
||||
value: new data.Null(),
|
||||
description
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {data} from "@github/actions-expressions";
|
||||
import {data, DescriptionDictionary} from "@github/actions-expressions";
|
||||
import {InputConfig} from "@github/actions-workflow-parser/model/workflow-template";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
|
||||
export function getInputsContext(workflowContext: WorkflowContext): data.Dictionary {
|
||||
const d = new data.Dictionary();
|
||||
export function getInputsContext(workflowContext: WorkflowContext): DescriptionDictionary {
|
||||
const d = new DescriptionDictionary();
|
||||
|
||||
const events = workflowContext?.template?.events;
|
||||
if (!events) {
|
||||
@@ -23,22 +23,22 @@ export function getInputsContext(workflowContext: WorkflowContext): data.Diction
|
||||
return d;
|
||||
}
|
||||
|
||||
function addInputs(d: data.Dictionary, inputs: {[inputName: string]: InputConfig}) {
|
||||
function addInputs(d: DescriptionDictionary, inputs: {[inputName: string]: InputConfig}) {
|
||||
for (const inputName of Object.keys(inputs)) {
|
||||
const input = inputs[inputName];
|
||||
switch (input.type) {
|
||||
case "choice":
|
||||
if (input.default) {
|
||||
d.add(inputName, new data.StringData(input.default as string));
|
||||
d.add(inputName, new data.StringData(input.default as string), input.description);
|
||||
} else {
|
||||
// Default to the first input or an empty string
|
||||
d.add(inputName, new data.StringData((input.options || [""])[0]));
|
||||
d.add(inputName, new data.StringData((input.options || [""])[0]), input.description);
|
||||
}
|
||||
break;
|
||||
|
||||
case "environment":
|
||||
if (input.default) {
|
||||
d.add(inputName, new data.StringData(input.default as string));
|
||||
d.add(inputName, new data.StringData(input.default as string), input.description);
|
||||
} else {
|
||||
// For now default to an empty value if there is no default value. This will always be an environment, so
|
||||
// we could also dynamically look up environments and default to the first one, but leaving this as a
|
||||
@@ -48,12 +48,12 @@ function addInputs(d: data.Dictionary, inputs: {[inputName: string]: InputConfig
|
||||
break;
|
||||
|
||||
case "boolean":
|
||||
d.add(inputName, new data.BooleanData((input.default as boolean) || false));
|
||||
d.add(inputName, new data.BooleanData((input.default as boolean) || false), input.description);
|
||||
break;
|
||||
|
||||
case "string":
|
||||
default:
|
||||
d.add(inputName, new data.StringData((input.default as string) || inputName));
|
||||
d.add(inputName, new data.StringData((input.default as string) || inputName), input.description);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import {data} from "@github/actions-expressions";
|
||||
import {data, DescriptionDictionary} from "@github/actions-expressions";
|
||||
import {isMapping, isSequence} from "@github/actions-workflow-parser";
|
||||
import {MappingToken} from "@github/actions-workflow-parser/templates/tokens/mapping-token";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
|
||||
export function getJobContext(workflowContext: WorkflowContext): data.Dictionary {
|
||||
export function getJobContext(workflowContext: WorkflowContext): DescriptionDictionary {
|
||||
// https://docs.github.com/en/actions/learn-github-actions/contexts#job-context
|
||||
const jobContext = new data.Dictionary();
|
||||
const jobContext = new DescriptionDictionary();
|
||||
const job = workflowContext.job;
|
||||
if (!job) {
|
||||
return jobContext;
|
||||
@@ -21,7 +21,7 @@ export function getJobContext(workflowContext: WorkflowContext): data.Dictionary
|
||||
// Services
|
||||
const jobServices = job.services;
|
||||
if (jobServices && isMapping(jobServices)) {
|
||||
const servicesContext = new data.Dictionary();
|
||||
const servicesContext = new DescriptionDictionary();
|
||||
for (const service of jobServices) {
|
||||
if (!isMapping(service.value)) {
|
||||
continue;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import {data} from "@github/actions-expressions";
|
||||
import {data, DescriptionDictionary} from "@github/actions-expressions";
|
||||
import {Job} from "@github/actions-workflow-parser/model/workflow-template";
|
||||
import {BasicExpressionToken} from "@github/actions-workflow-parser/templates/tokens/basic-expression-token";
|
||||
import {ExpressionToken} from "@github/actions-workflow-parser/templates/tokens/expression-token";
|
||||
import {MappingToken} from "@github/actions-workflow-parser/templates/tokens/mapping-token";
|
||||
import {SequenceToken} from "@github/actions-workflow-parser/templates/tokens/sequence-token";
|
||||
import {StringToken} from "@github/actions-workflow-parser/templates/tokens/string-token";
|
||||
@@ -64,7 +63,7 @@ describe("matrix context", () => {
|
||||
expect(workflowContext.job).toBeUndefined();
|
||||
|
||||
const context = getMatrixContext(workflowContext, Mode.Validation);
|
||||
expect(context).toEqual(new data.Dictionary());
|
||||
expect(context).toEqual(new DescriptionDictionary());
|
||||
});
|
||||
|
||||
it("strategy not defined", () => {
|
||||
@@ -73,7 +72,7 @@ describe("matrix context", () => {
|
||||
expect(workflowContext.job!.strategy).toBeUndefined();
|
||||
|
||||
const context = getMatrixContext(workflowContext, Mode.Validation);
|
||||
expect(context).toEqual(new data.Dictionary());
|
||||
expect(context).toEqual(new DescriptionDictionary());
|
||||
});
|
||||
|
||||
it("strategy is not a mapping token", () => {
|
||||
@@ -81,7 +80,7 @@ describe("matrix context", () => {
|
||||
expect(workflowContext.job!.strategy).toBeDefined();
|
||||
|
||||
const context = getMatrixContext(workflowContext, Mode.Validation);
|
||||
expect(context).toEqual(new data.Dictionary());
|
||||
expect(context).toEqual(new DescriptionDictionary());
|
||||
});
|
||||
|
||||
it("matrix is not defined", () => {
|
||||
@@ -107,7 +106,7 @@ describe("matrix context", () => {
|
||||
const workflowContext = contextFromStrategy(strategy);
|
||||
|
||||
const context = getMatrixContext(workflowContext, Mode.Validation);
|
||||
expect(context).toEqual(new data.Dictionary());
|
||||
expect(context).toEqual(new DescriptionDictionary());
|
||||
});
|
||||
});
|
||||
|
||||
@@ -161,7 +160,7 @@ describe("matrix context", () => {
|
||||
const context = getMatrixContext(workflowContext, Mode.Completion);
|
||||
|
||||
expect(context).toEqual(
|
||||
new data.Dictionary({
|
||||
new DescriptionDictionary({
|
||||
key: "node",
|
||||
value: new data.Array(new data.StringData("12"), new data.StringData("14"))
|
||||
})
|
||||
@@ -181,7 +180,7 @@ describe("matrix context", () => {
|
||||
const context = getMatrixContext(workflowContext, Mode.Validation);
|
||||
|
||||
expect(context).toEqual(
|
||||
new data.Dictionary({
|
||||
new DescriptionDictionary({
|
||||
key: "version",
|
||||
value: new data.Null()
|
||||
})
|
||||
@@ -195,7 +194,7 @@ describe("matrix context", () => {
|
||||
|
||||
const context = getMatrixContext(workflowContext, Mode.Validation);
|
||||
expect(context).toEqual(
|
||||
new data.Dictionary({
|
||||
new DescriptionDictionary({
|
||||
key: "os",
|
||||
value: new data.Array(new data.StringData("ubuntu-latest"), new data.StringData("windows-latest"))
|
||||
})
|
||||
@@ -210,7 +209,7 @@ describe("matrix context", () => {
|
||||
|
||||
const context = getMatrixContext(workflowContext, Mode.Validation);
|
||||
expect(context).toEqual(
|
||||
new data.Dictionary(
|
||||
new DescriptionDictionary(
|
||||
{
|
||||
key: "os",
|
||||
value: new data.Array(new data.StringData("ubuntu-latest"), new data.StringData("windows-latest"))
|
||||
@@ -238,7 +237,7 @@ describe("matrix context", () => {
|
||||
const context = getMatrixContext(workflowContext, Mode.Validation);
|
||||
|
||||
expect(context).toEqual(
|
||||
new data.Dictionary(
|
||||
new DescriptionDictionary(
|
||||
{
|
||||
key: "os",
|
||||
value: new data.Array(
|
||||
@@ -272,7 +271,7 @@ describe("matrix context", () => {
|
||||
const context = getMatrixContext(workflowContext, Mode.Validation);
|
||||
|
||||
expect(context).toEqual(
|
||||
new data.Dictionary(
|
||||
new DescriptionDictionary(
|
||||
{
|
||||
key: "site",
|
||||
value: new data.Array(new data.StringData("production"), new data.StringData("staging"))
|
||||
@@ -306,7 +305,7 @@ describe("matrix context", () => {
|
||||
const context = getMatrixContext(workflowContext, Mode.Validation);
|
||||
|
||||
expect(context).toEqual(
|
||||
new data.Dictionary(
|
||||
new DescriptionDictionary(
|
||||
{
|
||||
key: "os",
|
||||
value: new data.Array(new data.StringData("macos-latest"), new data.StringData("windows-latest"))
|
||||
@@ -340,7 +339,7 @@ describe("matrix context", () => {
|
||||
|
||||
const context = getMatrixContext(workflowContext, Mode.Validation);
|
||||
|
||||
expect(context).toEqual(new data.Dictionary());
|
||||
expect(context).toEqual(new DescriptionDictionary());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {data} from "@github/actions-expressions";
|
||||
import {data, DescriptionDictionary} from "@github/actions-expressions";
|
||||
import {isBasicExpression, isMapping, isSequence, isString} from "@github/actions-workflow-parser";
|
||||
import {KeyValuePair} from "@github/actions-workflow-parser/templates/tokens/key-value-pair";
|
||||
import {MappingToken} from "@github/actions-workflow-parser/templates/tokens/mapping-token";
|
||||
@@ -10,7 +10,7 @@ export function getMatrixContext(workflowContext: WorkflowContext, mode: Mode):
|
||||
// https://docs.github.com/en/actions/learn-github-actions/contexts#matrix-context
|
||||
const strategy = workflowContext.job?.strategy;
|
||||
if (!strategy || !isMapping(strategy)) {
|
||||
return new data.Dictionary();
|
||||
return new DescriptionDictionary();
|
||||
}
|
||||
|
||||
const matrix = strategy.find("matrix");
|
||||
@@ -25,7 +25,7 @@ export function getMatrixContext(workflowContext: WorkflowContext, mode: Mode):
|
||||
return new data.Null();
|
||||
}
|
||||
|
||||
const d = new data.Dictionary();
|
||||
const d = new DescriptionDictionary();
|
||||
for (const [key, value] of properties) {
|
||||
if (value === undefined) {
|
||||
d.add(key, new data.Null());
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import {data} from "@github/actions-expressions";
|
||||
import {data, DescriptionDictionary} from "@github/actions-expressions";
|
||||
import {isScalar, isString} from "@github/actions-workflow-parser";
|
||||
import {Job} from "@github/actions-workflow-parser/model/workflow-template";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
|
||||
export function getNeedsContext(workflowContext: WorkflowContext): data.Dictionary {
|
||||
const d = new data.Dictionary();
|
||||
export function getNeedsContext(workflowContext: WorkflowContext): DescriptionDictionary {
|
||||
const d = new DescriptionDictionary();
|
||||
if (!workflowContext.job || !workflowContext.job.needs) {
|
||||
return d;
|
||||
}
|
||||
@@ -17,9 +17,9 @@ export function getNeedsContext(workflowContext: WorkflowContext): data.Dictiona
|
||||
return d;
|
||||
}
|
||||
|
||||
function needsJobContext(job?: Job): data.Dictionary {
|
||||
function needsJobContext(job?: Job): DescriptionDictionary {
|
||||
// https://docs.github.com/en/actions/learn-github-actions/contexts#needs-context
|
||||
const d = new data.Dictionary();
|
||||
const d = new DescriptionDictionary();
|
||||
|
||||
d.add("outputs", jobOutputs(job));
|
||||
|
||||
@@ -28,8 +28,8 @@ function needsJobContext(job?: Job): data.Dictionary {
|
||||
return d;
|
||||
}
|
||||
|
||||
function jobOutputs(job?: Job): data.Dictionary {
|
||||
const d = new data.Dictionary();
|
||||
function jobOutputs(job?: Job): DescriptionDictionary {
|
||||
const d = new DescriptionDictionary();
|
||||
if (!job?.outputs) {
|
||||
return d;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import {data} from "@github/actions-expressions";
|
||||
import {data, DescriptionDictionary} from "@github/actions-expressions";
|
||||
import {Step} from "@github/actions-workflow-parser/model/workflow-template";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
import {getDescription} from "./descriptions";
|
||||
|
||||
export function getStepsContext(workflowContext: WorkflowContext): data.Dictionary {
|
||||
const d = new data.Dictionary();
|
||||
export function getStepsContext(workflowContext: WorkflowContext): DescriptionDictionary {
|
||||
const d = new DescriptionDictionary();
|
||||
if (!workflowContext.job?.steps) {
|
||||
return d;
|
||||
}
|
||||
@@ -26,15 +27,15 @@ export function getStepsContext(workflowContext: WorkflowContext): data.Dictiona
|
||||
return d;
|
||||
}
|
||||
|
||||
function stepContext(): data.Dictionary {
|
||||
function stepContext(): DescriptionDictionary {
|
||||
// https://docs.github.com/en/actions/learn-github-actions/contexts#steps-context
|
||||
const d = new data.Dictionary();
|
||||
const d = new DescriptionDictionary();
|
||||
|
||||
d.add("outputs", new data.Null());
|
||||
d.add("outputs", new data.Null(), getDescription("steps", "outputs"));
|
||||
|
||||
// Can be "success", "failure", "cancelled", or "skipped"
|
||||
d.add("conclusion", new data.Null());
|
||||
d.add("outcome", new data.Null());
|
||||
d.add("conclusion", new data.Null(), getDescription("steps", "conclusion"));
|
||||
d.add("outcome", new data.Null(), getDescription("steps", "outcome"));
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import {data} from "@github/actions-expressions";
|
||||
import {data, DescriptionDictionary} from "@github/actions-expressions";
|
||||
import {isMapping, isScalar, isString} from "@github/actions-workflow-parser";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
import {scalarToData} from "../utils/scalar-to-data";
|
||||
|
||||
export function getStrategyContext(workflowContext: WorkflowContext): data.Dictionary {
|
||||
export function getStrategyContext(workflowContext: WorkflowContext): DescriptionDictionary {
|
||||
// https://docs.github.com/en/actions/learn-github-actions/contexts#strategy-context
|
||||
const keys = ["fail-fast", "job-index", "job-total", "max-parallel"];
|
||||
|
||||
const strategy = workflowContext.job?.strategy;
|
||||
if (!strategy || !isMapping(strategy)) {
|
||||
return new data.Dictionary(
|
||||
return new DescriptionDictionary(
|
||||
...keys.map(key => {
|
||||
return {key, value: new data.Null()};
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const strategyContext = new data.Dictionary();
|
||||
const strategyContext = new DescriptionDictionary();
|
||||
for (const pair of strategy) {
|
||||
if (!isString(pair.key)) {
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user