Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 83de320ba9 | |||
| 74e6638098 | |||
| f8b8b57248 | |||
| aa1e7d8aec | |||
| bd6ce5923b | |||
| 3de9820cd8 | |||
| a7f581bde5 |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/expressions",
|
||||
"version": "0.3.46",
|
||||
"version": "0.3.49",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"source": "./src/index.ts",
|
||||
|
||||
@@ -54,7 +54,8 @@ describe("FeatureFlags", () => {
|
||||
expect(flags.getEnabledFeatures()).toEqual([
|
||||
"missingInputsQuickfix",
|
||||
"blockScalarChompingWarning",
|
||||
"allowCaseFunction"
|
||||
"allowCaseFunction",
|
||||
"allowCopilotRequestsPermission"
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -34,6 +34,12 @@ export interface ExperimentalFeatures {
|
||||
* @default false
|
||||
*/
|
||||
allowCaseFunction?: boolean;
|
||||
|
||||
/**
|
||||
* Enable the copilot-requests permission in workflow permissions.
|
||||
* @default false
|
||||
*/
|
||||
allowCopilotRequestsPermission?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,7 +54,8 @@ export type ExperimentalFeatureKey = Exclude<keyof ExperimentalFeatures, "all">;
|
||||
const allFeatureKeys: ExperimentalFeatureKey[] = [
|
||||
"missingInputsQuickfix",
|
||||
"blockScalarChompingWarning",
|
||||
"allowCaseFunction"
|
||||
"allowCaseFunction",
|
||||
"allowCopilotRequestsPermission"
|
||||
];
|
||||
|
||||
export class FeatureFlags {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/languageserver",
|
||||
"version": "0.3.46",
|
||||
"version": "0.3.49",
|
||||
"description": "Language server for GitHub Actions",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
@@ -48,8 +48,8 @@
|
||||
"actions-languageserver": "./bin/actions-languageserver"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/languageservice": "^0.3.46",
|
||||
"@actions/workflow-parser": "^0.3.46",
|
||||
"@actions/languageservice": "^0.3.49",
|
||||
"@actions/workflow-parser": "^0.3.49",
|
||||
"@octokit/rest": "^21.1.1",
|
||||
"@octokit/types": "^9.0.0",
|
||||
"vscode-languageserver": "^8.0.2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/languageservice",
|
||||
"version": "0.3.46",
|
||||
"version": "0.3.49",
|
||||
"description": "Language service for GitHub Actions",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
@@ -47,8 +47,8 @@
|
||||
"watch": "tsc --build tsconfig.build.json --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/expressions": "^0.3.46",
|
||||
"@actions/workflow-parser": "^0.3.46",
|
||||
"@actions/expressions": "^0.3.49",
|
||||
"@actions/workflow-parser": "^0.3.49",
|
||||
"vscode-languageserver-textdocument": "^1.0.7",
|
||||
"vscode-languageserver-types": "^3.17.2",
|
||||
"vscode-uri": "^3.0.8",
|
||||
|
||||
@@ -925,3 +925,93 @@ jobs:
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("schedule timezone completion", () => {
|
||||
it("includes timezone for schedule", async () => {
|
||||
const input = `on:
|
||||
schedule:
|
||||
- |`;
|
||||
const result = await complete(...getPositionFromCursor(input));
|
||||
|
||||
expect(result).not.toBeUndefined();
|
||||
const labels = result.map(x => x.label);
|
||||
expect(labels).toContain("cron");
|
||||
expect(labels).toContain("timezone");
|
||||
});
|
||||
});
|
||||
|
||||
describe("permissions copilot-requests completion", () => {
|
||||
it("includes copilot-requests when allowCopilotRequestsPermission is enabled", async () => {
|
||||
const input = `on: push
|
||||
permissions:
|
||||
|`;
|
||||
const result = await complete(...getPositionFromCursor(input), {
|
||||
featureFlags: new FeatureFlags({allowCopilotRequestsPermission: true})
|
||||
});
|
||||
|
||||
expect(result).not.toBeUndefined();
|
||||
const labels = result.map(x => x.label);
|
||||
expect(labels).toContain("actions");
|
||||
expect(labels).toContain("copilot-requests");
|
||||
});
|
||||
|
||||
it("excludes copilot-requests when allowCopilotRequestsPermission is disabled", async () => {
|
||||
const input = `on: push
|
||||
permissions:
|
||||
|`;
|
||||
const result = await complete(...getPositionFromCursor(input), {
|
||||
featureFlags: new FeatureFlags({allowCopilotRequestsPermission: false})
|
||||
});
|
||||
|
||||
expect(result).not.toBeUndefined();
|
||||
const labels = result.map(x => x.label);
|
||||
expect(labels).toContain("actions");
|
||||
expect(labels).not.toContain("copilot-requests");
|
||||
});
|
||||
|
||||
it("excludes copilot-requests when no feature flags are provided", async () => {
|
||||
const input = `on: push
|
||||
permissions:
|
||||
|`;
|
||||
const result = await complete(...getPositionFromCursor(input));
|
||||
|
||||
expect(result).not.toBeUndefined();
|
||||
const labels = result.map(x => x.label);
|
||||
expect(labels).toContain("actions");
|
||||
expect(labels).not.toContain("copilot-requests");
|
||||
});
|
||||
|
||||
it("includes copilot-requests in job-level permissions when allowCopilotRequestsPermission is enabled", async () => {
|
||||
const input = `on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
|`;
|
||||
const result = await complete(...getPositionFromCursor(input), {
|
||||
featureFlags: new FeatureFlags({allowCopilotRequestsPermission: true})
|
||||
});
|
||||
|
||||
expect(result).not.toBeUndefined();
|
||||
const labels = result.map(x => x.label);
|
||||
expect(labels).toContain("actions");
|
||||
expect(labels).toContain("copilot-requests");
|
||||
});
|
||||
|
||||
it("excludes copilot-requests from job-level permissions when allowCopilotRequestsPermission is disabled", async () => {
|
||||
const input = `on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
|`;
|
||||
const result = await complete(...getPositionFromCursor(input), {
|
||||
featureFlags: new FeatureFlags({allowCopilotRequestsPermission: false})
|
||||
});
|
||||
|
||||
expect(result).not.toBeUndefined();
|
||||
const labels = result.map(x => x.label);
|
||||
expect(labels).toContain("actions");
|
||||
expect(labels).not.toContain("copilot-requests");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -163,6 +163,14 @@ export async function complete(
|
||||
values = filterActionRunsCompletions(values, path, parsedTemplate.value);
|
||||
}
|
||||
|
||||
// Filter `copilot-requests` from permissions completions when the feature flag is disabled
|
||||
if (
|
||||
!config?.featureFlags?.isEnabled("allowCopilotRequestsPermission") &&
|
||||
parent?.definition?.key === "permissions-mapping"
|
||||
) {
|
||||
values = values.filter(v => v.label !== "copilot-requests");
|
||||
}
|
||||
|
||||
// Offer "(switch to list)" / "(switch to mapping)" when the schema allows alternative forms
|
||||
const escapeHatches = getEscapeHatchCompletions(token, keyToken, indentString, newPos, schema);
|
||||
values.push(...escapeHatches);
|
||||
|
||||
@@ -120,7 +120,9 @@ jobs:
|
||||
`;
|
||||
const result = await hover(...getPositionFromCursor(input));
|
||||
expect(result).not.toBeUndefined();
|
||||
expect(result?.contents).toEqual("");
|
||||
expect(result?.contents).toEqual(
|
||||
"A cron expression that represents a schedule. A scheduled workflow will run at most once every 5 minutes."
|
||||
);
|
||||
});
|
||||
|
||||
it("on an invalid cron schedule", async () => {
|
||||
@@ -130,7 +132,9 @@ jobs:
|
||||
`;
|
||||
const result = await hover(...getPositionFromCursor(input));
|
||||
expect(result).not.toBeUndefined();
|
||||
expect(result?.contents).toEqual("");
|
||||
expect(result?.contents).toEqual(
|
||||
"A cron expression that represents a schedule. A scheduled workflow will run at most once every 5 minutes."
|
||||
);
|
||||
});
|
||||
|
||||
it("shows context inherited from parent nodes", async () => {
|
||||
|
||||
@@ -368,6 +368,24 @@ jobs:
|
||||
});
|
||||
});
|
||||
|
||||
describe("environment deployment", () => {
|
||||
it("allows deployment boolean under environment mapping", async () => {
|
||||
const workflow = `
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: prod
|
||||
deployment: false
|
||||
steps:
|
||||
- run: echo
|
||||
`;
|
||||
const result = await validate(createDocument("wf.yaml", workflow));
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("workflow_dispatch", () => {
|
||||
it("allows empty string in choice options", async () => {
|
||||
const result = await validate(
|
||||
|
||||
+1
-1
@@ -6,5 +6,5 @@
|
||||
"languageservice",
|
||||
"languageserver"
|
||||
],
|
||||
"version": "0.3.46"
|
||||
"version": "0.3.49"
|
||||
}
|
||||
Generated
+9
-9
@@ -136,7 +136,7 @@
|
||||
},
|
||||
"expressions": {
|
||||
"name": "@actions/expressions",
|
||||
"version": "0.3.46",
|
||||
"version": "0.3.49",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.0.3",
|
||||
@@ -396,11 +396,11 @@
|
||||
},
|
||||
"languageserver": {
|
||||
"name": "@actions/languageserver",
|
||||
"version": "0.3.46",
|
||||
"version": "0.3.49",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/languageservice": "^0.3.46",
|
||||
"@actions/workflow-parser": "^0.3.46",
|
||||
"@actions/languageservice": "^0.3.49",
|
||||
"@actions/workflow-parser": "^0.3.49",
|
||||
"@octokit/rest": "^21.1.1",
|
||||
"@octokit/types": "^9.0.0",
|
||||
"vscode-languageserver": "^8.0.2",
|
||||
@@ -927,11 +927,11 @@
|
||||
},
|
||||
"languageservice": {
|
||||
"name": "@actions/languageservice",
|
||||
"version": "0.3.46",
|
||||
"version": "0.3.49",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/expressions": "^0.3.46",
|
||||
"@actions/workflow-parser": "^0.3.46",
|
||||
"@actions/expressions": "^0.3.49",
|
||||
"@actions/workflow-parser": "^0.3.49",
|
||||
"vscode-languageserver-textdocument": "^1.0.7",
|
||||
"vscode-languageserver-types": "^3.17.2",
|
||||
"vscode-uri": "^3.0.8",
|
||||
@@ -14020,10 +14020,10 @@
|
||||
},
|
||||
"workflow-parser": {
|
||||
"name": "@actions/workflow-parser",
|
||||
"version": "0.3.46",
|
||||
"version": "0.3.49",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/expressions": "^0.3.46",
|
||||
"@actions/expressions": "^0.3.49",
|
||||
"cronstrue": "^2.21.0",
|
||||
"yaml": "^2.0.0-8"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actions/workflow-parser",
|
||||
"version": "0.3.46",
|
||||
"version": "0.3.49",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"source": "./src/index.ts",
|
||||
@@ -48,7 +48,7 @@
|
||||
"watch": "tsc --build tsconfig.build.json --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/expressions": "^0.3.46",
|
||||
"@actions/expressions": "^0.3.49",
|
||||
"cronstrue": "^2.21.0",
|
||||
"yaml": "^2.0.0-8"
|
||||
},
|
||||
|
||||
@@ -578,4 +578,86 @@ jobs:
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("schedule timezone", () => {
|
||||
it("allows timezone in schedule", async () => {
|
||||
const result = parseWorkflow(
|
||||
{
|
||||
name: "wf.yaml",
|
||||
content: `on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
timezone: America/New_York
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest`
|
||||
},
|
||||
nullTrace
|
||||
);
|
||||
|
||||
const template = await convertWorkflowTemplate(result.context, result.value!, undefined, {
|
||||
errorPolicy: ErrorPolicy.TryConversion
|
||||
});
|
||||
|
||||
expect(result.context.errors.getErrors()).toHaveLength(0);
|
||||
expect(template.events?.schedule).toHaveLength(1);
|
||||
expect(template.events?.schedule?.[0]).toEqual({
|
||||
cron: "0 0 * * *",
|
||||
timezone: "America/New_York"
|
||||
});
|
||||
});
|
||||
|
||||
it("reports error when cron is missing from schedule entry", async () => {
|
||||
const result = parseWorkflow(
|
||||
{
|
||||
name: "wf.yaml",
|
||||
content: `on:
|
||||
schedule:
|
||||
- timezone: America/New_York
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest`
|
||||
},
|
||||
nullTrace
|
||||
);
|
||||
|
||||
const template = await convertWorkflowTemplate(result.context, result.value!, undefined, {
|
||||
errorPolicy: ErrorPolicy.TryConversion
|
||||
});
|
||||
|
||||
// Both schema validation and converter report the missing cron
|
||||
expect(result.context.errors.getErrors().length).toBeGreaterThanOrEqual(1);
|
||||
const errorMessages = result.context.errors
|
||||
.getErrors()
|
||||
.map(e => e.message)
|
||||
.join(", ");
|
||||
expect(errorMessages).toMatch(/Required property is missing: cron|Missing required key 'cron'/);
|
||||
expect(template.events?.schedule).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("converts schedule without timezone", async () => {
|
||||
const result = parseWorkflow(
|
||||
{
|
||||
name: "wf.yaml",
|
||||
content: `on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest`
|
||||
},
|
||||
nullTrace
|
||||
);
|
||||
|
||||
const template = await convertWorkflowTemplate(result.context, result.value!, undefined, {
|
||||
errorPolicy: ErrorPolicy.TryConversion
|
||||
});
|
||||
|
||||
expect(result.context.errors.getErrors()).toHaveLength(0);
|
||||
expect(template.events?.schedule).toHaveLength(1);
|
||||
expect(template.events?.schedule?.[0]).toEqual({
|
||||
cron: "0 0 * * *"
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import {FeatureFlags} from "@actions/expressions/features";
|
||||
import {TemplateContext} from "../templates/template-context.js";
|
||||
import {TemplateToken, TemplateTokenError} from "../templates/tokens/template-token.js";
|
||||
import {FileProvider} from "../workflows/file-provider.js";
|
||||
@@ -37,12 +38,19 @@ export type WorkflowTemplateConverterOptions = {
|
||||
* By default, conversion will be skipped if there are errors in the {@link TemplateContext}.
|
||||
*/
|
||||
errorPolicy?: ErrorPolicy;
|
||||
|
||||
/**
|
||||
* Feature flags for experimental features.
|
||||
* This option is not currently used but keeping it for future use.
|
||||
*/
|
||||
featureFlags?: FeatureFlags;
|
||||
};
|
||||
|
||||
const defaultOptions: Required<WorkflowTemplateConverterOptions> = {
|
||||
maxReusableWorkflowDepth: 4,
|
||||
fetchReusableWorkflowDepth: 0,
|
||||
errorPolicy: ErrorPolicy.ReturnErrorsOnly
|
||||
errorPolicy: ErrorPolicy.ReturnErrorsOnly,
|
||||
featureFlags: new FeatureFlags()
|
||||
};
|
||||
|
||||
export async function convertWorkflowTemplate(
|
||||
@@ -142,6 +150,7 @@ function getOptionsWithDefaults(options: WorkflowTemplateConverterOptions): Requ
|
||||
options.fetchReusableWorkflowDepth !== undefined
|
||||
? options.fetchReusableWorkflowDepth
|
||||
: defaultOptions.fetchReusableWorkflowDepth,
|
||||
errorPolicy: options.errorPolicy !== undefined ? options.errorPolicy : defaultOptions.errorPolicy
|
||||
errorPolicy: options.errorPolicy !== undefined ? options.errorPolicy : defaultOptions.errorPolicy,
|
||||
featureFlags: options.featureFlags ?? defaultOptions.featureFlags
|
||||
};
|
||||
}
|
||||
|
||||
@@ -149,23 +149,34 @@ function convertFilter<T extends TypesFilterConfig & WorkflowFilterConfig & Vers
|
||||
|
||||
function convertSchedule(context: TemplateContext, token: SequenceToken): ScheduleConfig[] | undefined {
|
||||
const result = [] as ScheduleConfig[];
|
||||
|
||||
for (const item of token) {
|
||||
const mappingToken = item.assertMapping(`event schedule`);
|
||||
if (mappingToken.count == 1) {
|
||||
const schedule = mappingToken.get(0);
|
||||
const scheduleKey = schedule.key.assertString(`schedule key`);
|
||||
if (scheduleKey.value == "cron") {
|
||||
const cron = schedule.value.assertString(`schedule cron`);
|
||||
// Validate the cron string
|
||||
const config: ScheduleConfig = {cron: ""};
|
||||
let valid = true;
|
||||
|
||||
for (const entry of mappingToken) {
|
||||
const key = entry.key.assertString(`schedule key`);
|
||||
|
||||
if (key.value === "cron") {
|
||||
const cron = entry.value.assertString(`schedule cron`);
|
||||
if (!isValidCron(cron.value)) {
|
||||
context.error(cron, "Invalid cron expression. Expected format: '* * * * *' (minute hour day month weekday)");
|
||||
}
|
||||
result.push({cron: cron.value});
|
||||
config.cron = cron.value;
|
||||
} else if (key.value === "timezone") {
|
||||
const timezone = entry.value.assertString(`schedule timezone`);
|
||||
config.timezone = timezone.value;
|
||||
} else {
|
||||
context.error(scheduleKey, `Invalid schedule key`);
|
||||
context.error(key, `Invalid schedule key`);
|
||||
valid = false;
|
||||
}
|
||||
} else {
|
||||
context.error(mappingToken, "Invalid format for 'schedule'");
|
||||
}
|
||||
|
||||
if (valid && config.cron) {
|
||||
result.push(config);
|
||||
} else if (valid && !config.cron) {
|
||||
context.error(mappingToken, "Missing required key 'cron' in schedule entry");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,14 @@ export function convertToActionsEnvironmentRef(
|
||||
case "url":
|
||||
result.url = property.value;
|
||||
break;
|
||||
|
||||
case "deployment": {
|
||||
const deploymentValue = property.value.assertBoolean("job environment deployment");
|
||||
if (deploymentValue.value === false) {
|
||||
result.skipDeployment = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ export type ConcurrencySetting = {
|
||||
export type ActionsEnvironmentReference = {
|
||||
name?: TemplateToken;
|
||||
url?: TemplateToken;
|
||||
skipDeployment?: boolean;
|
||||
};
|
||||
|
||||
export type WorkflowJob = Job | ReusableWorkflowJob;
|
||||
@@ -196,6 +197,7 @@ export type SecretConfig = {
|
||||
|
||||
export type ScheduleConfig = {
|
||||
cron: string;
|
||||
timezone?: string;
|
||||
};
|
||||
|
||||
export type WorkflowFilterConfig = {
|
||||
|
||||
@@ -1602,6 +1602,10 @@
|
||||
"type": "permission-level-any",
|
||||
"description": "Repository contents, commits, branches, downloads, releases, and merges."
|
||||
},
|
||||
"copilot-requests": {
|
||||
"type": "permission-level-write-or-no-access",
|
||||
"description": "GitHub Copilot requests."
|
||||
},
|
||||
"deployments": {
|
||||
"type": "permission-level-any",
|
||||
"description": "Deployments and deployment statuses."
|
||||
@@ -2075,6 +2079,10 @@
|
||||
"url": {
|
||||
"type": "string-runner-context-no-secrets",
|
||||
"description": "The environment URL, which maps to `environment_url` in the deployments API."
|
||||
},
|
||||
"deployment": {
|
||||
"type": "boolean",
|
||||
"description": "Whether to create a deployment record for this environment. Defaults to true."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2620,14 +2628,25 @@
|
||||
"cron-mapping": {
|
||||
"mapping": {
|
||||
"properties": {
|
||||
"cron": "cron-pattern"
|
||||
"cron": {
|
||||
"type": "cron-pattern",
|
||||
"required": true
|
||||
},
|
||||
"timezone": "timezone-string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"cron-pattern": {
|
||||
"description": "A cron expression that represents a schedule. A scheduled workflow will run at most once every 5 minutes.",
|
||||
"string": {
|
||||
"require-non-empty": true
|
||||
}
|
||||
},
|
||||
"timezone-string": {
|
||||
"description": "A string that represents the time zone a scheduled workflow will run relative to in IANA format (e.g. 'America/New_York' or 'Europe/London'). If omitted, the workflow will run relative to midnight UTC.",
|
||||
"string": {
|
||||
"require-non-empty": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
include-source: false # Drop file/line/col from output
|
||||
skip:
|
||||
- C#
|
||||
---
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
environment:
|
||||
name: production
|
||||
deployment: false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo hi
|
||||
build2:
|
||||
environment:
|
||||
name: staging
|
||||
deployment: true
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo hi
|
||||
---
|
||||
{
|
||||
"jobs": [
|
||||
{
|
||||
"type": "job",
|
||||
"id": "build",
|
||||
"name": "build",
|
||||
"if": {
|
||||
"type": 3,
|
||||
"expr": "success()"
|
||||
},
|
||||
"environment": {
|
||||
"type": 2,
|
||||
"map": [
|
||||
{
|
||||
"Key": "name",
|
||||
"Value": "production"
|
||||
},
|
||||
{
|
||||
"Key": "deployment",
|
||||
"Value": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"runs-on": "ubuntu-latest",
|
||||
"steps": [
|
||||
{
|
||||
"id": "__run",
|
||||
"if": {
|
||||
"type": 3,
|
||||
"expr": "success()"
|
||||
},
|
||||
"run": "echo hi"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "job",
|
||||
"id": "build2",
|
||||
"name": "build2",
|
||||
"if": {
|
||||
"type": 3,
|
||||
"expr": "success()"
|
||||
},
|
||||
"environment": {
|
||||
"type": 2,
|
||||
"map": [
|
||||
{
|
||||
"Key": "name",
|
||||
"Value": "staging"
|
||||
},
|
||||
{
|
||||
"Key": "deployment",
|
||||
"Value": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"runs-on": "ubuntu-latest",
|
||||
"steps": [
|
||||
{
|
||||
"id": "__run",
|
||||
"if": {
|
||||
"type": 3,
|
||||
"expr": "success()"
|
||||
},
|
||||
"run": "echo hi"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user