diff --git a/expressions/src/features.test.ts b/expressions/src/features.test.ts index dcb029f..e3a1c3e 100644 --- a/expressions/src/features.test.ts +++ b/expressions/src/features.test.ts @@ -56,7 +56,8 @@ describe("FeatureFlags", () => { "blockScalarChompingWarning", "allowCaseFunction", "allowCronTimezone", - "allowCopilotRequestsPermission" + "allowCopilotRequestsPermission", + "allowDeploymentKeyword" ]); }); }); diff --git a/expressions/src/features.ts b/expressions/src/features.ts index c2bcdd5..a12bcf8 100644 --- a/expressions/src/features.ts +++ b/expressions/src/features.ts @@ -46,6 +46,12 @@ export interface ExperimentalFeatures { * @default false */ allowCopilotRequestsPermission?: boolean; + + /** + * Enable the deployment keyword in workflow job environment. + * @default false + */ + allowDeploymentKeyword?: boolean; } /** @@ -62,7 +68,8 @@ const allFeatureKeys: ExperimentalFeatureKey[] = [ "blockScalarChompingWarning", "allowCaseFunction", "allowCronTimezone", - "allowCopilotRequestsPermission" + "allowCopilotRequestsPermission", + "allowDeploymentKeyword" ]; export class FeatureFlags { diff --git a/workflow-parser/src/model/converter/job/environment.ts b/workflow-parser/src/model/converter/job/environment.ts index 8da87cf..62f729c 100644 --- a/workflow-parser/src/model/converter/job/environment.ts +++ b/workflow-parser/src/model/converter/job/environment.ts @@ -1,3 +1,4 @@ +import {FeatureFlags} from "@actions/expressions/features"; import {TemplateContext} from "../../../templates/template-context.js"; import {TemplateToken} from "../../../templates/tokens/template-token.js"; import {isScalar} from "../../../templates/tokens/type-guards.js"; @@ -34,6 +35,17 @@ export function convertToActionsEnvironmentRef( case "url": result.url = property.value; break; + + case "deployment": { + const featureFlags = context.state["featureFlags"] as FeatureFlags | undefined; + const flags = featureFlags ?? new FeatureFlags(); + if (flags.isEnabled("allowDeploymentKeyword")) { + result.deployment = property.value; + } else { + context.error(property.key, `The key 'deployment' is not allowed`); + } + break; + } } } diff --git a/workflow-parser/src/model/workflow-template.ts b/workflow-parser/src/model/workflow-template.ts index 019e831..3ecb661 100644 --- a/workflow-parser/src/model/workflow-template.ts +++ b/workflow-parser/src/model/workflow-template.ts @@ -26,6 +26,7 @@ export type ConcurrencySetting = { export type ActionsEnvironmentReference = { name?: TemplateToken; url?: TemplateToken; + deployment?: TemplateToken; }; export type WorkflowJob = Job | ReusableWorkflowJob; diff --git a/workflow-parser/src/workflow-v1.0.json b/workflow-parser/src/workflow-v1.0.json index 1b7e214..10cb330 100644 --- a/workflow-parser/src/workflow-v1.0.json +++ b/workflow-parser/src/workflow-v1.0.json @@ -2079,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 for this environment. Set to `false` to access environment secrets and variables without creating a deployment record. Defaults to `true`." } } } diff --git a/workflow-parser/src/xlang.test.ts b/workflow-parser/src/xlang.test.ts index 5b4f76e..853f706 100644 --- a/workflow-parser/src/xlang.test.ts +++ b/workflow-parser/src/xlang.test.ts @@ -1,6 +1,7 @@ import * as fs from "fs"; import * as path from "path"; import * as YAML from "yaml"; +import {FeatureFlags} from "@actions/expressions/features"; import {convertWorkflowTemplate} from "./model/convert.js"; import {NoOperationTraceWriter} from "./templates/trace-writer.js"; import {File} from "./workflows/file.js"; @@ -10,6 +11,7 @@ import {parseWorkflow} from "./workflows/workflow-parser.js"; interface TestOptions { "include-source"?: boolean; + "allow-deployment-keyword"?: boolean; skip?: string[]; } @@ -85,7 +87,10 @@ describe("x-lang tests", () => { parseResult.value!, // eslint-disable-line @typescript-eslint/no-non-null-assertion testFileProvider, { - fetchReusableWorkflowDepth: 1 + fetchReusableWorkflowDepth: 1, + featureFlags: new FeatureFlags({ + allowDeploymentKeyword: testOptions["allow-deployment-keyword"] + }) } ); diff --git a/workflow-parser/testdata/reader/errors-job-environment-deployment-disabled-feature-default-go.yml b/workflow-parser/testdata/reader/errors-job-environment-deployment-disabled-feature-default-go.yml new file mode 100644 index 0000000..3927008 --- /dev/null +++ b/workflow-parser/testdata/reader/errors-job-environment-deployment-disabled-feature-default-go.yml @@ -0,0 +1,21 @@ +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 +--- +{ + "errors": [ + { + "Message": ".github/workflows/errors-job-environment-deployment-disabled-feature-default-go.yml (Line: 6, Col: 7): The key 'deployment' is not allowed" + } + ] +} diff --git a/workflow-parser/testdata/reader/errors-job-environment-deployment-disabled-feature.yml b/workflow-parser/testdata/reader/errors-job-environment-deployment-disabled-feature.yml new file mode 100644 index 0000000..6de4476 --- /dev/null +++ b/workflow-parser/testdata/reader/errors-job-environment-deployment-disabled-feature.yml @@ -0,0 +1,21 @@ +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 +--- +{ + "errors": [ + { + "Message": ".github/workflows/errors-job-environment-deployment-disabled-feature.yml (Line: 6, Col: 7): The key 'deployment' is not allowed" + } + ] +} diff --git a/workflow-parser/testdata/reader/job-environment-deployment.yml b/workflow-parser/testdata/reader/job-environment-deployment.yml new file mode 100644 index 0000000..7319673 --- /dev/null +++ b/workflow-parser/testdata/reader/job-environment-deployment.yml @@ -0,0 +1,92 @@ +include-source: false # Drop file/line/col from output +skip: + - C# +allow-deployment-keyword: true +--- +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" + } + ] + } + ] +}