@intentius/chant-lexicon-github 0.124.46 → 0.124.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/action-pins.d.ts +17 -27
- package/dist/action-pins.d.ts.map +1 -1
- package/dist/action-pins.js +39 -27
- package/dist/components/ci-jobs.d.ts +5 -2
- package/dist/components/ci-jobs.d.ts.map +1 -1
- package/dist/components/ci-jobs.js +8 -5
- package/dist/components/generate-op-pipeline.d.ts +7 -0
- package/dist/components/generate-op-pipeline.d.ts.map +1 -1
- package/dist/components/generate-op-pipeline.js +26 -3
- package/dist/components/generate-op-waves-pipeline.d.ts +13 -3
- package/dist/components/generate-op-waves-pipeline.d.ts.map +1 -1
- package/dist/components/generate-op-waves-pipeline.js +24 -5
- package/dist/components/generate-pipeline.d.ts.map +1 -1
- package/dist/components/generate-pipeline.js +55 -10
- package/dist/components/review-jobs.d.ts +55 -0
- package/dist/components/review-jobs.d.ts.map +1 -0
- package/dist/components/review-jobs.js +130 -0
- package/dist/composites/cache.d.ts.map +1 -1
- package/dist/composites/cache.js +2 -1
- package/dist/composites/checkout.d.ts +3 -3
- package/dist/composites/checkout.js +1 -1
- package/dist/composites/docker-build.js +4 -4
- package/dist/composites/download-artifact.d.ts.map +1 -1
- package/dist/composites/download-artifact.js +2 -1
- package/dist/composites/go-ci.js +4 -4
- package/dist/composites/node-pipeline.js +5 -5
- package/dist/composites/python-ci.js +2 -2
- package/dist/composites/setup-go.d.ts.map +1 -1
- package/dist/composites/setup-go.js +2 -1
- package/dist/composites/setup-node.d.ts +3 -3
- package/dist/composites/setup-node.js +1 -1
- package/dist/composites/setup-python.d.ts.map +1 -1
- package/dist/composites/setup-python.js +2 -1
- package/dist/composites/upload-artifact.d.ts.map +1 -1
- package/dist/composites/upload-artifact.js +2 -1
- package/dist/integrity.json +2 -2
- package/dist/manifest.json +1 -1
- package/package.json +2 -2
- package/src/action-pins.test.ts +29 -6
- package/src/action-pins.ts +48 -34
- package/src/components/__fixtures__/pr-loop-build.github.golden.yml +8 -9
- package/src/components/__fixtures__/pr-loop-record.github.golden.yml +4 -5
- package/src/components/__fixtures__/pr-loop-waves.github.golden.yml +16 -17
- package/src/components/__fixtures__/pr-loop.github.golden.yml +8 -9
- package/src/components/__golden__/op-waves-jobs.yml +71 -9
- package/src/components/ci-jobs.ts +8 -5
- package/src/components/generate-op-pipeline.review.test.ts +152 -0
- package/src/components/generate-op-pipeline.test.ts +5 -4
- package/src/components/generate-op-pipeline.ts +33 -3
- package/src/components/generate-op-waves-pipeline.test.ts +2 -1
- package/src/components/generate-op-waves-pipeline.ts +25 -5
- package/src/components/generate-pipeline.test.ts +10 -9
- package/src/components/generate-pipeline.ts +57 -9
- package/src/components/generate-pr-pipeline.locks.test.ts +68 -0
- package/src/components/generate-pr-pipeline.test.ts +6 -4
- package/src/components/generate-pr-waves-pipeline.test.ts +3 -2
- package/src/components/review-jobs.ts +165 -0
- package/src/composites/cache.ts +2 -1
- package/src/composites/checkout.ts +4 -4
- package/src/composites/composites.test.ts +23 -22
- package/src/composites/docker-build.ts +4 -4
- package/src/composites/download-artifact.ts +2 -1
- package/src/composites/go-ci.ts +4 -4
- package/src/composites/node-pipeline.ts +5 -5
- package/src/composites/python-ci.ts +2 -2
- package/src/composites/setup-go.ts +2 -1
- package/src/composites/setup-node.ts +4 -4
- package/src/composites/setup-python.ts +2 -1
- package/src/composites/upload-artifact.ts +2 -1
package/dist/action-pins.d.ts
CHANGED
|
@@ -1,44 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The action versions the github lexicon emits
|
|
3
|
-
*
|
|
2
|
+
* The action versions the github lexicon emits. The table is core's
|
|
3
|
+
* (`@intentius/chant/action-pins`, #4403); this file reads it, so an update is
|
|
4
|
+
* one change there (#4410). Each action here is its newest row in core's table,
|
|
5
|
+
* pinned at the commit of its latest release.
|
|
4
6
|
*
|
|
5
|
-
* Composites emit `<slug>@<
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Composites emit `<slug>@<sha> # <version>` by default, the form GHA021 and
|
|
8
|
+
* GHA029 accept and GHA059 reads the version label from. The serializer writes
|
|
9
|
+
* the `# <version>` part as a YAML comment, so GitHub sees only the SHA. With
|
|
10
|
+
* `pin: "tag"` they emit the major tag (`actions/checkout@v7`).
|
|
9
11
|
*
|
|
10
|
-
* Refresh a row by resolving the release tag to its commit:
|
|
12
|
+
* Refresh a row in core by resolving the release tag to its commit:
|
|
11
13
|
*
|
|
12
14
|
* gh api repos/actions/checkout/commits/v7.0.1 --jq .sha
|
|
13
|
-
*
|
|
14
|
-
* Keep `major` on a version whose action runs on a Node runtime GitHub still
|
|
15
|
-
* supports.
|
|
16
15
|
*/
|
|
17
16
|
export interface ActionPin {
|
|
18
|
-
/** The major tag
|
|
17
|
+
/** The major tag `pin: "tag"` emits, such as `v7`. */
|
|
19
18
|
major: string;
|
|
20
19
|
/** The release the SHA belongs to, written as the pin's comment. */
|
|
21
20
|
version: string;
|
|
22
21
|
/** The full commit SHA of `version`. */
|
|
23
22
|
sha: string;
|
|
24
23
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
readonly version: "v7.0.1";
|
|
29
|
-
readonly sha: "3d3c42e5aac5ba805825da76410c181273ba90b1";
|
|
30
|
-
};
|
|
31
|
-
readonly "actions/setup-node": {
|
|
32
|
-
readonly major: "v7";
|
|
33
|
-
readonly version: "v7.0.0";
|
|
34
|
-
readonly sha: "820762786026740c76f36085b0efc47a31fe5020";
|
|
35
|
-
};
|
|
36
|
-
};
|
|
37
|
-
export type PinnedAction = keyof typeof ACTION_PINS;
|
|
24
|
+
declare const SLUGS: readonly ["actions/checkout", "actions/setup-node", "actions/setup-go", "actions/setup-python", "actions/cache", "actions/upload-artifact", "actions/download-artifact", "oven-sh/setup-bun", "golangci/golangci-lint-action", "docker/login-action", "docker/setup-buildx-action", "docker/metadata-action", "docker/build-push-action"];
|
|
25
|
+
export type PinnedAction = (typeof SLUGS)[number];
|
|
26
|
+
export declare const ACTION_PINS: Record<PinnedAction, ActionPin>;
|
|
38
27
|
/**
|
|
39
|
-
* How a composite refers to its action: `"
|
|
40
|
-
* (`actions/checkout@v7`),
|
|
41
|
-
*
|
|
28
|
+
* How a composite refers to its action: `"sha"` (the default) emits the pinned
|
|
29
|
+
* commit with its version as a comment (`actions/checkout@3d3c…90b1 # v7.0.1`),
|
|
30
|
+
* `"tag"` emits the major tag (`actions/checkout@v7`).
|
|
42
31
|
*/
|
|
43
32
|
export type ActionPinMode = "tag" | "sha";
|
|
44
33
|
/** The `uses:` value for a pinned action. */
|
|
@@ -50,4 +39,5 @@ export declare function actionPin(slug: string): ActionPin | undefined;
|
|
|
50
39
|
* an empty string for any other action.
|
|
51
40
|
*/
|
|
52
41
|
export declare function pinFixHint(slug: string): string;
|
|
42
|
+
export {};
|
|
53
43
|
//# sourceMappingURL=action-pins.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action-pins.d.ts","sourceRoot":"","sources":["../src/action-pins.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"action-pins.d.ts","sourceRoot":"","sources":["../src/action-pins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,MAAM,WAAW,SAAS;IACxB,sDAAsD;IACtD,KAAK,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;IAChB,wCAAwC;IACxC,GAAG,EAAE,MAAM,CAAC;CACb;AAED,QAAA,MAAM,KAAK,YACT,kBAAkB,EAClB,oBAAoB,EACpB,kBAAkB,EAClB,sBAAsB,EACtB,eAAe,EACf,yBAAyB,EACzB,2BAA2B,EAC3B,mBAAmB,EACnB,+BAA+B,EAC/B,qBAAqB,EACrB,4BAA4B,EAC5B,wBAAwB,EACxB,0BAA0B,CAClB,CAAC;AAEX,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC;AAUlD,eAAO,MAAM,WAAW,EAA2D,MAAM,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;AAEnH;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,KAAK,CAAC;AAE1C,6CAA6C;AAC7C,wBAAgB,SAAS,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,GAAE,aAAqB,GAAG,MAAM,CAGhF;AAED,4EAA4E;AAC5E,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAE7D;AAOD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAM/C"}
|
package/dist/action-pins.js
CHANGED
|
@@ -1,41 +1,51 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The action versions the github lexicon emits
|
|
3
|
-
*
|
|
2
|
+
* The action versions the github lexicon emits. The table is core's
|
|
3
|
+
* (`@intentius/chant/action-pins`, #4403); this file reads it, so an update is
|
|
4
|
+
* one change there (#4410). Each action here is its newest row in core's table,
|
|
5
|
+
* pinned at the commit of its latest release.
|
|
4
6
|
*
|
|
5
|
-
* Composites emit `<slug>@<
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Composites emit `<slug>@<sha> # <version>` by default, the form GHA021 and
|
|
8
|
+
* GHA029 accept and GHA059 reads the version label from. The serializer writes
|
|
9
|
+
* the `# <version>` part as a YAML comment, so GitHub sees only the SHA. With
|
|
10
|
+
* `pin: "tag"` they emit the major tag (`actions/checkout@v7`).
|
|
9
11
|
*
|
|
10
|
-
* Refresh a row by resolving the release tag to its commit:
|
|
12
|
+
* Refresh a row in core by resolving the release tag to its commit:
|
|
11
13
|
*
|
|
12
14
|
* gh api repos/actions/checkout/commits/v7.0.1 --jq .sha
|
|
13
|
-
*
|
|
14
|
-
* Keep `major` on a version whose action runs on a Node runtime GitHub still
|
|
15
|
-
* supports.
|
|
16
15
|
*/
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"actions/
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
16
|
+
import { ACTION_PINS as CORE_PINS } from "@intentius/chant/action-pins";
|
|
17
|
+
const SLUGS = [
|
|
18
|
+
"actions/checkout",
|
|
19
|
+
"actions/setup-node",
|
|
20
|
+
"actions/setup-go",
|
|
21
|
+
"actions/setup-python",
|
|
22
|
+
"actions/cache",
|
|
23
|
+
"actions/upload-artifact",
|
|
24
|
+
"actions/download-artifact",
|
|
25
|
+
"oven-sh/setup-bun",
|
|
26
|
+
"golangci/golangci-lint-action",
|
|
27
|
+
"docker/login-action",
|
|
28
|
+
"docker/setup-buildx-action",
|
|
29
|
+
"docker/metadata-action",
|
|
30
|
+
"docker/build-push-action",
|
|
31
|
+
];
|
|
32
|
+
/** The newest row of core's table for `slug`. */
|
|
33
|
+
function newest(slug) {
|
|
34
|
+
const rows = Object.values(CORE_PINS).filter((p) => p.slug === slug);
|
|
35
|
+
if (rows.length === 0)
|
|
36
|
+
throw new Error(`action-pins: core's table has no row for ${slug}`);
|
|
37
|
+
const row = rows.reduce((a, b) => (Number(b.major.slice(1)) > Number(a.major.slice(1)) ? b : a));
|
|
38
|
+
return { major: row.major, version: row.tag, sha: row.sha };
|
|
39
|
+
}
|
|
40
|
+
export const ACTION_PINS = Object.fromEntries(SLUGS.map((s) => [s, newest(s)]));
|
|
29
41
|
/** The `uses:` value for a pinned action. */
|
|
30
|
-
export function actionRef(slug, pin = "
|
|
42
|
+
export function actionRef(slug, pin = "sha") {
|
|
31
43
|
const entry = ACTION_PINS[slug];
|
|
32
44
|
return pin === "sha" ? `${slug}@${entry.sha} # ${entry.version}` : `${slug}@${entry.major}`;
|
|
33
45
|
}
|
|
34
46
|
/** The pin table entry for an action slug, if the lexicon maintains one. */
|
|
35
47
|
export function actionPin(slug) {
|
|
36
|
-
return Object.prototype.hasOwnProperty.call(ACTION_PINS, slug)
|
|
37
|
-
? ACTION_PINS[slug]
|
|
38
|
-
: undefined;
|
|
48
|
+
return Object.prototype.hasOwnProperty.call(ACTION_PINS, slug) ? ACTION_PINS[slug] : undefined;
|
|
39
49
|
}
|
|
40
50
|
const COMPOSITE_FOR = {
|
|
41
51
|
"actions/checkout": "Checkout",
|
|
@@ -49,5 +59,7 @@ export function pinFixHint(slug) {
|
|
|
49
59
|
if (!actionPin(slug))
|
|
50
60
|
return "";
|
|
51
61
|
const pinned = slug;
|
|
52
|
-
|
|
62
|
+
const composite = COMPOSITE_FOR[pinned];
|
|
63
|
+
const how = composite ? `, which ${composite}({ pin: "sha" }) emits` : "";
|
|
64
|
+
return ` Use \`uses: ${actionRef(pinned, "sha")}\`${how}.`;
|
|
53
65
|
}
|
|
@@ -9,8 +9,11 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { OpTrigger } from "@intentius/chant/lexicon";
|
|
11
11
|
/** The artifact actions every generated job uses, at the major the generators have always pinned. */
|
|
12
|
-
export declare const UPLOAD_ARTIFACT
|
|
13
|
-
export declare const DOWNLOAD_ARTIFACT
|
|
12
|
+
export declare const UPLOAD_ARTIFACT: string;
|
|
13
|
+
export declare const DOWNLOAD_ARTIFACT: string;
|
|
14
|
+
export declare const CHECKOUT: string;
|
|
15
|
+
export declare const CACHE_RESTORE: string;
|
|
16
|
+
export declare const CACHE_SAVE: string;
|
|
14
17
|
/** The checkout step; `fullHistory` fetches every commit, for a job that reads the base commit or measures a change with git. */
|
|
15
18
|
export declare function checkoutStep(fullHistory?: boolean): Record<string, unknown>;
|
|
16
19
|
/** One `run:` step per line, so each line is its own step in the log. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ci-jobs.d.ts","sourceRoot":"","sources":["../../src/components/ci-jobs.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAG1D,qGAAqG;AACrG,eAAO,MAAM,eAAe
|
|
1
|
+
{"version":3,"file":"ci-jobs.d.ts","sourceRoot":"","sources":["../../src/components/ci-jobs.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAG1D,qGAAqG;AACrG,eAAO,MAAM,eAAe,QAAgC,CAAC;AAC7D,eAAO,MAAM,iBAAiB,QAAkC,CAAC;AACjE,eAAO,MAAM,QAAQ,QAA0B,CAAC;AAChD,eAAO,MAAM,aAAa,QAA8B,CAAC;AACzD,eAAO,MAAM,UAAU,QAA2B,CAAC;AAEnD,iIAAiI;AACjI,wBAAgB,YAAY,CAAC,WAAW,UAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED,yEAAyE;AACzE,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAEjF;AAED,oDAAoD;AACpD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAE1G;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAOpG;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAG7E;AAED,6DAA6D;AAC7D,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,gBAAgB,UAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAE5F;AAED,oGAAoG;AACpG,wBAAgB,WAAW,CAAC,EAAE,UAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAE9D;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAYrE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,GAAG,MAAM,CAG9E;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,qGAAqG;IACrG,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,8BAA8B;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5B,uEAAuE;IACvE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,kCAAkC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,kCAAkC;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,+EAA+E;IAC/E,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,2BAA2B;IAC3B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,kFAAkF;AAClF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,iBAAiB,GAAG,MAAM,CAW/D"}
|
|
@@ -9,13 +9,16 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { emitYAML, emitYAMLEntry } from "@intentius/chant/yaml";
|
|
11
11
|
import { BUILD_REQUEST_TAGS, GITHUB_JOB_TOKEN, pushBranches } from "@intentius/chant/ci-jobs";
|
|
12
|
-
import {
|
|
12
|
+
import { ACTION_PINS as PINS, tagComments, usesRef } from "@intentius/chant/action-pins";
|
|
13
13
|
/** The artifact actions every generated job uses, at the major the generators have always pinned. */
|
|
14
|
-
export const UPLOAD_ARTIFACT =
|
|
15
|
-
export const DOWNLOAD_ARTIFACT =
|
|
14
|
+
export const UPLOAD_ARTIFACT = usesRef(PINS.uploadArtifact4);
|
|
15
|
+
export const DOWNLOAD_ARTIFACT = usesRef(PINS.downloadArtifact4);
|
|
16
|
+
export const CHECKOUT = usesRef(PINS.checkout7);
|
|
17
|
+
export const CACHE_RESTORE = usesRef(PINS.cacheRestore4);
|
|
18
|
+
export const CACHE_SAVE = usesRef(PINS.cacheSave4);
|
|
16
19
|
/** The checkout step; `fullHistory` fetches every commit, for a job that reads the base commit or measures a change with git. */
|
|
17
20
|
export function checkoutStep(fullHistory = false) {
|
|
18
|
-
return fullHistory ? { uses:
|
|
21
|
+
return fullHistory ? { uses: CHECKOUT, with: { "fetch-depth": 0 } } : { uses: CHECKOUT };
|
|
19
22
|
}
|
|
20
23
|
/** One `run:` step per line, so each line is its own step in the log. */
|
|
21
24
|
export function runSteps(lines) {
|
|
@@ -106,5 +109,5 @@ export function emitWorkflowYAML(doc) {
|
|
|
106
109
|
if (doc.permissions && Object.keys(doc.permissions).length > 0)
|
|
107
110
|
sections.push(emitYAMLEntry("permissions", doc.permissions));
|
|
108
111
|
sections.push(emitYAMLEntry("jobs", doc.jobsDoc));
|
|
109
|
-
return sections.join("\n\n") + "\n";
|
|
112
|
+
return tagComments(sections.join("\n\n") + "\n");
|
|
110
113
|
}
|
|
@@ -124,6 +124,13 @@ export interface GithubOpPipelineDoc {
|
|
|
124
124
|
* artifact instead (#2256).
|
|
125
125
|
*/
|
|
126
126
|
gatedNoticeDoc?: Record<string, unknown>;
|
|
127
|
+
/**
|
|
128
|
+
* The review note job (#4326), when the spec sets `reviewAgent`. Kept out
|
|
129
|
+
* of {@link jobsDoc} for the reason the gate notice is: a member's
|
|
130
|
+
* `working-directory` default belongs to the job that runs the Op, and a
|
|
131
|
+
* dialect transforms this job on its own terms.
|
|
132
|
+
*/
|
|
133
|
+
reviewNoteDoc?: Record<string, unknown>;
|
|
127
134
|
}
|
|
128
135
|
/** One generated file: a suggested name plus its pipeline document, pre-emission. */
|
|
129
136
|
export interface GithubOpPipelineFile {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-op-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-op-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsEG;AAGH,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAOhB,gBAAgB,EAIjB,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EACV,wBAAwB,IAAI,uBAAuB,EAEnD,aAAa,EACb,gBAAgB,IAAI,sBAAsB,EAG1C,eAAe,EAChB,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"generate-op-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-op-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsEG;AAGH,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAOhB,gBAAgB,EAIjB,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EACV,wBAAwB,IAAI,uBAAuB,EAEnD,aAAa,EACb,gBAAgB,IAAI,sBAAsB,EAG1C,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAIlC,YAAY,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,CAAC;AAIhE,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,CAAC;AAEjE;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5B,2DAA2D;IAC3D,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACzC;AAED,qFAAqF;AACrF,MAAM,WAAW,oBAAoB;IACnC,4FAA4F;IAC5F,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,mBAAmB,CAAC;CAC1B;AAuOD;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,eAAe,EAAE,EACtB,OAAO,GAAE,uBAA4B,GACpC;IAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAAC,IAAI,EAAE,aAAa,EAAE,CAAA;CAAE,CAoH1D;AAwCD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,mBAAmB,GAAG,MAAM,CAMnE;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,eAAe,EAAE,EACtB,OAAO,GAAE,uBAA4B,GACpC,sBAAsB,CAMxB"}
|
|
@@ -73,6 +73,7 @@ import { readFileSync } from "node:fs";
|
|
|
73
73
|
import { assertEnvironment, assertSetupSteps, CI_IMAGE, ciJobName, environmentDoc, fillCommand, GITHUB_JOB_TOKEN, GITHUB_RUNNER, mergePermissions, nonEmpty, permissionsFor, setupStepDoc, } from "@intentius/chant/ci-jobs";
|
|
74
74
|
import { memberPathFilter, resolveOpTrigger } from "@intentius/chant/lexicon";
|
|
75
75
|
import { checkoutStep, concurrency, emitWorkflowYAML, jobTokenEnv, onTrigger, opConcurrencyGroup, runSteps } from "./ci-jobs.js";
|
|
76
|
+
import { assertReviewSplit, reviewJobs, reviewNoteJobName } from "./review-jobs.js";
|
|
76
77
|
// The setup, environment and permission checks are shared with the Op waves
|
|
77
78
|
// generator and the forgejo lexicon (#3765); they stay importable from here.
|
|
78
79
|
export { assertEnvironment, assertSetupSteps, mergePermissions };
|
|
@@ -316,7 +317,11 @@ export function buildGithubOpPipelineDocs(ops, options = {}) {
|
|
|
316
317
|
const runParts = fillCommand(runCommand, spec.name);
|
|
317
318
|
// A live-resolution read (rate limits) always benefits from a token;
|
|
318
319
|
// creating an issue/PR additionally needs `gh` CLI's own token variable.
|
|
319
|
-
|
|
320
|
+
// A split review (#4326) runs the change's code beside a model: no step of that job holds a forge token.
|
|
321
|
+
const split = spec.reviewAgent === true;
|
|
322
|
+
if (split)
|
|
323
|
+
assertReviewSplit(spec.name, trigger, findingMode, spec.permissions);
|
|
324
|
+
const stepEnv = split ? {} : jobTokenEnv(findingMode !== "report");
|
|
320
325
|
const setup = spec.setup ?? [];
|
|
321
326
|
assertSetupSteps(spec.name, setup);
|
|
322
327
|
if (spec.environment)
|
|
@@ -334,6 +339,19 @@ export function buildGithubOpPipelineDocs(ops, options = {}) {
|
|
|
334
339
|
? gatedRunStep(RUN_STEP_ID, spec.name, invocation, stepEnv)
|
|
335
340
|
: { run: invocation, env: stepEnv });
|
|
336
341
|
steps.push(...runSteps(extraScript));
|
|
342
|
+
const review = split
|
|
343
|
+
? reviewJobs({
|
|
344
|
+
name: spec.name,
|
|
345
|
+
jobName,
|
|
346
|
+
image,
|
|
347
|
+
invocation,
|
|
348
|
+
setup: setup.map(setupStepDoc),
|
|
349
|
+
beforeScript,
|
|
350
|
+
extraScript,
|
|
351
|
+
...(spec.variables ? { variables: spec.variables } : {}),
|
|
352
|
+
...(options.member ? { member: options.member } : {}),
|
|
353
|
+
})
|
|
354
|
+
: undefined;
|
|
337
355
|
const doc = {
|
|
338
356
|
name: spec.name,
|
|
339
357
|
on: onTrigger(trigger),
|
|
@@ -369,6 +387,11 @@ export function buildGithubOpPipelineDocs(ops, options = {}) {
|
|
|
369
387
|
},
|
|
370
388
|
...(gated ? { gatedNoticeDoc: { [`${jobName}-gate-notice`]: gateNoticeJob(jobName, image) } } : {}),
|
|
371
389
|
};
|
|
390
|
+
// A split review (#4326): the Op's job is the review job, and the note job rides beside it.
|
|
391
|
+
if (review) {
|
|
392
|
+
doc.jobsDoc = { [jobName]: { ...review.review, ...(spec.environment ? { environment: environmentDoc(spec.environment) } : {}) } };
|
|
393
|
+
doc.reviewNoteDoc = { [reviewNoteJobName(jobName)]: review.note };
|
|
394
|
+
}
|
|
372
395
|
files.push({ name: `${spec.name}.yml`, doc });
|
|
373
396
|
}
|
|
374
397
|
if (options.member) {
|
|
@@ -422,8 +445,8 @@ export function emitOpPipelineYAML(doc) {
|
|
|
422
445
|
// The gated-apply notice job rides in `jobs:` beside the Op's own job; it
|
|
423
446
|
// is carried separately on the doc so a dialect transforms it on its own.
|
|
424
447
|
// A dialect's `header` note (#2257) is absent on github.
|
|
425
|
-
const { gatedNoticeDoc, ...workflow } = doc;
|
|
426
|
-
return emitWorkflowYAML({ ...workflow, jobsDoc: { ...doc.jobsDoc, ...(gatedNoticeDoc ?? {}) } });
|
|
448
|
+
const { gatedNoticeDoc, reviewNoteDoc, ...workflow } = doc;
|
|
449
|
+
return emitWorkflowYAML({ ...workflow, jobsDoc: { ...doc.jobsDoc, ...(gatedNoticeDoc ?? {}), ...(reviewNoteDoc ?? {}) } });
|
|
427
450
|
}
|
|
428
451
|
/**
|
|
429
452
|
* Synthesize one `.github/workflows/*.yml` per scheduled Op. Thin wrapper
|
|
@@ -3,9 +3,17 @@
|
|
|
3
3
|
*
|
|
4
4
|
* One workflow for the whole spec, on a push to its branches (and by hand),
|
|
5
5
|
* one run at a time. Each job is one of core's `opWaveJobs`: a wave, or a wide
|
|
6
|
-
* wave's deciding job and share jobs, chained by `needs
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* wave's deciding job and share jobs, chained by `needs:`. Re-running the
|
|
7
|
+
* failed jobs after `chant approve` carries on from that wave.
|
|
8
|
+
*
|
|
9
|
+
* Each job keeps its outcome file as an artifact whatever its exit code
|
|
10
|
+
* (#4322), and a job with `needs:` downloads every artifact of the run into
|
|
11
|
+
* the outcome directory first. A wave or deciding job after the first runs
|
|
12
|
+
* unless the run was cancelled (`if: !cancelled()`), so it runs after a
|
|
13
|
+
* failure in the wave before: it blocks the runs that depend on a failed one
|
|
14
|
+
* and applies the rest, and it stops by itself when an earlier wave waits at
|
|
15
|
+
* its gate (exit 3) or applied nothing for another reason. A share job still
|
|
16
|
+
* needs its deciding job to succeed.
|
|
9
17
|
*
|
|
10
18
|
* Every job checks out the full history, because the runner reads the gate
|
|
11
19
|
* policy from the spec file at the base commit and records pending gates on
|
|
@@ -49,6 +57,8 @@ import { type GithubOpPipelineDoc } from "./generate-op-pipeline.js";
|
|
|
49
57
|
export declare const OP_WAVES_IMAGE = "node:22";
|
|
50
58
|
/** The artifact a wide wave's deciding job hands its share jobs. */
|
|
51
59
|
export declare function opWaveRecordArtifact(spec: Pick<OpWavesSpec, "name">, wave: number): string;
|
|
60
|
+
/** The artifact a job keeps its outcome file in (#4322). */
|
|
61
|
+
export declare function opWaveOutcomeArtifact(spec: Pick<OpWavesSpec, "name">, jobName: string): string;
|
|
52
62
|
/** The workflow document, before emission, and the jobs it holds. */
|
|
53
63
|
export declare function buildGithubOpWavesDoc(spec: OpWavesSpec, options: OpWavesPipelineOptions): {
|
|
54
64
|
doc: GithubOpPipelineDoc;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-op-waves-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-op-waves-pipeline.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"generate-op-waves-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-op-waves-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAEH,OAAO,EAAsF,KAAK,WAAW,EAAE,MAAM,8BAA8B,CAAC;AACpJ,OAAO,KAAK,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAiB9F,OAAO,EAAsB,KAAK,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAEtF,yFAAyF;AACzF,eAAO,MAAM,cAAc,YAAW,CAAC;AAEvC,oEAAoE;AACpE,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE1F;AAED,4DAA4D;AAC5D,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAE9F;AAED,qEAAqE;AACrE,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,sBAAsB,GAC9B;IAAE,GAAG,EAAE,mBAAmB,CAAC;IAAC,IAAI,EAAE,qBAAqB,CAAC,MAAM,CAAC,CAAC;IAAC,SAAS,CAAC,EAAE,mBAAmB,CAAC;IAAC,QAAQ,CAAC,EAAE,mBAAmB,CAAA;CAAE,CAuEpI;AAoDD,4EAA4E;AAC5E,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,sBAAsB,GAAG,qBAAqB,CAMvH"}
|
|
@@ -3,9 +3,17 @@
|
|
|
3
3
|
*
|
|
4
4
|
* One workflow for the whole spec, on a push to its branches (and by hand),
|
|
5
5
|
* one run at a time. Each job is one of core's `opWaveJobs`: a wave, or a wide
|
|
6
|
-
* wave's deciding job and share jobs, chained by `needs
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* wave's deciding job and share jobs, chained by `needs:`. Re-running the
|
|
7
|
+
* failed jobs after `chant approve` carries on from that wave.
|
|
8
|
+
*
|
|
9
|
+
* Each job keeps its outcome file as an artifact whatever its exit code
|
|
10
|
+
* (#4322), and a job with `needs:` downloads every artifact of the run into
|
|
11
|
+
* the outcome directory first. A wave or deciding job after the first runs
|
|
12
|
+
* unless the run was cancelled (`if: !cancelled()`), so it runs after a
|
|
13
|
+
* failure in the wave before: it blocks the runs that depend on a failed one
|
|
14
|
+
* and applies the rest, and it stops by itself when an earlier wave waits at
|
|
15
|
+
* its gate (exit 3) or applied nothing for another reason. A share job still
|
|
16
|
+
* needs its deciding job to succeed.
|
|
9
17
|
*
|
|
10
18
|
* Every job checks out the full history, because the runner reads the gate
|
|
11
19
|
* policy from the spec file at the base commit and records pending gates on
|
|
@@ -42,10 +50,10 @@
|
|
|
42
50
|
* token is requested with, so the code the job runs holds no OIDC token
|
|
43
51
|
* and cannot request one for another group's identity.
|
|
44
52
|
*/
|
|
45
|
-
import { notifyVariables, opWaveGroupOf, opWaveJobs, opWavesResumeCommand } from "@intentius/chant/op/op-waves";
|
|
53
|
+
import { notifyVariables, opWaveGroupOf, opWaveJobs, opWaveOutcomeDir, opWavesResumeCommand } from "@intentius/chant/op/op-waves";
|
|
46
54
|
import { assertEnvironment, assertSetupSteps, CI_IMAGE, dirnameOf, environmentDoc, GITHUB_JOB_TOKEN, githubRunsOn, githubSecretEnv, idTokenAudienceEnv, nonEmpty, pushBranches, setupStepDoc, } from "@intentius/chant/ci-jobs";
|
|
47
55
|
import { OIDC_REQUEST_VARS, TRADE_SPEC_ENV, tradeLine } from "@intentius/chant/ci-identity";
|
|
48
|
-
import { checkoutStep, concurrency, downloadArtifactStep, keepRecordStep, runSteps } from "./ci-jobs.js";
|
|
56
|
+
import { checkoutStep, concurrency, DOWNLOAD_ARTIFACT, downloadArtifactStep, keepRecordStep, runSteps } from "./ci-jobs.js";
|
|
49
57
|
import { emitOpPipelineYAML } from "./generate-op-pipeline.js";
|
|
50
58
|
/** The default image: it carries git, which the runner needs to read the base commit. */
|
|
51
59
|
export const OP_WAVES_IMAGE = CI_IMAGE;
|
|
@@ -53,6 +61,10 @@ export const OP_WAVES_IMAGE = CI_IMAGE;
|
|
|
53
61
|
export function opWaveRecordArtifact(spec, wave) {
|
|
54
62
|
return `${spec.name}-wave-${wave}-decision`;
|
|
55
63
|
}
|
|
64
|
+
/** The artifact a job keeps its outcome file in (#4322). */
|
|
65
|
+
export function opWaveOutcomeArtifact(spec, jobName) {
|
|
66
|
+
return `${spec.name}-${jobName}-outcome`;
|
|
67
|
+
}
|
|
56
68
|
/** The workflow document, before emission, and the jobs it holds. */
|
|
57
69
|
export function buildGithubOpWavesDoc(spec, options) {
|
|
58
70
|
const jobs = opWaveJobs(spec, options.specFile);
|
|
@@ -80,6 +92,10 @@ export function buildGithubOpWavesDoc(spec, options) {
|
|
|
80
92
|
if (job.kind === "share") {
|
|
81
93
|
steps.push(downloadArtifactStep(`Download wave ${job.wave}'s decision`, opWaveRecordArtifact(spec, job.wave), dirnameOf(job.record)));
|
|
82
94
|
}
|
|
95
|
+
if (job.needs.length > 0) {
|
|
96
|
+
// #4322: every artifact of the run, so the job reads each earlier job's outcome file.
|
|
97
|
+
steps.push({ name: "Download the earlier jobs' outcomes", uses: DOWNLOAD_ARTIFACT, with: { path: opWaveOutcomeDir(spec) } });
|
|
98
|
+
}
|
|
83
99
|
const later = [];
|
|
84
100
|
later.push(...[...(wave.setup ?? []), ...(group?.setup ?? [])].map(setupStepDoc), ...runSteps(options.beforeScript ?? []));
|
|
85
101
|
later.push({ name: describeJob(job.kind, job.wave, wave.name, job.share, job.group), run: job.command.join(" ") });
|
|
@@ -88,12 +104,15 @@ export function buildGithubOpWavesDoc(spec, options) {
|
|
|
88
104
|
if (job.kind === "decide") {
|
|
89
105
|
steps.push(keepRecordStep(`Keep wave ${job.wave}'s decision`, opWaveRecordArtifact(spec, job.wave), job.record));
|
|
90
106
|
}
|
|
107
|
+
steps.push(keepRecordStep(`Keep the job's outcome`, opWaveOutcomeArtifact(spec, job.jobName), job.outcome));
|
|
91
108
|
const environment = group?.environment ?? wave.environment;
|
|
92
109
|
const idTokens = group?.idTokens ?? wave.idTokens;
|
|
93
110
|
const idToken = idTokens !== undefined || cloud !== undefined;
|
|
94
111
|
jobsDoc[job.jobName] = {
|
|
95
112
|
"runs-on": githubRunsOn(group?.runner ?? wave.runner ?? spec.runner),
|
|
96
113
|
...(job.needs.length > 0 ? { needs: job.needs } : {}),
|
|
114
|
+
// #4322: a wave's jobs run after a failure in the wave before; a share job runs only on its deciding job's success.
|
|
115
|
+
...(job.needs.length > 0 && job.kind !== "share" ? { if: "!cancelled()" } : {}),
|
|
97
116
|
container: image,
|
|
98
117
|
...(environment ? { environment: environmentDoc(environment) } : {}),
|
|
99
118
|
// #3914: a job's own permissions replace the workflow's, so they repeat them.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AAEH,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"generate-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AAEH,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAgCjG,OAAO,KAAK,EACV,oBAAoB,IAAI,YAAY,EACpC,wBAAwB,IAAI,qBAAqB,EACjD,uBAAuB,IAAI,oBAAoB,EAEhD,MAAM,0BAA0B,CAAC;AAGlC,YAAY,EAAE,YAAY,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,CAAC;AAE1E;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;;OAKG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,6FAA6F;IAC7F,WAAW,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5B,mIAAmI;IACnI,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,uEAAuE;IACvE,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,0DAA0D;IAC1D,IAAI,EAAE,YAAY,EAAE,CAAC;CACtB;AAgfD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAWpD;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,eAAe,EAAE,EAC7B,OAAO,GAAE,qBAA0B,GAClC,iBAAiB,CA+JnB;AA4CD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,iBAAiB,GAAG,MAAM,CAE/D;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,eAAe,EAAE,EAC7B,OAAO,GAAE,qBAA0B,GAClC,oBAAoB,CAGtB"}
|
|
@@ -61,10 +61,9 @@ import { GATED_WAVE_RECORD, gatedWaveJobs } from "@intentius/chant/components/ga
|
|
|
61
61
|
import { CI_IMAGE, ciJobName, dirnameOf, fillCommand, GITHUB_JOB_TOKEN, GITHUB_RUNNER } from "@intentius/chant/ci-jobs";
|
|
62
62
|
import { CHECKOUT_TOKEN_ENV, checkoutScript, dropForgeTokens, handoverOutputs, handoverTrap, takeOverScript } from "@intentius/chant/ci-handover";
|
|
63
63
|
import { OIDC_REQUEST_VARS, TRADE_SPEC_ENV, tradeLine } from "@intentius/chant/ci-identity";
|
|
64
|
-
import { APPLY_COMMENT, GREEN_TAG_PATTERN, GREEN_TICK_WORKFLOW, PR_APPLY_RECORD, PR_LOOP_REPORT_DIR, prApplyGroup, prRecordGroup, prApplyRecordKey, prApplyWaveJobName, prLoopJobs, prLoopPostJob, prLoopSetup, PR_PLAN_NOTE_JOB, prLoopWaveJobs, prLoopWithWaves, prEnvJobs, PR_ENV_REPORT_DIR, PR_ENV_SWEEP_CRON, PR_ENV_POST_JOB, } from "@intentius/chant/components/pr-pipeline";
|
|
64
|
+
import { APPLY_COMMENT, GREEN_TAG_PATTERN, GREEN_TICK_WORKFLOW, PR_APPLY_RECORD, PR_LOOP_REPORT_DIR, prApplyGroup, prRecordGroup, prApplyRecordKey, prApplyWaveJobName, prLoopJobs, prLoopPostJob, prLoopSetup, PR_PLAN_NOTE_JOB, PR_LOCK_JOB, PR_UNLOCK_JOB, PR_UNLOCK_COMMAND, prLockCommand, prLoopWaveJobs, prLoopWithWaves, prEnvJobs, PR_ENV_REPORT_DIR, PR_ENV_SWEEP_CRON, PR_ENV_POST_JOB, } from "@intentius/chant/components/pr-pipeline";
|
|
65
65
|
import { memberRepoPath } from "@intentius/chant/lexicon";
|
|
66
|
-
import { afterEarlierWave, checkoutStep, concurrency, downloadArtifactStep, emitWorkflowYAML, keepRecordStep, runSteps, UPLOAD_ARTIFACT } from "./ci-jobs.js";
|
|
67
|
-
import { actionRef } from "../action-pins.js";
|
|
66
|
+
import { afterEarlierWave, CACHE_RESTORE, CACHE_SAVE, CHECKOUT, checkoutStep, concurrency, downloadArtifactStep, emitWorkflowYAML, keepRecordStep, runSteps, UPLOAD_ARTIFACT } from "./ci-jobs.js";
|
|
68
67
|
/** The plan job's last step, whose outputs carry its report to the plan-note job (#4295). */
|
|
69
68
|
const PLAN_STEP_ID = "plan";
|
|
70
69
|
/** The output a gated apply wave hands the green commit it applies through, and the step that sets it (#4221). */
|
|
@@ -185,13 +184,13 @@ function prLoopGithubDoc(components, env, image, beforeScript, extraScript, opti
|
|
|
185
184
|
const recordKey = (wave) => `${recordPrefix}\${{ github.run_id }}-\${{ github.run_attempt }}${wave ? `-wave-${wave}` : ""}`;
|
|
186
185
|
const restoreRecord = (wave) => ({
|
|
187
186
|
name: wave ? "Restore the newest record of this commit's apply" : "Restore the record of an earlier attempt at this commit",
|
|
188
|
-
uses:
|
|
187
|
+
uses: CACHE_RESTORE,
|
|
189
188
|
with: { path: recordPath, key: recordKey(wave), "restore-keys": recordPrefix },
|
|
190
189
|
});
|
|
191
190
|
const saveRecord = (wave) => ({
|
|
192
191
|
name: wave ? "Keep the record for the next wave and a re-run" : "Keep the record for a re-run",
|
|
193
192
|
if: "always()",
|
|
194
|
-
uses:
|
|
193
|
+
uses: CACHE_SAVE,
|
|
195
194
|
with: { path: recordPath, key: recordKey(wave) },
|
|
196
195
|
});
|
|
197
196
|
// #4296: the step that trades the job's OIDC token for its cloud identity, before the checkout.
|
|
@@ -295,7 +294,7 @@ function prLoopGithubDoc(components, env, image, beforeScript, extraScript, opti
|
|
|
295
294
|
const post = prLoopPostJob(forgeName, env, member?.name);
|
|
296
295
|
const noteSteps = [
|
|
297
296
|
// The target branch's commit: the chant this job runs is the target branch's, and none of the change's code is here.
|
|
298
|
-
{ name: "Check out the target branch's commit", uses:
|
|
297
|
+
{ name: "Check out the target branch's commit", uses: CHECKOUT, with: { ref: "${{ github.event.pull_request.base.sha }}" } },
|
|
299
298
|
...runSteps(beforeScript),
|
|
300
299
|
{ name: "Take the plan report from the plan job", run: takeOverScript("plan", PR_LOOP_REPORT_DIR, forgeName) },
|
|
301
300
|
{ name: "Keep the plan report", if: "always()", uses: UPLOAD_ARTIFACT, with: { name: reportName(plan), path: reportPath, "if-no-files-found": "ignore", "include-hidden-files": true } },
|
|
@@ -303,9 +302,46 @@ function prLoopGithubDoc(components, env, image, beforeScript, extraScript, opti
|
|
|
303
302
|
];
|
|
304
303
|
// #4298: with a pull request environment the pull_request trigger fires on close too, and the plan skips that event.
|
|
305
304
|
const prEnv = loop.prEnvironment ? prEnvGithubJobs(loop.forge ?? "github", image, beforeScript, extraScript, loop, member) : undefined;
|
|
305
|
+
// #4327: with ci.locks: plan the pull_request trigger fires on close too, for the unlock job, and the plan skips that event.
|
|
306
|
+
const locks = loop.locks === "plan";
|
|
307
|
+
const closes = Boolean(prEnv) || locks;
|
|
308
|
+
const lockJobs = locks
|
|
309
|
+
? {
|
|
310
|
+
[PR_LOCK_JOB]: {
|
|
311
|
+
// The plan job's outputs carry the report; a plan that was skipped or failed leaves this job skipped.
|
|
312
|
+
needs: ["plan"],
|
|
313
|
+
if: "github.event_name == 'pull_request'",
|
|
314
|
+
"runs-on": GITHUB_RUNNER,
|
|
315
|
+
container: image,
|
|
316
|
+
permissions: { contents: "write", "pull-requests": "read", statuses: "write" },
|
|
317
|
+
steps: [
|
|
318
|
+
// The target branch's commit: this job pushes chant/lifecycle, so none of the change's code runs in it.
|
|
319
|
+
{ name: "Check out the target branch's commit", uses: CHECKOUT, with: { ref: "${{ github.event.pull_request.base.sha }}", "fetch-depth": 0 } },
|
|
320
|
+
...runSteps(beforeScript),
|
|
321
|
+
{ name: "Take the plan report from the plan job", run: takeOverScript("plan", PR_LOOP_REPORT_DIR, forgeName) },
|
|
322
|
+
{
|
|
323
|
+
name: "Lock the components the plan selected",
|
|
324
|
+
env: { PR_NUMBER: "${{ github.event.pull_request.number }}", HEAD_SHA: "${{ github.event.pull_request.head.sha }}", GITHUB_TOKEN: GITHUB_JOB_TOKEN },
|
|
325
|
+
run: prLockCommand(forgeName),
|
|
326
|
+
},
|
|
327
|
+
],
|
|
328
|
+
},
|
|
329
|
+
[PR_UNLOCK_JOB]: {
|
|
330
|
+
if: "github.event_name == 'pull_request' && github.event.action == 'closed'",
|
|
331
|
+
"runs-on": GITHUB_RUNNER,
|
|
332
|
+
container: image,
|
|
333
|
+
permissions: { contents: "write" },
|
|
334
|
+
steps: [
|
|
335
|
+
{ name: "Check out the target branch's commit", uses: CHECKOUT, with: { ref: "${{ github.event.pull_request.base.sha }}", "fetch-depth": 0 } },
|
|
336
|
+
...runSteps(beforeScript),
|
|
337
|
+
{ name: "Release the pull request's locks", env: { PR_NUMBER: "${{ github.event.pull_request.number }}" }, run: PR_UNLOCK_COMMAND },
|
|
338
|
+
],
|
|
339
|
+
},
|
|
340
|
+
}
|
|
341
|
+
: {};
|
|
306
342
|
const jobsDoc = {
|
|
307
343
|
plan: {
|
|
308
|
-
if:
|
|
344
|
+
if: closes ? "github.event_name == 'pull_request' && github.event.action != 'closed'" : "github.event_name == 'pull_request'",
|
|
309
345
|
"runs-on": GITHUB_RUNNER,
|
|
310
346
|
container: image,
|
|
311
347
|
permissions: withIdToken({ contents: "read" }),
|
|
@@ -321,6 +357,7 @@ function prLoopGithubDoc(components, env, image, beforeScript, extraScript, opti
|
|
|
321
357
|
permissions: { actions: "read", contents: "read", "pull-requests": "write", statuses: "write" },
|
|
322
358
|
steps: noteSteps,
|
|
323
359
|
},
|
|
360
|
+
...lockJobs,
|
|
324
361
|
...Object.fromEntries(applyJobs),
|
|
325
362
|
...(prEnv ? prEnv.jobs : {}),
|
|
326
363
|
};
|
|
@@ -330,6 +367,8 @@ function prLoopGithubDoc(components, env, image, beforeScript, extraScript, opti
|
|
|
330
367
|
: forgejo
|
|
331
368
|
? { pull_request: { branches: [branch] }, push: { branches: [branch], tags: [GREEN_TAG_PATTERN] } }
|
|
332
369
|
: { pull_request: { branches: [branch] }, push: { branches: [branch] }, workflow_run: { workflows: [GREEN_TICK_WORKFLOW], types: ["completed"] }, workflow_dispatch: {} };
|
|
370
|
+
if (closes)
|
|
371
|
+
on.pull_request = { branches: [branch], types: ["opened", "synchronize", "reopened", "closed"] };
|
|
333
372
|
if (prEnv) {
|
|
334
373
|
on.pull_request = { branches: [branch], types: ["opened", "synchronize", "reopened", "closed"] };
|
|
335
374
|
on.schedule = [{ cron: PR_ENV_SWEEP_CRON }];
|
|
@@ -344,10 +383,16 @@ function prLoopGithubDoc(components, env, image, beforeScript, extraScript, opti
|
|
|
344
383
|
...(rooted ? {} : { defaults: { run: { "working-directory": member.dir } } }),
|
|
345
384
|
env: { ...options.variables, CHANT_ENV: env },
|
|
346
385
|
jobsDoc,
|
|
347
|
-
stages: ["plan", PR_PLAN_NOTE_JOB, ...applyNames, ...(prEnv ? ["env-up", PR_ENV_POST_JOB, "env-down", "env-sweep"] : [])],
|
|
386
|
+
stages: ["plan", PR_PLAN_NOTE_JOB, ...(locks ? [PR_LOCK_JOB, PR_UNLOCK_JOB] : []), ...applyNames, ...(prEnv ? ["env-up", PR_ENV_POST_JOB, "env-down", "env-sweep"] : [])],
|
|
348
387
|
jobs: [
|
|
349
388
|
{ jobName: "plan", component: "pull request plan", stage: "plan", needs: [] },
|
|
350
389
|
{ jobName: PR_PLAN_NOTE_JOB, component: "pull request plan note", stage: PR_PLAN_NOTE_JOB, needs: ["plan"] },
|
|
390
|
+
...(locks
|
|
391
|
+
? [
|
|
392
|
+
{ jobName: PR_LOCK_JOB, component: "pull request lock", stage: PR_LOCK_JOB, needs: ["plan"] },
|
|
393
|
+
{ jobName: PR_UNLOCK_JOB, component: "pull request unlock", stage: PR_UNLOCK_JOB, needs: [] },
|
|
394
|
+
]
|
|
395
|
+
: []),
|
|
351
396
|
...applyNames.map((name, i) => ({
|
|
352
397
|
jobName: name,
|
|
353
398
|
component: name === "record" ? "merge record" : loop.applyBeforeMerge ? "apply before merge" : loop.waveGate ? `green apply, wave ${i}` : "green apply",
|
|
@@ -426,7 +471,7 @@ function prEnvGithubJobs(forge, image, beforeScript, extraScript, loop, member)
|
|
|
426
471
|
permissions: writes,
|
|
427
472
|
steps: [
|
|
428
473
|
// The target branch's commit: the chant this job runs is the target branch's, and none of the change's code is here.
|
|
429
|
-
{ name: "Check out the target branch's commit", uses:
|
|
474
|
+
{ name: "Check out the target branch's commit", uses: CHECKOUT, with: { ref: "${{ github.event.pull_request.base.sha }}", "fetch-depth": 0 } },
|
|
430
475
|
...runSteps(beforeScript),
|
|
431
476
|
{ name: "Take the environment's report from the env-up job", run: takeOverScript("env-up", PR_ENV_REPORT_DIR, forge) },
|
|
432
477
|
keep("env-up"),
|
|
@@ -441,7 +486,7 @@ function prEnvGithubJobs(forge, image, beforeScript, extraScript, loop, member)
|
|
|
441
486
|
permissions: withIdToken(writes),
|
|
442
487
|
steps: [
|
|
443
488
|
...tradeStep,
|
|
444
|
-
{ uses:
|
|
489
|
+
{ uses: CHECKOUT, with: { "fetch-depth": 0, ...(ref ? { ref } : {}) } },
|
|
445
490
|
...runSteps(prLoopSetup(image)),
|
|
446
491
|
...runSteps(beforeScript),
|
|
447
492
|
{ name, env: j.env, run: j.command },
|