Merge pull request #124 from github/joshmgross/reusable-wf-jobs
Add initial support for converting reusable workflow jobs
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
node_modules
|
||||
dist
|
||||
*.md
|
||||
*.js
|
||||
*.json
|
||||
@@ -0,0 +1,5 @@
|
||||
node_modules
|
||||
dist
|
||||
*.md
|
||||
*.js
|
||||
*.json
|
||||
@@ -1,5 +1,6 @@
|
||||
import {WorkflowContext} from "@github/actions-languageservice/context/workflow-context";
|
||||
import {convertWorkflowTemplate, parseWorkflow, TraceWriter} from "@github/actions-workflow-parser";
|
||||
import {isJob} from "@github/actions-workflow-parser/model/type-guards";
|
||||
|
||||
const nullTrace: TraceWriter = {
|
||||
info: x => {},
|
||||
@@ -16,7 +17,14 @@ export function createWorkflowContext(workflow: string, job?: string, stepIndex?
|
||||
const context: WorkflowContext = {uri: "test.yaml", template};
|
||||
|
||||
if (job) {
|
||||
context.job = template.jobs.find(j => j.id.value === job);
|
||||
const workflowJob = template.jobs.find(j => j.id.value === job);
|
||||
if (workflowJob) {
|
||||
if (isJob(workflowJob)) {
|
||||
context.job = workflowJob;
|
||||
} else {
|
||||
context.reusableWorkflowJob = workflowJob;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (stepIndex !== undefined) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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 {isJob} from "@github/actions-workflow-parser/model/type-guards";
|
||||
import {Job, WorkflowJob} from "@github/actions-workflow-parser/model/workflow-template";
|
||||
import {WorkflowContext} from "../context/workflow-context";
|
||||
|
||||
export function getNeedsContext(workflowContext: WorkflowContext): DescriptionDictionary {
|
||||
@@ -17,11 +18,13 @@ export function getNeedsContext(workflowContext: WorkflowContext): DescriptionDi
|
||||
return d;
|
||||
}
|
||||
|
||||
function needsJobContext(job?: Job): DescriptionDictionary {
|
||||
function needsJobContext(job?: WorkflowJob): DescriptionDictionary {
|
||||
// https://docs.github.com/en/actions/learn-github-actions/contexts#needs-context
|
||||
const d = new DescriptionDictionary();
|
||||
|
||||
d.add("outputs", jobOutputs(job));
|
||||
if (job && isJob(job)) {
|
||||
d.add("outputs", jobOutputs(job));
|
||||
}
|
||||
|
||||
// Can be "success", "failure", "cancelled", or "skipped"
|
||||
d.add("result", new data.Null());
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {isMapping, isSequence, WorkflowTemplate} from "@github/actions-workflow-parser";
|
||||
import {Job, Step} from "@github/actions-workflow-parser/model/workflow-template";
|
||||
import {isJob, isReusableWorkflowJob} from "@github/actions-workflow-parser/model/type-guards";
|
||||
import {Step, Job, ReusableWorkflowJob} from "@github/actions-workflow-parser/model/workflow-template";
|
||||
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";
|
||||
@@ -10,9 +11,12 @@ export interface WorkflowContext {
|
||||
|
||||
template: WorkflowTemplate | undefined;
|
||||
|
||||
/** If the context is for a position within a job, this will be the job */
|
||||
/** If the context is for a position within a regular job, this will be the job */
|
||||
job?: Job;
|
||||
|
||||
/** If the context is for a position within a reusable workflow job, this will be the reusable workflow job */
|
||||
reusableWorkflowJob?: ReusableWorkflowJob;
|
||||
|
||||
/** If the context is for a position within a step, this will be the step */
|
||||
step?: Step;
|
||||
}
|
||||
@@ -35,7 +39,15 @@ export function getWorkflowContext(
|
||||
switch (token.definition?.key) {
|
||||
case "job": {
|
||||
const jobID = (token as StringToken).value;
|
||||
context.job = template.jobs.find(job => job.id.value === jobID);
|
||||
const job = template.jobs.find(job => job.id.value === jobID);
|
||||
if (!job) {
|
||||
break;
|
||||
}
|
||||
if (isJob(job)) {
|
||||
context.job = job;
|
||||
} else if (isReusableWorkflowJob(job)) {
|
||||
context.reusableWorkflowJob = job;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "steps": {
|
||||
@@ -54,7 +66,10 @@ export function getWorkflowContext(
|
||||
}
|
||||
}
|
||||
|
||||
context.step = findStep(context.job?.steps, stepsSequence, stepToken);
|
||||
if (context.job && isJob(context.job)) {
|
||||
context.step = findStep(context.job.steps, stepsSequence, stepToken);
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {convertWorkflowTemplate, parseWorkflow} from "@github/actions-workflow-parser";
|
||||
import {ErrorPolicy} from "@github/actions-workflow-parser/model/convert";
|
||||
import {isJob} from "@github/actions-workflow-parser/model/type-guards";
|
||||
import {File} from "@github/actions-workflow-parser/workflows/file";
|
||||
import {TextDocument} from "vscode-languageserver-textdocument";
|
||||
import {DocumentLink} from "vscode-languageserver-types";
|
||||
@@ -27,7 +28,10 @@ export async function documentLinks(document: TextDocument): Promise<DocumentLin
|
||||
const gitHubBaseUri = "https://www.github.com/";
|
||||
|
||||
for (const job of template?.jobs || []) {
|
||||
for (const step of job?.steps || []) {
|
||||
if (!job || !isJob(job)) {
|
||||
continue;
|
||||
}
|
||||
for (const step of job.steps || []) {
|
||||
if ("uses" in step) {
|
||||
const actionRef = parseActionReference(step.uses.value);
|
||||
if (!actionRef) {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import {File} from "@github/actions-workflow-parser/workflows/file";
|
||||
|
||||
export type WorkflowProvider = {
|
||||
getWorkflow: (name: string) => Promise<File | undefined>;
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
node_modules
|
||||
dist
|
||||
*.md
|
||||
*.js
|
||||
*.json
|
||||
@@ -0,0 +1,66 @@
|
||||
import {TemplateContext} from "../../templates/template-context";
|
||||
import {StringToken, MappingToken, BasicExpressionToken} from "../../templates/tokens";
|
||||
import {isSequence, isString} from "../../templates/tokens/type-guards";
|
||||
import {BaseJob} from "../workflow-template";
|
||||
import {convertConcurrency} from "./concurrency";
|
||||
import {handleTemplateTokenErrors} from "./handle-errors";
|
||||
import {IdBuilder} from "./id-builder";
|
||||
|
||||
export function convertBaseJob(
|
||||
context: TemplateContext,
|
||||
jobKey: StringToken,
|
||||
token: MappingToken
|
||||
): Omit<BaseJob, "type"> {
|
||||
const error = new IdBuilder().tryAddKnownId(jobKey.value);
|
||||
if (error) {
|
||||
context.error(jobKey, error);
|
||||
}
|
||||
|
||||
const result: Omit<BaseJob, "type"> = {
|
||||
id: jobKey,
|
||||
name: undefined,
|
||||
needs: undefined,
|
||||
if: new BasicExpressionToken(undefined, undefined, "success()", undefined, undefined, undefined),
|
||||
concurrency: undefined,
|
||||
strategy: undefined
|
||||
};
|
||||
|
||||
for (const item of token) {
|
||||
const propertyName = item.key.assertString("job property name");
|
||||
switch (propertyName.value) {
|
||||
case "concurrency":
|
||||
handleTemplateTokenErrors(item.value, context, undefined, () => convertConcurrency(context, item.value));
|
||||
result.concurrency = item.value;
|
||||
break;
|
||||
|
||||
case "name":
|
||||
result.name = item.value.assertScalar("job name");
|
||||
break;
|
||||
|
||||
case "needs":
|
||||
result.needs = [];
|
||||
if (isString(item.value)) {
|
||||
const jobNeeds = item.value.assertString("job needs id");
|
||||
result.needs.push(jobNeeds);
|
||||
}
|
||||
|
||||
if (isSequence(item.value)) {
|
||||
for (const seqItem of item.value) {
|
||||
const jobNeeds = seqItem.assertString("job needs id");
|
||||
result.needs.push(jobNeeds);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "strategy":
|
||||
result.strategy = item.value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!result.name) {
|
||||
result.name = result.id;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import {TemplateContext} from "../../templates/template-context";
|
||||
import {StringToken, MappingToken} from "../../templates/tokens";
|
||||
import {Job} from "../workflow-template";
|
||||
import {convertBaseJob} from "./base-job";
|
||||
import {convertToJobContainer, convertToJobServices} from "./container";
|
||||
import {handleTemplateTokenErrors} from "./handle-errors";
|
||||
import {convertToActionsEnvironmentRef} from "./job/environment";
|
||||
import {convertRunsOn} from "./job/runs-on";
|
||||
import {convertSteps} from "./steps";
|
||||
|
||||
export function convertJob(context: TemplateContext, jobKey: StringToken, token: MappingToken): Job {
|
||||
const base = convertBaseJob(context, jobKey, token);
|
||||
const result: Job = {
|
||||
type: "job",
|
||||
id: base.id,
|
||||
name: base.name,
|
||||
needs: base.needs,
|
||||
if: base.if,
|
||||
env: undefined,
|
||||
concurrency: base.concurrency,
|
||||
environment: undefined,
|
||||
strategy: base.strategy,
|
||||
"runs-on": undefined,
|
||||
container: undefined,
|
||||
services: undefined,
|
||||
outputs: undefined,
|
||||
steps: []
|
||||
};
|
||||
|
||||
for (const item of token) {
|
||||
const propertyName = item.key.assertString("job property name");
|
||||
switch (propertyName.value) {
|
||||
case "container":
|
||||
convertToJobContainer(context, item.value);
|
||||
result.container = item.value;
|
||||
break;
|
||||
|
||||
case "env":
|
||||
result.env = item.value.assertMapping("job env");
|
||||
break;
|
||||
|
||||
case "environment":
|
||||
handleTemplateTokenErrors(item.value, context, undefined, () =>
|
||||
convertToActionsEnvironmentRef(context, item.value)
|
||||
);
|
||||
result.environment = item.value;
|
||||
break;
|
||||
|
||||
case "outputs":
|
||||
result.outputs = item.value.assertMapping("job outputs");
|
||||
break;
|
||||
|
||||
case "runs-on":
|
||||
handleTemplateTokenErrors(item.value, context, undefined, () => convertRunsOn(context, item.value));
|
||||
result["runs-on"] = item.value;
|
||||
break;
|
||||
|
||||
case "services":
|
||||
convertToJobServices(context, item.value);
|
||||
result.services = item.value;
|
||||
break;
|
||||
|
||||
case "steps":
|
||||
result.steps = convertSteps(context, item.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import {TemplateContext} from "../../../templates/template-context";
|
||||
import {TemplateToken} from "../../../templates/tokens";
|
||||
import {isMapping, isString, isSequence} from "../../../templates/tokens/type-guards";
|
||||
|
||||
type RunsOn = {
|
||||
labels: Set<string>;
|
||||
group: string;
|
||||
};
|
||||
|
||||
export function convertRunsOn(context: TemplateContext, token: TemplateToken): RunsOn {
|
||||
const labels = convertRunsOnLabels(token);
|
||||
|
||||
if (!isMapping(token)) {
|
||||
return {
|
||||
labels,
|
||||
group: ""
|
||||
};
|
||||
}
|
||||
|
||||
let group = "";
|
||||
|
||||
for (const item of token) {
|
||||
const key = item.key.assertString("job runs-on property name");
|
||||
switch (key.value) {
|
||||
case "group": {
|
||||
if (item.value.isExpression) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const groupName = item.value.assertString("job runs-on group name").value;
|
||||
const names = groupName.split("/");
|
||||
switch (names.length) {
|
||||
case 1: {
|
||||
group = groupName;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
if (!["org", "organization", "ent", "enterprise"].includes(names[0])) {
|
||||
context.error(
|
||||
item.value,
|
||||
`Invalid runs-on group name '${groupName}. Please use 'organization/' or 'enterprise/' prefix to target a single runner group.'`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
if (!names[1]) {
|
||||
context.error(item.value, `Invalid runs-on group name '${groupName}'.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
group = groupName;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
context.error(
|
||||
item.value,
|
||||
`Invalid runs-on group name '${groupName}. Please use 'organization/' or 'enterprise/' prefix to target a single runner group.'`
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "labels": {
|
||||
const mapLabels = convertRunsOnLabels(item.value);
|
||||
for (const label of mapLabels) {
|
||||
labels.add(label);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
labels,
|
||||
group
|
||||
};
|
||||
}
|
||||
|
||||
function convertRunsOnLabels(token: TemplateToken): Set<string> {
|
||||
const labels = new Set<string>();
|
||||
if (token.isExpression) {
|
||||
return labels;
|
||||
}
|
||||
|
||||
if (isString(token)) {
|
||||
labels.add(token.value);
|
||||
return labels;
|
||||
}
|
||||
|
||||
if (isSequence(token)) {
|
||||
for (const item of token) {
|
||||
if (item.isExpression) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const label = item.assertString("job runs-on label sequence item");
|
||||
labels.add(label.value);
|
||||
}
|
||||
}
|
||||
|
||||
return labels;
|
||||
}
|
||||
@@ -1,14 +1,10 @@
|
||||
import {TemplateContext} from "../../templates/template-context";
|
||||
import {BasicExpressionToken, MappingToken, StringToken} from "../../templates/tokens";
|
||||
import {StringToken} from "../../templates/tokens";
|
||||
import {TemplateToken} from "../../templates/tokens/template-token";
|
||||
import {isMapping, isSequence, isString} from "../../templates/tokens/type-guards";
|
||||
import {isMapping} from "../../templates/tokens/type-guards";
|
||||
import {Job} from "../workflow-template";
|
||||
import {convertConcurrency} from "./concurrency";
|
||||
import {convertToJobContainer, convertToJobServices} from "./container";
|
||||
import {handleTemplateTokenErrors} from "./handle-errors";
|
||||
import {IdBuilder} from "./id-builder";
|
||||
import {convertToActionsEnvironmentRef} from "./job/environment";
|
||||
import {convertSteps} from "./steps";
|
||||
import {convertJob} from "./job";
|
||||
|
||||
type nodeInfo = {
|
||||
name: string;
|
||||
@@ -100,200 +96,3 @@ function validateNeeds(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function convertJob(context: TemplateContext, jobKey: StringToken, token: MappingToken): Job {
|
||||
const error = new IdBuilder().tryAddKnownId(jobKey.value);
|
||||
if (error) {
|
||||
context.error(jobKey, error);
|
||||
}
|
||||
const result: Job = {
|
||||
type: "job",
|
||||
id: jobKey,
|
||||
name: undefined,
|
||||
needs: undefined,
|
||||
if: new BasicExpressionToken(undefined, undefined, "success()", undefined, undefined, undefined),
|
||||
env: undefined,
|
||||
concurrency: undefined,
|
||||
environment: undefined,
|
||||
strategy: undefined,
|
||||
"runs-on": undefined,
|
||||
container: undefined,
|
||||
services: undefined,
|
||||
outputs: undefined,
|
||||
steps: []
|
||||
};
|
||||
|
||||
for (const item of token) {
|
||||
const propertyName = item.key.assertString("job property name");
|
||||
switch (propertyName.value) {
|
||||
case "concurrency":
|
||||
handleTemplateTokenErrors(item.value, context, undefined, () => convertConcurrency(context, item.value));
|
||||
result.concurrency = item.value;
|
||||
break;
|
||||
|
||||
case "container":
|
||||
// Do early validation, but don't convert
|
||||
convertToJobContainer(context, item.value);
|
||||
result.container = item.value;
|
||||
break;
|
||||
|
||||
case "env":
|
||||
result.env = item.value.assertMapping("job env");
|
||||
break;
|
||||
|
||||
case "environment":
|
||||
handleTemplateTokenErrors(item.value, context, undefined, () =>
|
||||
convertToActionsEnvironmentRef(context, item.value)
|
||||
);
|
||||
result.environment = item.value;
|
||||
break;
|
||||
|
||||
case "name":
|
||||
result.name = item.value.assertScalar("job name");
|
||||
break;
|
||||
|
||||
case "needs":
|
||||
result.needs = [];
|
||||
if (isString(item.value)) {
|
||||
const jobNeeds = item.value.assertString("job needs id");
|
||||
result.needs.push(jobNeeds);
|
||||
}
|
||||
|
||||
if (isSequence(item.value)) {
|
||||
for (const seqItem of item.value) {
|
||||
const jobNeeds = seqItem.assertString("job needs id");
|
||||
result.needs.push(jobNeeds);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "outputs":
|
||||
result.outputs = item.value.assertMapping("job outputs");
|
||||
break;
|
||||
|
||||
case "runs-on":
|
||||
handleTemplateTokenErrors(item.value, context, undefined, () => convertRunsOn(context, item.value));
|
||||
result["runs-on"] = item.value;
|
||||
break;
|
||||
|
||||
case "services":
|
||||
// Do early validation, but don't convert
|
||||
convertToJobServices(context, item.value);
|
||||
result.services = item.value;
|
||||
break;
|
||||
|
||||
case "steps":
|
||||
result.steps = convertSteps(context, item.value);
|
||||
break;
|
||||
|
||||
case "strategy":
|
||||
result.strategy = item.value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!result.name) {
|
||||
result.name = result.id;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
type RunsOn = {
|
||||
labels: Set<string>;
|
||||
group: string;
|
||||
};
|
||||
|
||||
function convertRunsOn(context: TemplateContext, token: TemplateToken): RunsOn {
|
||||
const labels = convertRunsOnLabels(token);
|
||||
|
||||
if (!isMapping(token)) {
|
||||
return {
|
||||
labels,
|
||||
group: ""
|
||||
};
|
||||
}
|
||||
|
||||
let group = "";
|
||||
|
||||
for (const item of token) {
|
||||
const key = item.key.assertString("job runs-on property name");
|
||||
switch (key.value) {
|
||||
case "group": {
|
||||
if (item.value.isExpression) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const groupName = item.value.assertString("job runs-on group name").value;
|
||||
const names = groupName.split("/");
|
||||
switch (names.length) {
|
||||
case 1: {
|
||||
group = groupName;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
if (!["org", "organization", "ent", "enterprise"].includes(names[0])) {
|
||||
context.error(
|
||||
item.value,
|
||||
`Invalid runs-on group name '${groupName}. Please use 'organization/' or 'enterprise/' prefix to target a single runner group.'`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
if (!names[1]) {
|
||||
context.error(item.value, `Invalid runs-on group name '${groupName}'.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
group = groupName;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
context.error(
|
||||
item.value,
|
||||
`Invalid runs-on group name '${groupName}. Please use 'organization/' or 'enterprise/' prefix to target a single runner group.'`
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "labels": {
|
||||
const mapLabels = convertRunsOnLabels(item.value);
|
||||
for (const label of mapLabels) {
|
||||
labels.add(label);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
labels,
|
||||
group
|
||||
};
|
||||
}
|
||||
|
||||
function convertRunsOnLabels(token: TemplateToken): Set<string> {
|
||||
const labels = new Set<string>();
|
||||
if (token.isExpression) {
|
||||
return labels;
|
||||
}
|
||||
|
||||
if (isString(token)) {
|
||||
labels.add(token.value);
|
||||
return labels;
|
||||
}
|
||||
|
||||
if (isSequence(token)) {
|
||||
for (const item of token) {
|
||||
if (item.isExpression) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const label = item.assertString("job runs-on label sequence item");
|
||||
labels.add(label.value);
|
||||
}
|
||||
}
|
||||
|
||||
return labels;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import {TemplateContext} from "../../templates/template-context";
|
||||
import {MappingToken, StringToken} from "../../templates/tokens";
|
||||
import {ReusableWorkflowJob} from "../workflow-template";
|
||||
import {convertBaseJob} from "./base-job";
|
||||
|
||||
export function convertResuableWorkflowJob(
|
||||
context: TemplateContext,
|
||||
jobKey: StringToken,
|
||||
token: MappingToken
|
||||
): ReusableWorkflowJob {
|
||||
const base = convertBaseJob(context, jobKey, token);
|
||||
|
||||
const result: ReusableWorkflowJob = {
|
||||
type: "reusableWorkflowJob",
|
||||
id: base.id,
|
||||
name: base.name,
|
||||
needs: base.needs,
|
||||
if: base.if,
|
||||
concurrency: base.concurrency,
|
||||
strategy: base.strategy,
|
||||
uses: new StringToken(undefined, undefined, "undefined", undefined, undefined)
|
||||
};
|
||||
|
||||
for (const item of token) {
|
||||
const propertyName = item.key.assertString("job property name");
|
||||
switch (propertyName.value) {
|
||||
case "uses":
|
||||
result.uses = item.value.assertString("job item uses");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import {ActionStep, RunStep, Step} from "./workflow-template";
|
||||
import {ActionStep, Job, ReusableWorkflowJob, RunStep, Step, WorkflowJob} from "./workflow-template";
|
||||
|
||||
export function isRunStep(step: Step): step is RunStep {
|
||||
return (step as RunStep).run !== undefined;
|
||||
@@ -7,3 +7,11 @@ export function isRunStep(step: Step): step is RunStep {
|
||||
export function isActionStep(step: Step): step is ActionStep {
|
||||
return (step as ActionStep).uses !== undefined;
|
||||
}
|
||||
|
||||
export function isJob(job: WorkflowJob): job is Job {
|
||||
return job.type === "job";
|
||||
}
|
||||
|
||||
export function isReusableWorkflowJob(job: WorkflowJob): job is ReusableWorkflowJob {
|
||||
return job.type === "reusableWorkflowJob";
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
|
||||
export type WorkflowTemplate = {
|
||||
events: EventsConfig;
|
||||
jobs: Job[];
|
||||
jobs: WorkflowJob[];
|
||||
concurrency: TemplateToken;
|
||||
env: TemplateToken;
|
||||
|
||||
@@ -28,16 +28,25 @@ export type ActionsEnvironmentReference = {
|
||||
url?: TemplateToken;
|
||||
};
|
||||
|
||||
export type Job = {
|
||||
type: string;
|
||||
export type WorkflowJob = Job | ReusableWorkflowJob;
|
||||
|
||||
export type JobType = "job" | "reusableWorkflowJob";
|
||||
|
||||
export type BaseJob = {
|
||||
type: JobType;
|
||||
id: StringToken;
|
||||
name?: ScalarToken;
|
||||
needs?: StringToken[];
|
||||
if: BasicExpressionToken;
|
||||
env?: MappingToken;
|
||||
concurrency?: TemplateToken;
|
||||
environment?: TemplateToken;
|
||||
strategy?: TemplateToken;
|
||||
};
|
||||
|
||||
// `job-factory` in the schema
|
||||
export type Job = BaseJob & {
|
||||
type: "job";
|
||||
env?: MappingToken;
|
||||
environment?: TemplateToken;
|
||||
"runs-on"?: TemplateToken;
|
||||
container?: TemplateToken;
|
||||
services?: TemplateToken;
|
||||
@@ -45,6 +54,12 @@ export type Job = {
|
||||
steps: Step[];
|
||||
};
|
||||
|
||||
// `workflow-job` in the schema
|
||||
export type ReusableWorkflowJob = BaseJob & {
|
||||
type: "reusableWorkflowJob";
|
||||
uses: StringToken;
|
||||
};
|
||||
|
||||
export type Container = {
|
||||
image: StringToken;
|
||||
credentials?: Credential;
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
node_modules
|
||||
dist
|
||||
*.md
|
||||
*.js
|
||||
*.json
|
||||
Reference in New Issue
Block a user