diff --git a/expressions/package.json b/expressions/package.json index 2552d3b..1d592de 100755 --- a/expressions/package.json +++ b/expressions/package.json @@ -1,6 +1,6 @@ { "name": "@actions/expressions", - "version": "0.3.17", + "version": "0.3.18", "license": "MIT", "type": "module", "source": "./src/index.ts", @@ -9,10 +9,12 @@ }, "exports": { ".": { - "import": "./dist/index.js" + "import": "./dist/index.js", + "types": "./dist/index.d.ts" }, "./*": { - "import": "./dist/*.js" + "import": "./dist/*.js", + "types": "./dist/*.d.ts" } }, "typesVersions": { diff --git a/languageserver/package.json b/languageserver/package.json index 3f0ee2b..201f875 100644 --- a/languageserver/package.json +++ b/languageserver/package.json @@ -1,6 +1,6 @@ { "name": "@actions/languageserver", - "version": "0.3.17", + "version": "0.3.18", "description": "Language server for GitHub Actions", "license": "MIT", "type": "module", @@ -43,8 +43,8 @@ "watch": "tsc --build tsconfig.build.json --watch" }, "dependencies": { - "@actions/languageservice": "^0.3.17", - "@actions/workflow-parser": "^0.3.17", + "@actions/languageservice": "^0.3.18", + "@actions/workflow-parser": "^0.3.18", "@octokit/rest": "^21.1.1", "@octokit/types": "^9.0.0", "vscode-languageserver": "^8.0.2", diff --git a/languageservice/package.json b/languageservice/package.json index c9ae6bf..3c3762d 100644 --- a/languageservice/package.json +++ b/languageservice/package.json @@ -1,6 +1,6 @@ { "name": "@actions/languageservice", - "version": "0.3.17", + "version": "0.3.18", "description": "Language service for GitHub Actions", "license": "MIT", "type": "module", @@ -44,8 +44,8 @@ "watch": "tsc --build tsconfig.build.json --watch" }, "dependencies": { - "@actions/expressions": "^0.3.17", - "@actions/workflow-parser": "^0.3.17", + "@actions/expressions": "^0.3.18", + "@actions/workflow-parser": "^0.3.18", "vscode-languageserver-textdocument": "^1.0.7", "vscode-languageserver-types": "^3.17.2", "vscode-uri": "^3.0.8", diff --git a/languageservice/src/complete.test.ts b/languageservice/src/complete.test.ts index f4ca821..cf45095 100644 --- a/languageservice/src/complete.test.ts +++ b/languageservice/src/complete.test.ts @@ -70,7 +70,7 @@ jobs: |`; const result = await complete(...getPositionFromCursor(input)); expect(result).not.toBeUndefined(); - expect(result.length).toEqual(20); + expect(result.length).toEqual(21); }); it("string definition completion in sequence", async () => { @@ -243,7 +243,7 @@ jobs: runs-|`; const result = await complete(...getPositionFromCursor(input)); expect(result).not.toBeUndefined(); - expect(result).toHaveLength(20); + expect(result).toHaveLength(21); }); it("job key with comment afterwards", async () => { @@ -254,7 +254,7 @@ jobs: #`; const result = await complete(...getPositionFromCursor(input)); expect(result).not.toBeUndefined(); - expect(result).toHaveLength(20); + expect(result).toHaveLength(21); }); it("job key with other values afterwards", async () => { @@ -266,7 +266,7 @@ jobs: concurrency: 'group-name'`; const result = await complete(...getPositionFromCursor(input)); expect(result).not.toBeUndefined(); - expect(result).toHaveLength(19); + expect(result).toHaveLength(20); }); it("step key without space after colon", async () => { @@ -335,7 +335,7 @@ jobs: - uses: actions/checkout@v2 `; const result = await complete(...getPositionFromCursor(input)); - expect(result).toHaveLength(16); + expect(result).toHaveLength(17); }); it("complete from behind a colon will replace it", async () => { @@ -348,7 +348,7 @@ jobs: - uses: actions/checkout@v2 `; const result = await complete(...getPositionFromCursor(input)); - expect(result).toHaveLength(16); + expect(result).toHaveLength(17); const textEdit = result[0].textEdit as TextEdit; expect(textEdit.range).toEqual({ start: {line: 5, character: 4}, diff --git a/lerna.json b/lerna.json index f74e517..f3d943b 100644 --- a/lerna.json +++ b/lerna.json @@ -6,5 +6,5 @@ "languageservice", "languageserver" ], - "version": "0.3.17" + "version": "0.3.18" } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 2fc195d..17b9f17 100644 --- a/package-lock.json +++ b/package-lock.json @@ -135,7 +135,7 @@ }, "expressions": { "name": "@actions/expressions", - "version": "0.3.17", + "version": "0.3.18", "license": "MIT", "devDependencies": { "@types/jest": "^29.0.3", @@ -395,11 +395,11 @@ }, "languageserver": { "name": "@actions/languageserver", - "version": "0.3.17", + "version": "0.3.18", "license": "MIT", "dependencies": { - "@actions/languageservice": "^0.3.17", - "@actions/workflow-parser": "^0.3.17", + "@actions/languageservice": "^0.3.18", + "@actions/workflow-parser": "^0.3.18", "@octokit/rest": "^21.1.1", "@octokit/types": "^9.0.0", "vscode-languageserver": "^8.0.2", @@ -921,11 +921,11 @@ }, "languageservice": { "name": "@actions/languageservice", - "version": "0.3.17", + "version": "0.3.18", "license": "MIT", "dependencies": { - "@actions/expressions": "^0.3.17", - "@actions/workflow-parser": "^0.3.17", + "@actions/expressions": "^0.3.18", + "@actions/workflow-parser": "^0.3.18", "vscode-languageserver-textdocument": "^1.0.7", "vscode-languageserver-types": "^3.17.2", "vscode-uri": "^3.0.8", @@ -6760,15 +6760,16 @@ } }, "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -12916,10 +12917,10 @@ }, "workflow-parser": { "name": "@actions/workflow-parser", - "version": "0.3.17", + "version": "0.3.18", "license": "MIT", "dependencies": { - "@actions/expressions": "^0.3.17", + "@actions/expressions": "^0.3.18", "cronstrue": "^2.21.0", "yaml": "^2.0.0-8" }, diff --git a/workflow-parser/package.json b/workflow-parser/package.json index a08ceb6..e95a843 100644 --- a/workflow-parser/package.json +++ b/workflow-parser/package.json @@ -1,6 +1,6 @@ { "name": "@actions/workflow-parser", - "version": "0.3.17", + "version": "0.3.18", "license": "MIT", "type": "module", "source": "./src/index.ts", @@ -9,10 +9,12 @@ }, "exports": { ".": { - "import": "./dist/index.js" + "import": "./dist/index.js", + "types": "./dist/index.d.ts" }, "./*": { - "import": "./dist/*.js" + "import": "./dist/*.js", + "types": "./dist/*.d.ts" } }, "typesVersions": { @@ -43,7 +45,7 @@ "watch": "tsc --build tsconfig.build.json --watch" }, "dependencies": { - "@actions/expressions": "^0.3.17", + "@actions/expressions": "^0.3.18", "cronstrue": "^2.21.0", "yaml": "^2.0.0-8" }, diff --git a/workflow-parser/src/model/converter/job.ts b/workflow-parser/src/model/converter/job.ts index 1886207..2ee2c70 100644 --- a/workflow-parser/src/model/converter/job.ts +++ b/workflow-parser/src/model/converter/job.ts @@ -16,7 +16,16 @@ export function convertJob(context: TemplateContext, jobKey: StringToken, token: context.error(jobKey, error); } - let concurrency, container, env, environment, name, outputs, runsOn, services, strategy: TemplateToken | undefined; + let concurrency, + container, + env, + environment, + name, + outputs, + runsOn, + services, + strategy, + snapshot: TemplateToken | undefined; let needs: StringToken[] | undefined = undefined; let steps: Step[] = []; let workflowJobRef: StringToken | undefined; @@ -86,6 +95,10 @@ export function convertJob(context: TemplateContext, jobKey: StringToken, token: services = item.value; break; + case "snapshot": + snapshot = item.value; + break; + case "steps": steps = convertSteps(context, item.value); break; @@ -147,7 +160,8 @@ export function convertJob(context: TemplateContext, jobKey: StringToken, token: container, services, outputs, - steps + steps, + snapshot }; } } diff --git a/workflow-parser/src/model/workflow-template.ts b/workflow-parser/src/model/workflow-template.ts index 679da4f..ebe10ac 100644 --- a/workflow-parser/src/model/workflow-template.ts +++ b/workflow-parser/src/model/workflow-template.ts @@ -41,6 +41,7 @@ export type BaseJob = { concurrency?: TemplateToken; strategy?: TemplateToken; outputs?: MappingToken; + snapshot?: TemplateToken; }; // `job-factory` in the schema diff --git a/workflow-parser/src/workflow-v1.0.json b/workflow-parser/src/workflow-v1.0.json index 6faa40e..3ccc372 100644 --- a/workflow-parser/src/workflow-v1.0.json +++ b/workflow-parser/src/workflow-v1.0.json @@ -1767,7 +1767,8 @@ "concurrency": "job-concurrency", "outputs": "job-outputs", "defaults": "job-defaults", - "steps": "steps" + "steps": "steps", + "snapshot": "snapshot" } } }, @@ -1911,6 +1912,43 @@ "loose-value-type": "any" } }, + "snapshot": { + "description": "Use `snapshot` to define a custom image you want to create or update after your job succeeds by taking a snapshot of your runner.", + "one-of": [ + "non-empty-string", + "snapshot-mapping" + ] + }, + "snapshot-mapping": { + "mapping": { + "properties": { + "image-name": { + "description": "The desired name of the custom image you want to create or update.", + "type": "non-empty-string", + "required": true + }, + "if": "snapshot-if", + "version": { + "description": "The desired major version updates upon a new custom image version creation.", + "type": "non-empty-string" + } + } + } + }, + "snapshot-if": { + "context": [ + "github", + "inputs", + "vars", + "needs", + "strategy", + "matrix" + ], + "description": "Use the if conditional to prevent a snapshot from being taken unless a condition is met. Any supported context and expression can be used to create a conditional. Expressions in an `if` conditional do not require the bracketed expression syntax. When you use expressions in an `if` conditional, you may omit the expression syntax because GitHub automatically evaluates the `if` conditional as an expression.", + "string": { + "is-expression": true + } + }, "runs-on": { "description": "Use `runs-on` to define the type of machine to run the job on.\n* The destination machine can be either a GitHub-hosted runner, larger runner, or a self-hosted runner.\n* You can target runners based on the labels assigned to them, or their group membership, or a combination of these.\n* You can provide `runs-on` as a single string or as an array of strings.\n* If you specify an array of strings, your workflow will execute on any runner that matches all of the specified `runs-on` values.\n* If you would like to run your workflow on multiple machines, use `jobs..strategy`.", "context": [ diff --git a/workflow-parser/testdata/reader/job-snapshot-mapping.yml b/workflow-parser/testdata/reader/job-snapshot-mapping.yml new file mode 100644 index 0000000..2689a6e --- /dev/null +++ b/workflow-parser/testdata/reader/job-snapshot-mapping.yml @@ -0,0 +1,58 @@ +include-source: false # Drop file/line/col from output +--- +on: push +jobs: + build: + runs-on: ubuntu-latest + steps: + - run: echo hi + snapshot: + image-name: custom-image + version: 1.* + if: ${{ github.event_name == 'something' }} + +--- +{ + "jobs": [ + { + "type": "job", + "id": "build", + "name": "build", + "if": { + "type": 3, + "expr": "success()" + }, + "runs-on": "ubuntu-latest", + "steps": [ + { + "id": "__run", + "if": { + "type": 3, + "expr": "success()" + }, + "run": "echo hi" + } + ], + "snapshot": { + "type": 2, + "map": [ + { + "Key": "image-name", + "Value": "custom-image" + }, + { + "Key": "version", + "Value": "1.*" + }, + { + "Key": "if", + "Value": { + "type": 3, + "expr": "github.event_name == 'something'" + } + } + ] + } + } + ] +} \ No newline at end of file diff --git a/workflow-parser/testdata/reader/job-snapshot-simple.yml b/workflow-parser/testdata/reader/job-snapshot-simple.yml new file mode 100644 index 0000000..b009a09 --- /dev/null +++ b/workflow-parser/testdata/reader/job-snapshot-simple.yml @@ -0,0 +1,42 @@ +include-source: false # Drop file/line/col from output +--- +# on: push +# jobs: +# job1: +# runs-on: windows-2019 +# snapshot: custom-image +# steps: +# - run: echo 1 +on: push +jobs: + build: + runs-on: ubuntu-latest + steps: + - run: echo hi + snapshot: custom-image +--- +{ + "jobs": [ + { + "type": "job", + "id": "build", + "name": "build", + "if": { + "type": 3, + "expr": "success()" + }, + "runs-on": "ubuntu-latest", + "steps": [ + { + "id": "__run", + "if": { + "type": 3, + "expr": "success()" + }, + "run": "echo hi" + } + ], + "snapshot": "custom-image" + } + ] +} \ No newline at end of file