@codyswann/lisa 4.42.1 → 4.42.3
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/all/copy-overwrite/scripts/check-orphaned-branches.mjs +458 -29
- package/dist/cli/doctor-reusable-workflow-refs.d.ts +3 -3
- package/dist/cli/doctor-reusable-workflow-refs.d.ts.map +1 -1
- package/dist/cli/doctor-reusable-workflow-refs.js +46 -74
- package/dist/cli/doctor-reusable-workflow-refs.js.map +1 -1
- package/dist/cli/version.d.ts +17 -0
- package/dist/cli/version.d.ts.map +1 -1
- package/dist/cli/version.js +34 -0
- package/dist/cli/version.js.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +18 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/lisa-release-pin.d.ts +63 -0
- package/dist/core/lisa-release-pin.d.ts.map +1 -0
- package/dist/core/lisa-release-pin.js +147 -0
- package/dist/core/lisa-release-pin.js.map +1 -0
- package/dist/core/lisa.d.ts.map +1 -1
- package/dist/core/lisa.js +4 -1
- package/dist/core/lisa.js.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
- package/dist/core/reusable-workflow-pin.d.ts +113 -0
- package/dist/core/reusable-workflow-pin.d.ts.map +1 -0
- package/dist/core/reusable-workflow-pin.js +165 -0
- package/dist/core/reusable-workflow-pin.js.map +1 -0
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +17 -8
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/migrations/ensure-nightly-e2e-workflow-pins.d.ts +16 -36
- package/dist/migrations/ensure-nightly-e2e-workflow-pins.d.ts.map +1 -1
- package/dist/migrations/ensure-nightly-e2e-workflow-pins.js +33 -76
- package/dist/migrations/ensure-nightly-e2e-workflow-pins.js.map +1 -1
- package/dist/migrations/ensure-pinned-reusable-workflow-refs.d.ts +143 -0
- package/dist/migrations/ensure-pinned-reusable-workflow-refs.d.ts.map +1 -0
- package/dist/migrations/ensure-pinned-reusable-workflow-refs.js +293 -0
- package/dist/migrations/ensure-pinned-reusable-workflow-refs.js.map +1 -0
- package/dist/migrations/index.d.ts +1 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/index.js +5 -0
- package/dist/migrations/index.js.map +1 -1
- package/package.json +5 -4
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-drive-pr-to-merge/SKILL.md +27 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-git-submit-pr/SKILL.md +16 -0
- package/plugins/lisa/skills/lisa-drive-pr-to-merge/SKILL.md +27 -0
- package/plugins/lisa/skills/lisa-git-submit-pr/SKILL.md +16 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-drive-pr-to-merge/SKILL.md +27 -0
- package/plugins/lisa-agy/skills/lisa-git-submit-pr/SKILL.md +16 -0
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/skills/lisa-drive-pr-to-merge/SKILL.md +27 -0
- package/plugins/lisa-copilot/skills/lisa-git-submit-pr/SKILL.md +16 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/skills/lisa-drive-pr-to-merge/SKILL.md +27 -0
- package/plugins/lisa-cursor/skills/lisa-git-submit-pr/SKILL.md +16 -0
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/skills/lisa-drive-pr-to-merge/SKILL.md +27 -0
- package/plugins/src/base/skills/lisa-git-submit-pr/SKILL.md +16 -0
- package/scripts/lisa-update-local.sh +1 -0
- package/scripts/measure-tmpdir-growth.mjs +24 -3
- package/typescript/copy-overwrite/.github/GITHUB_ACTIONS.md +6 -1
- package/typescript/copy-overwrite/scripts/check-skipped-required-checks.mjs +370 -4
- package/typescript/create-only/.github/workflows/review-evidence.yml +20 -0
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import type { Migration, MigrationContext, MigrationResult } from "./migration.interface.js";
|
|
2
2
|
/** Read the installed Lisa package version. */
|
|
3
3
|
type VersionReader = () => string;
|
|
4
|
-
/** Read the release tag stamped into the installed package. */
|
|
5
|
-
type ReleaseTagReader = () => string | null;
|
|
6
4
|
/**
|
|
7
|
-
* Keep an installed nightly E2E caller
|
|
8
|
-
* and
|
|
5
|
+
* Keep an installed nightly E2E caller's bypass gate armed against
|
|
6
|
+
* body-evidence deletion, and its explanatory version comments current.
|
|
9
7
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
8
|
+
* This used to rewrite the caller's `uses:` ref as well. It no longer does:
|
|
9
|
+
* `ensure-pinned-reusable-workflow-refs` pins EVERY Lisa reusable caller,
|
|
10
|
+
* these two included, at the commit the installed version's tag names. Two
|
|
11
|
+
* migrations rewriting the same line would each undo the other on every apply
|
|
12
|
+
* — content-stable, endlessly "applied", and pinned at whichever ran last.
|
|
13
13
|
*
|
|
14
|
-
* The
|
|
15
|
-
* lives. The caller ships from `create-only` and is marked "this file is YOURS
|
|
14
|
+
* The remaining job is here rather than in the template because of where the
|
|
15
|
+
* file lives. The caller ships from `create-only` and is marked "this file is YOURS
|
|
16
16
|
* — Lisa will not overwrite it", so a template fix reaches new adoptions only
|
|
17
17
|
* and every already-seeded repository keeps its original trigger list forever.
|
|
18
18
|
* The reusable workflow cannot carry the fix either: it is `on: workflow_call`,
|
|
@@ -21,49 +21,28 @@ type ReleaseTagReader = () => string | null;
|
|
|
21
21
|
*/
|
|
22
22
|
export declare class EnsureNightlyE2EWorkflowPinsMigration implements Migration {
|
|
23
23
|
private readonly readLisaVersion;
|
|
24
|
-
private readonly readReleaseTag;
|
|
25
24
|
readonly name = "ensure-nightly-e2e-workflow-pins";
|
|
26
|
-
readonly description = "
|
|
25
|
+
readonly description = "Keep the nightly E2E bypass gate armed against body-evidence deletion and its version comments current";
|
|
27
26
|
/**
|
|
28
27
|
* Create the migration.
|
|
29
28
|
*
|
|
30
29
|
* @param readLisaVersion - Version reader, injectable for deterministic tests
|
|
31
|
-
* @param readReleaseTag - Published release tag reader
|
|
32
30
|
*/
|
|
33
|
-
constructor(readLisaVersion?: VersionReader
|
|
31
|
+
constructor(readLisaVersion?: VersionReader);
|
|
34
32
|
/**
|
|
35
|
-
* Decide whether an Expo caller still
|
|
33
|
+
* Decide whether an installed Expo caller still needs arming or refreshing.
|
|
36
34
|
*
|
|
37
35
|
* @param ctx - Migration context
|
|
38
36
|
* @returns True when one supported caller needs a safe literal update
|
|
39
37
|
*/
|
|
40
38
|
applies(ctx: MigrationContext): Promise<boolean>;
|
|
41
39
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* A host that deliberately uses a branch or different workflow path is left
|
|
45
|
-
* untouched because Lisa cannot infer that host's release policy. A caller
|
|
46
|
-
* already pinned at a bare SHA is rewritten to the tag: that pin is the
|
|
47
|
-
* defect, and rewriting it is the only repair a consumer gets, because this
|
|
48
|
-
* runs from postinstall and would otherwise re-stamp the SHA over any
|
|
49
|
-
* hand repin.
|
|
40
|
+
* Arm the bypass gate and refresh the caller's explanatory version comments.
|
|
50
41
|
*
|
|
51
42
|
* @param ctx - Migration context
|
|
52
43
|
* @returns Applied or no-op result
|
|
53
44
|
*/
|
|
54
45
|
apply(ctx: MigrationContext): Promise<MigrationResult>;
|
|
55
|
-
/**
|
|
56
|
-
* Resolve the one ref this migration is allowed to pin a caller at.
|
|
57
|
-
*
|
|
58
|
-
* A stamped value is honoured only when it is a release tag. The published
|
|
59
|
-
* package used to stamp its build commit here, and that commit is exactly
|
|
60
|
-
* what a history rewrite orphans, so a value of any other shape — a bare
|
|
61
|
-
* SHA above all — is refused in favour of the installed version's tag.
|
|
62
|
-
*
|
|
63
|
-
* @param lisaVersion - Installed Lisa version
|
|
64
|
-
* @returns Release tag ref to pin every supported caller at
|
|
65
|
-
*/
|
|
66
|
-
private resolveReleaseRef;
|
|
67
46
|
/**
|
|
68
47
|
* Read the contract version shipped into the destination project.
|
|
69
48
|
*
|
|
@@ -72,12 +51,13 @@ export declare class EnsureNightlyE2EWorkflowPinsMigration implements Migration
|
|
|
72
51
|
*/
|
|
73
52
|
private readContractVersion;
|
|
74
53
|
/**
|
|
75
|
-
*
|
|
54
|
+
* Arm the bypass gate and refresh the caller's explanatory version comments.
|
|
55
|
+
*
|
|
56
|
+
* The `uses:` ref is deliberately not touched here — see the class note.
|
|
76
57
|
*
|
|
77
58
|
* @param source - Workflow source
|
|
78
59
|
* @param file - Supported workflow filename
|
|
79
60
|
* @param lisaVersion - Installed Lisa version
|
|
80
|
-
* @param releaseRef - Release tag ref every supported caller is pinned at
|
|
81
61
|
* @param contractVersion - Guard contract version, when available
|
|
82
62
|
* @returns Updated or original workflow source
|
|
83
63
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ensure-nightly-e2e-workflow-pins.d.ts","sourceRoot":"","sources":["../../src/migrations/ensure-nightly-e2e-workflow-pins.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,SAAS,EACT,gBAAgB,EAChB,eAAe,EAChB,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"ensure-nightly-e2e-workflow-pins.d.ts","sourceRoot":"","sources":["../../src/migrations/ensure-nightly-e2e-workflow-pins.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,SAAS,EACT,gBAAgB,EAChB,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAWlC,+CAA+C;AAC/C,KAAK,aAAa,GAAG,MAAM,MAAM,CAAC;AAElC;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,qCAAsC,YAAW,SAAS;IAcnE,OAAO,CAAC,QAAQ,CAAC,eAAe;IAVlC,QAAQ,CAAC,IAAI,sCAAsC;IACnD,QAAQ,CAAC,WAAW,4GACuF;IAE3G;;;;OAIG;gBAEgB,eAAe,GAAE,aAAiC;IAGrE;;;;;OAKG;IACG,OAAO,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAkBtD;;;;;OAKG;IACG,KAAK,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC;IAuC5D;;;;;OAKG;YACW,mBAAmB;IAQjC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,YAAY;CAuBrB"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import * as fse from "fs-extra";
|
|
4
|
-
import {
|
|
4
|
+
import { getPackageVersion } from "../cli/version.js";
|
|
5
5
|
import { ensureBodyChangeTrigger } from "../core/nightly-e2e-pull-request-triggers.js";
|
|
6
6
|
const WORKFLOW_DIR = path.join(".github", "workflows");
|
|
7
7
|
const WORKFLOW_FILES = [
|
|
@@ -11,27 +11,17 @@ const WORKFLOW_FILES = [
|
|
|
11
11
|
const CONTRACT_SCRIPT = path.join("scripts", "check-nightly-e2e-health.mjs");
|
|
12
12
|
const CONTRACT_PATTERN = /NIGHTLY_E2E_CONTRACT_VERSION\s*=\s*["'](\d+\.\d+\.\d+)["']/;
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
14
|
+
* Keep an installed nightly E2E caller's bypass gate armed against
|
|
15
|
+
* body-evidence deletion, and its explanatory version comments current.
|
|
15
16
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* Anything that does not match this pattern is discarded in favour of the
|
|
22
|
-
* installed version's tag.
|
|
23
|
-
*/
|
|
24
|
-
const RELEASE_TAG_PATTERN = /^v\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/;
|
|
25
|
-
/**
|
|
26
|
-
* Keep an installed nightly E2E caller pinned at the installed Lisa release tag,
|
|
27
|
-
* and keep its bypass gate armed against body-evidence deletion.
|
|
28
|
-
*
|
|
29
|
-
* The tag, never the release commit: the commit the package was built from is
|
|
30
|
-
* not guaranteed to survive, and a caller pinned at an orphaned commit stops
|
|
31
|
-
* loading silently rather than failing.
|
|
17
|
+
* This used to rewrite the caller's `uses:` ref as well. It no longer does:
|
|
18
|
+
* `ensure-pinned-reusable-workflow-refs` pins EVERY Lisa reusable caller,
|
|
19
|
+
* these two included, at the commit the installed version's tag names. Two
|
|
20
|
+
* migrations rewriting the same line would each undo the other on every apply
|
|
21
|
+
* — content-stable, endlessly "applied", and pinned at whichever ran last.
|
|
32
22
|
*
|
|
33
|
-
* The
|
|
34
|
-
* lives. The caller ships from `create-only` and is marked "this file is YOURS
|
|
23
|
+
* The remaining job is here rather than in the template because of where the
|
|
24
|
+
* file lives. The caller ships from `create-only` and is marked "this file is YOURS
|
|
35
25
|
* — Lisa will not overwrite it", so a template fix reaches new adoptions only
|
|
36
26
|
* and every already-seeded repository keeps its original trigger list forever.
|
|
37
27
|
* The reusable workflow cannot carry the fix either: it is `on: workflow_call`,
|
|
@@ -40,21 +30,21 @@ const RELEASE_TAG_PATTERN = /^v\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/;
|
|
|
40
30
|
*/
|
|
41
31
|
export class EnsureNightlyE2EWorkflowPinsMigration {
|
|
42
32
|
readLisaVersion;
|
|
43
|
-
|
|
33
|
+
// The name outlived the pin arm. It is kept because it is the identity the
|
|
34
|
+
// migration registry, its tests, and any apply receipt already record — a
|
|
35
|
+
// rename would be a bigger change than the one that emptied it of meaning.
|
|
44
36
|
name = "ensure-nightly-e2e-workflow-pins";
|
|
45
|
-
description = "
|
|
37
|
+
description = "Keep the nightly E2E bypass gate armed against body-evidence deletion and its version comments current";
|
|
46
38
|
/**
|
|
47
39
|
* Create the migration.
|
|
48
40
|
*
|
|
49
41
|
* @param readLisaVersion - Version reader, injectable for deterministic tests
|
|
50
|
-
* @param readReleaseTag - Published release tag reader
|
|
51
42
|
*/
|
|
52
|
-
constructor(readLisaVersion = getPackageVersion
|
|
43
|
+
constructor(readLisaVersion = getPackageVersion) {
|
|
53
44
|
this.readLisaVersion = readLisaVersion;
|
|
54
|
-
this.readReleaseTag = readReleaseTag;
|
|
55
45
|
}
|
|
56
46
|
/**
|
|
57
|
-
* Decide whether an Expo caller still
|
|
47
|
+
* Decide whether an installed Expo caller still needs arming or refreshing.
|
|
58
48
|
*
|
|
59
49
|
* @param ctx - Migration context
|
|
60
50
|
* @returns True when one supported caller needs a safe literal update
|
|
@@ -64,34 +54,25 @@ export class EnsureNightlyE2EWorkflowPinsMigration {
|
|
|
64
54
|
return false;
|
|
65
55
|
const contractVersion = await this.readContractVersion(ctx.projectDir);
|
|
66
56
|
const lisaVersion = this.readLisaVersion();
|
|
67
|
-
const releaseRef = this.resolveReleaseRef(lisaVersion);
|
|
68
57
|
for (const file of WORKFLOW_FILES) {
|
|
69
58
|
const absolute = path.join(ctx.projectDir, WORKFLOW_DIR, file);
|
|
70
59
|
if (!(await fse.pathExists(absolute)))
|
|
71
60
|
continue;
|
|
72
61
|
const source = await readFile(absolute, "utf8");
|
|
73
|
-
if (this.updateSource(source, file, lisaVersion,
|
|
62
|
+
if (this.updateSource(source, file, lisaVersion, contractVersion) !== source) {
|
|
74
63
|
return true;
|
|
75
64
|
}
|
|
76
65
|
}
|
|
77
66
|
return false;
|
|
78
67
|
}
|
|
79
68
|
/**
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* A host that deliberately uses a branch or different workflow path is left
|
|
83
|
-
* untouched because Lisa cannot infer that host's release policy. A caller
|
|
84
|
-
* already pinned at a bare SHA is rewritten to the tag: that pin is the
|
|
85
|
-
* defect, and rewriting it is the only repair a consumer gets, because this
|
|
86
|
-
* runs from postinstall and would otherwise re-stamp the SHA over any
|
|
87
|
-
* hand repin.
|
|
69
|
+
* Arm the bypass gate and refresh the caller's explanatory version comments.
|
|
88
70
|
*
|
|
89
71
|
* @param ctx - Migration context
|
|
90
72
|
* @returns Applied or no-op result
|
|
91
73
|
*/
|
|
92
74
|
async apply(ctx) {
|
|
93
75
|
const lisaVersion = this.readLisaVersion();
|
|
94
|
-
const releaseRef = this.resolveReleaseRef(lisaVersion);
|
|
95
76
|
const contractVersion = await this.readContractVersion(ctx.projectDir);
|
|
96
77
|
const changedFiles = [];
|
|
97
78
|
const updates = [];
|
|
@@ -101,7 +82,7 @@ export class EnsureNightlyE2EWorkflowPinsMigration {
|
|
|
101
82
|
if (!(await fse.pathExists(absolute)))
|
|
102
83
|
continue;
|
|
103
84
|
const before = await readFile(absolute, "utf8");
|
|
104
|
-
const after = this.updateSource(before, file, lisaVersion,
|
|
85
|
+
const after = this.updateSource(before, file, lisaVersion, contractVersion);
|
|
105
86
|
if (after === before)
|
|
106
87
|
continue;
|
|
107
88
|
changedFiles.push(relative);
|
|
@@ -121,23 +102,6 @@ export class EnsureNightlyE2EWorkflowPinsMigration {
|
|
|
121
102
|
ctx.logger.success(message);
|
|
122
103
|
return { name: this.name, action: "applied", changedFiles, message };
|
|
123
104
|
}
|
|
124
|
-
/**
|
|
125
|
-
* Resolve the one ref this migration is allowed to pin a caller at.
|
|
126
|
-
*
|
|
127
|
-
* A stamped value is honoured only when it is a release tag. The published
|
|
128
|
-
* package used to stamp its build commit here, and that commit is exactly
|
|
129
|
-
* what a history rewrite orphans, so a value of any other shape — a bare
|
|
130
|
-
* SHA above all — is refused in favour of the installed version's tag.
|
|
131
|
-
*
|
|
132
|
-
* @param lisaVersion - Installed Lisa version
|
|
133
|
-
* @returns Release tag ref to pin every supported caller at
|
|
134
|
-
*/
|
|
135
|
-
resolveReleaseRef(lisaVersion) {
|
|
136
|
-
const stamped = this.readReleaseTag();
|
|
137
|
-
return stamped !== null && RELEASE_TAG_PATTERN.test(stamped)
|
|
138
|
-
? stamped
|
|
139
|
-
: `v${lisaVersion}`;
|
|
140
|
-
}
|
|
141
105
|
/**
|
|
142
106
|
* Read the contract version shipped into the destination project.
|
|
143
107
|
*
|
|
@@ -151,34 +115,27 @@ export class EnsureNightlyE2EWorkflowPinsMigration {
|
|
|
151
115
|
return CONTRACT_PATTERN.exec(await readFile(file, "utf8"))?.[1] ?? null;
|
|
152
116
|
}
|
|
153
117
|
/**
|
|
154
|
-
*
|
|
118
|
+
* Arm the bypass gate and refresh the caller's explanatory version comments.
|
|
119
|
+
*
|
|
120
|
+
* The `uses:` ref is deliberately not touched here — see the class note.
|
|
155
121
|
*
|
|
156
122
|
* @param source - Workflow source
|
|
157
123
|
* @param file - Supported workflow filename
|
|
158
124
|
* @param lisaVersion - Installed Lisa version
|
|
159
|
-
* @param releaseRef - Release tag ref every supported caller is pinned at
|
|
160
125
|
* @param contractVersion - Guard contract version, when available
|
|
161
126
|
* @returns Updated or original workflow source
|
|
162
127
|
*/
|
|
163
|
-
updateSource(source, file, lisaVersion,
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (!pinChanged)
|
|
175
|
-
return armed;
|
|
176
|
-
const commentUpdated = armed.replace(/# v\d+\.\d+\.\d+ matches this repo's own installed Lisa/, `# v${lisaVersion} matches this repo's own installed Lisa`);
|
|
177
|
-
return contractVersion
|
|
178
|
-
? commentUpdated.replace(/# the guard reports contract \d+\.\d+\.\d+ and the reusable asserts its MAJOR/, `# the guard reports contract ${contractVersion} and the reusable asserts its MAJOR`)
|
|
179
|
-
: commentUpdated;
|
|
180
|
-
}
|
|
181
|
-
return pinUpdated;
|
|
128
|
+
updateSource(source, file, lisaVersion, contractVersion) {
|
|
129
|
+
if (file !== "nightly-e2e-health.yml")
|
|
130
|
+
return source;
|
|
131
|
+
// Armed BEFORE the comment edits, and unconditionally. A consumer whose
|
|
132
|
+
// comments are already current has nothing to refresh and would otherwise
|
|
133
|
+
// never receive this, which is the entire installed base a month from now.
|
|
134
|
+
const armed = ensureBodyChangeTrigger(source);
|
|
135
|
+
const commentUpdated = armed.replace(/# v\d+\.\d+\.\d+ matches this repo's own installed Lisa/, `# v${lisaVersion} matches this repo's own installed Lisa`);
|
|
136
|
+
return contractVersion
|
|
137
|
+
? commentUpdated.replace(/# the guard reports contract \d+\.\d+\.\d+ and the reusable asserts its MAJOR/, `# the guard reports contract ${contractVersion} and the reusable asserts its MAJOR`)
|
|
138
|
+
: commentUpdated;
|
|
182
139
|
}
|
|
183
140
|
}
|
|
184
141
|
//# sourceMappingURL=ensure-nightly-e2e-workflow-pins.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ensure-nightly-e2e-workflow-pins.js","sourceRoot":"","sources":["../../src/migrations/ensure-nightly-e2e-workflow-pins.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"ensure-nightly-e2e-workflow-pins.js","sourceRoot":"","sources":["../../src/migrations/ensure-nightly-e2e-workflow-pins.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import type { ReleasePinDependencies } from "../core/lisa-release-pin.js";
|
|
2
|
+
import type { Migration, MigrationContext, MigrationResult } from "./migration.interface.js";
|
|
3
|
+
/**
|
|
4
|
+
* Pin every Lisa reusable-workflow caller in a project at the commit the
|
|
5
|
+
* installed version's tag names, and keep it pinned as that version moves.
|
|
6
|
+
*
|
|
7
|
+
* ## Why this is a migration and not a template change
|
|
8
|
+
*
|
|
9
|
+
* A template cannot carry the answer. The SHA a caller must name is the commit
|
|
10
|
+
* of a tag that does not exist yet when the template is authored, so the
|
|
11
|
+
* shipped templates track `@main` and this migration rewrites what was copied.
|
|
12
|
+
* That also makes one mechanism cover three cases that would otherwise need
|
|
13
|
+
* three: a fresh install (templates land, then get pinned), an already
|
|
14
|
+
* installed project still on `@main` (rewritten on its next apply — "fixed
|
|
15
|
+
* upstream" is not "a bump brings it", and every caller workflow is
|
|
16
|
+
* create-only, so nothing else would ever reach them), and a version change
|
|
17
|
+
* (repinned in the same pass that rewrites the package pin).
|
|
18
|
+
*
|
|
19
|
+
* ## Why it resolves before any file is written
|
|
20
|
+
*
|
|
21
|
+
* The abort has to leave the working tree untouched, and by the time
|
|
22
|
+
* migrations run the copy strategies have already written. So the resolution
|
|
23
|
+
* happens in `beforeStrategies`, which runs first: an installed Lisa that
|
|
24
|
+
* cannot name its own release commit stops the apply there, before anything
|
|
25
|
+
* has changed.
|
|
26
|
+
*
|
|
27
|
+
* ## What it refuses to do, and what it only reports
|
|
28
|
+
*
|
|
29
|
+
* A Lisa whose DECLARED release identity will not resolve is broken, and it
|
|
30
|
+
* stops the apply wherever a caller is involved — installed in the project, or
|
|
31
|
+
* about to arrive from the templates.
|
|
32
|
+
*
|
|
33
|
+
* A Lisa that declares NO release identity was never released: a working
|
|
34
|
+
* checkout, or a template tree copied somewhere without its history. There is
|
|
35
|
+
* no tag for a caller to name, so the apply continues and says so — refusing
|
|
36
|
+
* would make every developer checkout unable to apply Lisa at all. The one
|
|
37
|
+
* exception is a project that ALREADY calls a Lisa reusable workflow: leaving
|
|
38
|
+
* an existing caller mutable while reporting a successful apply is the
|
|
39
|
+
* fail-open shape this migration exists to remove, so that case still stops.
|
|
40
|
+
*
|
|
41
|
+
* ## Why it declines nothing in postinstall-safe mode
|
|
42
|
+
*
|
|
43
|
+
* The version change IS the postinstall moment. A pin that only moved when an
|
|
44
|
+
* operator happened to run a full apply would spend most of its life naming a
|
|
45
|
+
* release the project no longer has installed, which is the staleness this
|
|
46
|
+
* whole change exists to prevent.
|
|
47
|
+
*/
|
|
48
|
+
export declare class EnsurePinnedReusableWorkflowRefsMigration implements Migration {
|
|
49
|
+
private readonly deps;
|
|
50
|
+
readonly name = "ensure-pinned-reusable-workflow-refs";
|
|
51
|
+
readonly description = "Pin every Lisa reusable-workflow caller at the commit the installed version's tag names";
|
|
52
|
+
/** Resolution captured before the strategies ran, when they ran at all. */
|
|
53
|
+
private resolution;
|
|
54
|
+
/**
|
|
55
|
+
* Whether the project already called a Lisa reusable workflow when this
|
|
56
|
+
* apply started, or null when nobody looked before the strategies ran.
|
|
57
|
+
*
|
|
58
|
+
* Asked before the copy, because afterwards the answer is always yes: the
|
|
59
|
+
* templates have just seeded callers, and "was already installed" and "was
|
|
60
|
+
* seeded thirty seconds ago" are the two cases that need opposite answers
|
|
61
|
+
* from an unreleased Lisa.
|
|
62
|
+
*/
|
|
63
|
+
private hadCallersBefore;
|
|
64
|
+
/**
|
|
65
|
+
* Create the migration.
|
|
66
|
+
* @param deps - Pin resolution readers, injectable for deterministic tests
|
|
67
|
+
*/
|
|
68
|
+
constructor(deps?: ReleasePinDependencies);
|
|
69
|
+
/**
|
|
70
|
+
* Resolve the pin before any strategy writes, and abort here if it cannot be.
|
|
71
|
+
* @param ctx - Migration context
|
|
72
|
+
* @throws {UnresolvableReleasePinError} When a caller is involved and the pin will not resolve
|
|
73
|
+
*/
|
|
74
|
+
beforeStrategies(ctx: MigrationContext): Promise<void>;
|
|
75
|
+
/**
|
|
76
|
+
* Whether any caller in this project still needs rewriting.
|
|
77
|
+
*
|
|
78
|
+
* The caller check comes FIRST, and deliberately. A project with no Lisa
|
|
79
|
+
* reusable callers has nothing to pin, so an installation that cannot name
|
|
80
|
+
* its release commit is not a problem it has — declining here is what keeps
|
|
81
|
+
* an unreleased checkout able to apply Lisa to such a project at all. Once a
|
|
82
|
+
* caller does exist, an unresolved pin returns true so `apply` can abort
|
|
83
|
+
* loudly rather than reporting a skip that reads like conformance.
|
|
84
|
+
* @param ctx - Migration context
|
|
85
|
+
* @returns True when at least one reference is not already at the pin
|
|
86
|
+
*/
|
|
87
|
+
applies(ctx: MigrationContext): Promise<boolean>;
|
|
88
|
+
/**
|
|
89
|
+
* Rewrite every caller to the pin, or abort having written nothing.
|
|
90
|
+
* @param ctx - Migration context
|
|
91
|
+
* @returns Applied or no-op result
|
|
92
|
+
* @throws {UnresolvableReleasePinError} When the installed version's tag resolves to no commit
|
|
93
|
+
*/
|
|
94
|
+
apply(ctx: MigrationContext): Promise<MigrationResult>;
|
|
95
|
+
/**
|
|
96
|
+
* Whether an unresolved pin must stop this apply rather than be reported.
|
|
97
|
+
*
|
|
98
|
+
* A DECLARED identity that will not resolve is a broken installation and
|
|
99
|
+
* stops wherever a caller is involved. No declared identity at all is an
|
|
100
|
+
* unreleased tree, which has no tag for a caller to name — it stops only
|
|
101
|
+
* where continuing would leave a caller that was ALREADY there mutable
|
|
102
|
+
* while the apply reported success.
|
|
103
|
+
* @param ctx - Migration context
|
|
104
|
+
* @param error - The resolution failure
|
|
105
|
+
* @returns True when the apply must abort
|
|
106
|
+
*/
|
|
107
|
+
private isFatal;
|
|
108
|
+
/**
|
|
109
|
+
* The rewrite to perform on every workflow file that needs one.
|
|
110
|
+
* @param projectDir - Destination project directory
|
|
111
|
+
* @param pin - The identity every caller must carry
|
|
112
|
+
* @returns One entry per file whose content changes
|
|
113
|
+
*/
|
|
114
|
+
private plan;
|
|
115
|
+
/**
|
|
116
|
+
* The resolution captured in `beforeStrategies`, resolving now if it was not.
|
|
117
|
+
*
|
|
118
|
+
* `runAll` can be invoked without the pre-strategy hook — a caller that only
|
|
119
|
+
* wants migrations, and every unit test of this class. Resolving lazily there
|
|
120
|
+
* keeps both paths honest rather than silently treating "never asked" as "no
|
|
121
|
+
* pin needed".
|
|
122
|
+
* @param ctx - Migration context
|
|
123
|
+
* @returns The resolution
|
|
124
|
+
*/
|
|
125
|
+
private resolved;
|
|
126
|
+
/**
|
|
127
|
+
* Attempt resolution, capturing the failure rather than throwing it.
|
|
128
|
+
* @param ctx - Migration context
|
|
129
|
+
* @returns The pin, or the error explaining why there is none
|
|
130
|
+
*/
|
|
131
|
+
private resolve;
|
|
132
|
+
/**
|
|
133
|
+
* Whether this apply could put a Lisa reusable-workflow caller in the project.
|
|
134
|
+
*
|
|
135
|
+
* Both halves matter. The project's own workflows cover an existing install;
|
|
136
|
+
* the templates about to be copied cover a fresh one, where the project has
|
|
137
|
+
* no workflows yet and the callers arrive minutes later.
|
|
138
|
+
* @param ctx - Migration context
|
|
139
|
+
* @returns True when a caller exists or is about to
|
|
140
|
+
*/
|
|
141
|
+
private anyCallerReachable;
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=ensure-pinned-reusable-workflow-refs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ensure-pinned-reusable-workflow-refs.d.ts","sourceRoot":"","sources":["../../src/migrations/ensure-pinned-reusable-workflow-refs.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAY1E,OAAO,KAAK,EACV,SAAS,EACT,gBAAgB,EAChB,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAelC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,qBAAa,yCAA0C,YAAW,SAAS;IAwBvE,OAAO,CAAC,QAAQ,CAAC,IAAI;IAvBvB,QAAQ,CAAC,IAAI,0CAA0C;IACvD,QAAQ,CAAC,WAAW,6FACwE;IAE5F,2EAA2E;IAC3E,OAAO,CAAC,UAAU,CAA2B;IAE7C;;;;;;;;OAQG;IACH,OAAO,CAAC,gBAAgB,CAAwB;IAEhD;;;OAGG;gBAEgB,IAAI,GAAE,sBAKtB;IAGH;;;;OAIG;IACG,gBAAgB,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB5D;;;;;;;;;;;OAWG;IACG,OAAO,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;IAUtD;;;;;OAKG;IACG,KAAK,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC;IAkC5D;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,OAAO;IAYf;;;;;OAKG;YACW,IAAI;IAuBlB;;;;;;;;;OASG;YACW,QAAQ;IAKtB;;;;OAIG;YACW,OAAO;IAWrB;;;;;;;;OAQG;YACW,kBAAkB;CAYjC"}
|