@mannyc1/ts-release 0.0.0 → 0.0.2
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/ARCHITECTURE.md +53 -0
- package/README.md +243 -43
- package/SPEC.md +8 -1
- package/dist/cli/command.d.ts +1 -1
- package/dist/cli/command.d.ts.map +1 -1
- package/dist/cli/command.js +116 -33
- package/dist/cli/command.js.map +1 -1
- package/dist/cli/main.js +3 -6
- package/dist/cli/main.js.map +1 -1
- package/dist/domain/evidence.d.ts +49 -3
- package/dist/domain/evidence.d.ts.map +1 -1
- package/dist/domain/evidence.js +43 -2
- package/dist/domain/evidence.js.map +1 -1
- package/dist/domain/operation.d.ts +56 -2
- package/dist/domain/operation.d.ts.map +1 -1
- package/dist/domain/operation.js +78 -2
- package/dist/domain/operation.js.map +1 -1
- package/dist/domain/release.d.ts +4 -4
- package/dist/domain/remote-state.d.ts +90 -0
- package/dist/domain/remote-state.d.ts.map +1 -0
- package/dist/domain/remote-state.js +85 -0
- package/dist/domain/remote-state.js.map +1 -0
- package/dist/domain/status.d.ts +43 -0
- package/dist/domain/status.d.ts.map +1 -0
- package/dist/domain/status.js +50 -0
- package/dist/domain/status.js.map +1 -0
- package/dist/domain/target.d.ts +63 -3
- package/dist/domain/target.d.ts.map +1 -1
- package/dist/domain/target.js +75 -5
- package/dist/domain/target.js.map +1 -1
- package/dist/host/host.d.ts +7 -23
- package/dist/host/host.d.ts.map +1 -1
- package/dist/host/host.js +3 -22
- package/dist/host/host.js.map +1 -1
- package/dist/host/http-live.d.ts +6 -0
- package/dist/host/http-live.d.ts.map +1 -0
- package/dist/host/http-live.js +82 -0
- package/dist/host/http-live.js.map +1 -0
- package/dist/host/http.d.ts +40 -0
- package/dist/host/http.d.ts.map +1 -0
- package/dist/host/http.js +56 -0
- package/dist/host/http.js.map +1 -0
- package/dist/host/platform.d.ts +10 -0
- package/dist/host/platform.d.ts.map +1 -0
- package/dist/host/platform.js +121 -0
- package/dist/host/platform.js.map +1 -0
- package/dist/host/test.d.ts +6 -3
- package/dist/host/test.d.ts.map +1 -1
- package/dist/host/test.js +111 -25
- package/dist/host/test.js.map +1 -1
- package/dist/planner/artifact-inventory.d.ts +9 -0
- package/dist/planner/artifact-inventory.d.ts.map +1 -0
- package/dist/planner/artifact-inventory.js +66 -0
- package/dist/planner/artifact-inventory.js.map +1 -0
- package/dist/planner/create-release-plan.d.ts +1 -1
- package/dist/planner/create-release-plan.d.ts.map +1 -1
- package/dist/planner/errors.d.ts +41 -3
- package/dist/planner/errors.d.ts.map +1 -1
- package/dist/planner/errors.js +35 -3
- package/dist/planner/errors.js.map +1 -1
- package/dist/planner/evidence-recorder.d.ts +16 -8
- package/dist/planner/evidence-recorder.d.ts.map +1 -1
- package/dist/planner/evidence-recorder.js +244 -17
- package/dist/planner/evidence-recorder.js.map +1 -1
- package/dist/planner/executor.d.ts +54 -15
- package/dist/planner/executor.d.ts.map +1 -1
- package/dist/planner/executor.js +117 -38
- package/dist/planner/executor.js.map +1 -1
- package/dist/planner/normalize-release.d.ts +2 -2
- package/dist/planner/normalize-release.d.ts.map +1 -1
- package/dist/planner/normalize-release.js +80 -42
- package/dist/planner/normalize-release.js.map +1 -1
- package/dist/planner/reconcile.d.ts +17 -0
- package/dist/planner/reconcile.d.ts.map +1 -0
- package/dist/planner/reconcile.js +280 -0
- package/dist/planner/reconcile.js.map +1 -0
- package/dist/planner/release-eligibility.d.ts +33 -0
- package/dist/planner/release-eligibility.d.ts.map +1 -0
- package/dist/planner/release-eligibility.js +172 -0
- package/dist/planner/release-eligibility.js.map +1 -0
- package/dist/planner/render-plan.d.ts.map +1 -1
- package/dist/planner/render-plan.js +30 -0
- package/dist/planner/render-plan.js.map +1 -1
- package/dist/planner/status.d.ts +40 -0
- package/dist/planner/status.d.ts.map +1 -0
- package/dist/planner/status.js +336 -0
- package/dist/planner/status.js.map +1 -0
- package/dist/runtime/bun.d.ts +9 -0
- package/dist/runtime/bun.d.ts.map +1 -0
- package/dist/runtime/bun.js +10 -0
- package/dist/runtime/bun.js.map +1 -0
- package/dist/targets/adapter-helpers.d.ts +31 -0
- package/dist/targets/adapter-helpers.d.ts.map +1 -0
- package/dist/targets/adapter-helpers.js +67 -0
- package/dist/targets/adapter-helpers.js.map +1 -0
- package/dist/targets/adapter.d.ts +3 -1
- package/dist/targets/adapter.d.ts.map +1 -1
- package/dist/targets/github.d.ts +2 -2
- package/dist/targets/github.d.ts.map +1 -1
- package/dist/targets/github.js +74 -83
- package/dist/targets/github.js.map +1 -1
- package/dist/targets/homebrew.d.ts +2 -2
- package/dist/targets/homebrew.d.ts.map +1 -1
- package/dist/targets/homebrew.js +45 -75
- package/dist/targets/homebrew.js.map +1 -1
- package/dist/targets/live.d.ts.map +1 -1
- package/dist/targets/live.js +10 -0
- package/dist/targets/live.js.map +1 -1
- package/dist/targets/npm.d.ts +2 -3
- package/dist/targets/npm.d.ts.map +1 -1
- package/dist/targets/npm.js +90 -55
- package/dist/targets/npm.js.map +1 -1
- package/dist/targets/pypi.d.ts +11 -0
- package/dist/targets/pypi.d.ts.map +1 -0
- package/dist/targets/pypi.js +115 -0
- package/dist/targets/pypi.js.map +1 -0
- package/dist/targets/registry.d.ts +3 -3
- package/dist/targets/registry.d.ts.map +1 -1
- package/dist/targets/scoop.d.ts +11 -0
- package/dist/targets/scoop.d.ts.map +1 -0
- package/dist/targets/scoop.js +93 -0
- package/dist/targets/scoop.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/dist/workflows/config.d.ts +81 -0
- package/dist/workflows/config.d.ts.map +1 -0
- package/dist/workflows/config.js +191 -0
- package/dist/workflows/config.js.map +1 -0
- package/dist/workflows/evidence.d.ts +21 -0
- package/dist/workflows/evidence.d.ts.map +1 -0
- package/dist/workflows/evidence.js +45 -0
- package/dist/workflows/evidence.js.map +1 -0
- package/dist/workflows/live.d.ts +7 -0
- package/dist/workflows/live.d.ts.map +1 -0
- package/dist/workflows/live.js +5 -0
- package/dist/workflows/live.js.map +1 -0
- package/examples/README.md +20 -3
- package/examples/github-release/release.config.json +2 -1
- package/examples/multi-target/artifacts/release-example-multi-target-0.1.0.tgz +1 -0
- package/examples/multi-target/index.js +1 -0
- package/examples/multi-target/package.json +7 -0
- package/examples/multi-target/release.config.json +67 -0
- package/examples/non-strict-skips/release.config.json +1 -0
- package/examples/npm-first-publish/release.config.json +32 -0
- package/examples/npm-only/release.config.json +6 -1
- package/examples/pypi-registry/artifacts/release_example_pypi-0.1.0-py3-none-any.whl +1 -0
- package/examples/pypi-registry/release.config.json +30 -0
- package/examples/scoop-bucket/artifacts/release-example-scoop-0.1.0.zip +1 -0
- package/examples/scoop-bucket/release.config.json +36 -0
- package/package.json +48 -9
- package/dist/cli/programmatic.d.ts +0 -25
- package/dist/cli/programmatic.d.ts.map +0 -1
- package/dist/cli/programmatic.js +0 -48
- package/dist/cli/programmatic.js.map +0 -1
- package/dist/host/bun.d.ts +0 -9
- package/dist/host/bun.d.ts.map +0 -1
- package/dist/host/bun.js +0 -156
- package/dist/host/bun.js.map +0 -1
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
3
|
+
import { CommandSpec } from "../domain/operation.js";
|
|
4
|
+
import { ReleaseEligibilityDecision, ReleaseEligibilityInput } from "../domain/remote-state.js";
|
|
5
|
+
import { ReleaseCommandRunner } from "../host/host.js";
|
|
6
|
+
import { ReleaseEligibilityCheckError } from "./errors.js";
|
|
7
|
+
export class ReleasePackageManifest extends Schema.Class("ReleasePackageManifest")({
|
|
8
|
+
name: Schema.String,
|
|
9
|
+
version: Schema.String
|
|
10
|
+
}) {
|
|
11
|
+
}
|
|
12
|
+
export class ReleaseEligibilityRemoteCheck extends Schema.Class("ReleaseEligibilityRemoteCheck")({
|
|
13
|
+
packageName: Schema.String,
|
|
14
|
+
packageVersion: Schema.String,
|
|
15
|
+
npmTargetId: Schema.String,
|
|
16
|
+
npmRegistry: Schema.String,
|
|
17
|
+
githubTargetId: Schema.String,
|
|
18
|
+
githubRepository: Schema.String,
|
|
19
|
+
githubTokenEnv: Schema.optionalKey(Schema.String),
|
|
20
|
+
expectedGithubDraft: Schema.Boolean
|
|
21
|
+
}) {
|
|
22
|
+
}
|
|
23
|
+
class GitHubCliReleaseViewResponse extends Schema.Class("GitHubCliReleaseViewResponse")({
|
|
24
|
+
isDraft: Schema.Boolean
|
|
25
|
+
}) {
|
|
26
|
+
}
|
|
27
|
+
const decodeGitHubCliReleaseViewResponse = Schema.decodeUnknownEffect(GitHubCliReleaseViewResponse);
|
|
28
|
+
const releaseLabel = (input) => `${input.packageName}@${input.packageVersion}`;
|
|
29
|
+
const expectedGithubState = (input) => input.expectedGithubDraft ? "draft" : "published";
|
|
30
|
+
export const decideReleaseEligibility = (input) => {
|
|
31
|
+
if (input.npm === "missing" && input.github === "missing") {
|
|
32
|
+
return ReleaseEligibilityDecision.make({
|
|
33
|
+
shouldRelease: true,
|
|
34
|
+
status: "ready",
|
|
35
|
+
reason: `${releaseLabel(input)} is missing from npm and GitHub.`
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
const expectedGithub = expectedGithubState(input);
|
|
39
|
+
if (input.npm === "published" && input.github === expectedGithub) {
|
|
40
|
+
return ReleaseEligibilityDecision.make({
|
|
41
|
+
shouldRelease: false,
|
|
42
|
+
status: "complete",
|
|
43
|
+
reason: `${releaseLabel(input)} is already published to npm and GitHub.`
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
if (input.npm === "published" && input.github === "draft" && !input.expectedGithubDraft) {
|
|
47
|
+
return ReleaseEligibilityDecision.make({
|
|
48
|
+
shouldRelease: false,
|
|
49
|
+
status: "partial",
|
|
50
|
+
reason: `${releaseLabel(input)} is published to npm, but GitHub release v${input.packageVersion} is still a draft.`
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (input.npm === "published" && input.github === "published" && input.expectedGithubDraft) {
|
|
54
|
+
return ReleaseEligibilityDecision.make({
|
|
55
|
+
shouldRelease: false,
|
|
56
|
+
status: "partial",
|
|
57
|
+
reason: `${releaseLabel(input)} is published to npm, but GitHub release v${input.packageVersion} is public while the target expects a draft.`
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return ReleaseEligibilityDecision.make({
|
|
61
|
+
shouldRelease: false,
|
|
62
|
+
status: "partial",
|
|
63
|
+
reason: `${releaseLabel(input)} has partial remote state: npm=${input.npm}, github=${input.github}, expected-github=${expectedGithub}.`
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
const eligibilityError = (reason, targetId) => ReleaseEligibilityCheckError.make({
|
|
67
|
+
...(targetId === undefined ? {} : { targetId }),
|
|
68
|
+
reason
|
|
69
|
+
});
|
|
70
|
+
const findNpmTarget = (intent, packageName) => intent.targets.find((target) => target._tag === "NpmRegistryTarget" && (target.packageName === packageName || target.id === "npm"));
|
|
71
|
+
const findGitHubTarget = (intent) => intent.targets.find((target) => target._tag === "GitHubReleaseTarget");
|
|
72
|
+
export const releaseEligibilityRemoteCheckFromIntent = Effect.fn("releaseEligibilityRemoteCheckFromIntent")(function* (manifest, intent) {
|
|
73
|
+
const npmTarget = findNpmTarget(intent, manifest.name);
|
|
74
|
+
if (npmTarget === undefined) {
|
|
75
|
+
return yield* Effect.fail(eligibilityError(`release config must include an npm target for ${manifest.name}`));
|
|
76
|
+
}
|
|
77
|
+
const githubTarget = findGitHubTarget(intent);
|
|
78
|
+
if (githubTarget === undefined) {
|
|
79
|
+
return yield* Effect.fail(eligibilityError("release config must include a GitHub release target"));
|
|
80
|
+
}
|
|
81
|
+
return ReleaseEligibilityRemoteCheck.make({
|
|
82
|
+
packageName: manifest.name,
|
|
83
|
+
packageVersion: manifest.version,
|
|
84
|
+
npmTargetId: npmTarget.id,
|
|
85
|
+
npmRegistry: npmTarget.registry,
|
|
86
|
+
githubTargetId: githubTarget.id,
|
|
87
|
+
githubRepository: githubTarget.repository,
|
|
88
|
+
...(githubTarget.tokenEnv === undefined ? {} : { githubTokenEnv: githubTarget.tokenEnv }),
|
|
89
|
+
expectedGithubDraft: githubTarget.draft ?? false
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
const looksMissing = (stdout, stderr) => {
|
|
93
|
+
const text = `${stdout}\n${stderr}`.toLowerCase();
|
|
94
|
+
return text.includes("not found") || text.includes("404") || text.includes("e404");
|
|
95
|
+
};
|
|
96
|
+
const firstDiagnosticLine = (result) => {
|
|
97
|
+
const lines = `${result.stderr}\n${result.stdout}`
|
|
98
|
+
.split(/\r?\n/)
|
|
99
|
+
.map((line) => line.trim())
|
|
100
|
+
.filter((line) => line.length > 0);
|
|
101
|
+
return lines[0] ?? `exit code ${result.exitCode}`;
|
|
102
|
+
};
|
|
103
|
+
const npmViewCommand = (input) => CommandSpec.make({
|
|
104
|
+
executable: "npm",
|
|
105
|
+
args: [
|
|
106
|
+
"view",
|
|
107
|
+
`${input.packageName}@${input.packageVersion}`,
|
|
108
|
+
"version",
|
|
109
|
+
"--registry",
|
|
110
|
+
input.npmRegistry
|
|
111
|
+
],
|
|
112
|
+
requiredEnv: [],
|
|
113
|
+
redactedEnv: []
|
|
114
|
+
});
|
|
115
|
+
const githubReleaseViewCommand = (input) => CommandSpec.make({
|
|
116
|
+
executable: "gh",
|
|
117
|
+
args: [
|
|
118
|
+
"release",
|
|
119
|
+
"view",
|
|
120
|
+
`v${input.packageVersion}`,
|
|
121
|
+
"--repo",
|
|
122
|
+
input.githubRepository,
|
|
123
|
+
"--json",
|
|
124
|
+
"isDraft,tagName,publishedAt"
|
|
125
|
+
],
|
|
126
|
+
requiredEnv: input.githubTokenEnv === undefined ? [] : [input.githubTokenEnv],
|
|
127
|
+
redactedEnv: input.githubTokenEnv === undefined ? [] : [input.githubTokenEnv]
|
|
128
|
+
});
|
|
129
|
+
const parseGitHubCliReleaseView = Effect.fn("parseGitHubCliReleaseView")(function* (input, stdout) {
|
|
130
|
+
const parsed = yield* Effect.try({
|
|
131
|
+
try: () => JSON.parse(stdout),
|
|
132
|
+
catch: (cause) => eligibilityError(cause instanceof Error ? cause.message : String(cause), input.githubTargetId)
|
|
133
|
+
});
|
|
134
|
+
const response = yield* decodeGitHubCliReleaseViewResponse(parsed).pipe(Effect.mapError((error) => eligibilityError(`gh release view returned malformed JSON: ${error.message}`, input.githubTargetId)));
|
|
135
|
+
return response.isDraft ? "draft" : "published";
|
|
136
|
+
});
|
|
137
|
+
export const classifyNpmRemoteState = Effect.fn("classifyNpmRemoteState")(function* (input) {
|
|
138
|
+
const commandRunner = yield* ReleaseCommandRunner;
|
|
139
|
+
const result = yield* commandRunner.runCommand(npmViewCommand(input));
|
|
140
|
+
if (result.exitCode === 0) {
|
|
141
|
+
return "published";
|
|
142
|
+
}
|
|
143
|
+
if (looksMissing(result.stdout, result.stderr)) {
|
|
144
|
+
return "missing";
|
|
145
|
+
}
|
|
146
|
+
return yield* Effect.fail(eligibilityError(`npm view failed while checking package state: ${firstDiagnosticLine(result)}`, input.npmTargetId));
|
|
147
|
+
});
|
|
148
|
+
export const classifyGitHubReleaseAvailability = Effect.fn("classifyGitHubReleaseAvailability")(function* (input) {
|
|
149
|
+
const commandRunner = yield* ReleaseCommandRunner;
|
|
150
|
+
const result = yield* commandRunner.runCommand(githubReleaseViewCommand(input));
|
|
151
|
+
if (result.exitCode === 0) {
|
|
152
|
+
return yield* parseGitHubCliReleaseView(input, result.stdout);
|
|
153
|
+
}
|
|
154
|
+
if (looksMissing(result.stdout, result.stderr)) {
|
|
155
|
+
return "missing";
|
|
156
|
+
}
|
|
157
|
+
return yield* Effect.fail(eligibilityError(`gh release view failed while checking release state: ${firstDiagnosticLine(result)}`, input.githubTargetId));
|
|
158
|
+
});
|
|
159
|
+
export const checkReleaseEligibility = Effect.fn("checkReleaseEligibility")(function* (input) {
|
|
160
|
+
const npm = yield* classifyNpmRemoteState(input);
|
|
161
|
+
const github = yield* classifyGitHubReleaseAvailability(input);
|
|
162
|
+
return decideReleaseEligibility(ReleaseEligibilityInput.make({
|
|
163
|
+
packageName: input.packageName,
|
|
164
|
+
packageVersion: input.packageVersion,
|
|
165
|
+
expectedGithubDraft: input.expectedGithubDraft,
|
|
166
|
+
npm,
|
|
167
|
+
github
|
|
168
|
+
}));
|
|
169
|
+
});
|
|
170
|
+
export const renderReleaseEligibilityText = (decision) => `release eligibility status=${decision.status} should-release=${decision.shouldRelease} reason=${JSON.stringify(decision.reason)}\n`;
|
|
171
|
+
export const renderReleaseEligibilityJson = (decision) => `${JSON.stringify(decision, null, 2)}\n`;
|
|
172
|
+
//# sourceMappingURL=release-eligibility.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"release-eligibility.js","sourceRoot":"","sources":["../../src/planner/release-eligibility.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AACpD,OAAO,EAGL,0BAA0B,EAC1B,uBAAuB,EACxB,MAAM,2BAA2B,CAAA;AAGlC,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AACtD,OAAO,EAAE,4BAA4B,EAAE,MAAM,aAAa,CAAA;AAI1D,MAAM,OAAO,sBAAuB,SAAQ,MAAM,CAAC,KAAK,CAAyB,wBAAwB,CAAC,CAAC;IACzG,IAAI,EAAE,MAAM,CAAC,MAAM;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM;CACvB,CAAC;CAAG;AAEL,MAAM,OAAO,6BAA8B,SAAQ,MAAM,CAAC,KAAK,CAC7D,+BAA+B,CAChC,CAAC;IACA,WAAW,EAAE,MAAM,CAAC,MAAM;IAC1B,cAAc,EAAE,MAAM,CAAC,MAAM;IAC7B,WAAW,EAAE,MAAM,CAAC,MAAM;IAC1B,WAAW,EAAE,MAAM,CAAC,MAAM;IAC1B,cAAc,EAAE,MAAM,CAAC,MAAM;IAC7B,gBAAgB,EAAE,MAAM,CAAC,MAAM;IAC/B,cAAc,EAAE,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC;IACjD,mBAAmB,EAAE,MAAM,CAAC,OAAO;CACpC,CAAC;CAAG;AAEL,MAAM,4BAA6B,SAAQ,MAAM,CAAC,KAAK,CACrD,8BAA8B,CAC/B,CAAC;IACA,OAAO,EAAE,MAAM,CAAC,OAAO;CACxB,CAAC;CAAG;AAEL,MAAM,kCAAkC,GAAG,MAAM,CAAC,mBAAmB,CAAC,4BAA4B,CAAC,CAAA;AAEnG,MAAM,YAAY,GAAG,CAAC,KAA8B,EAAU,EAAE,CAC9D,GAAG,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,cAAc,EAAE,CAAA;AAEhD,MAAM,mBAAmB,GAAG,CAAC,KAA8B,EAA6B,EAAE,CACxF,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAA;AAEnD,MAAM,CAAC,MAAM,wBAAwB,GAAG,CACtC,KAA8B,EACF,EAAE;IAC9B,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC1D,OAAO,0BAA0B,CAAC,IAAI,CAAC;YACrC,aAAa,EAAE,IAAI;YACnB,MAAM,EAAE,OAAO;YACf,MAAM,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,kCAAkC;SACjE,CAAC,CAAA;IACJ,CAAC;IAED,MAAM,cAAc,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAA;IACjD,IAAI,KAAK,CAAC,GAAG,KAAK,WAAW,IAAI,KAAK,CAAC,MAAM,KAAK,cAAc,EAAE,CAAC;QACjE,OAAO,0BAA0B,CAAC,IAAI,CAAC;YACrC,aAAa,EAAE,KAAK;YACpB,MAAM,EAAE,UAAU;YAClB,MAAM,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,0CAA0C;SACzE,CAAC,CAAA;IACJ,CAAC;IAED,IAAI,KAAK,CAAC,GAAG,KAAK,WAAW,IAAI,KAAK,CAAC,MAAM,KAAK,OAAO,IAAI,CAAC,KAAK,CAAC,mBAAmB,EAAE,CAAC;QACxF,OAAO,0BAA0B,CAAC,IAAI,CAAC;YACrC,aAAa,EAAE,KAAK;YACpB,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,6CAA6C,KAAK,CAAC,cAAc,oBAAoB;SACpH,CAAC,CAAA;IACJ,CAAC;IAED,IAAI,KAAK,CAAC,GAAG,KAAK,WAAW,IAAI,KAAK,CAAC,MAAM,KAAK,WAAW,IAAI,KAAK,CAAC,mBAAmB,EAAE,CAAC;QAC3F,OAAO,0BAA0B,CAAC,IAAI,CAAC;YACrC,aAAa,EAAE,KAAK;YACpB,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,6CAA6C,KAAK,CAAC,cAAc,8CAA8C;SAC9I,CAAC,CAAA;IACJ,CAAC;IAED,OAAO,0BAA0B,CAAC,IAAI,CAAC;QACrC,aAAa,EAAE,KAAK;QACpB,MAAM,EAAE,SAAS;QACjB,MAAM,EACJ,GAAG,YAAY,CAAC,KAAK,CAAC,kCAAkC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,MAAM,qBAAqB,cAAc,GAAG;KAClI,CAAC,CAAA;AACJ,CAAC,CAAA;AAED,MAAM,gBAAgB,GAAG,CACvB,MAAc,EACd,QAAiB,EACa,EAAE,CAChC,4BAA4B,CAAC,IAAI,CAAC;IAChC,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC;IAC/C,MAAM;CACP,CAAC,CAAA;AAEJ,MAAM,aAAa,GAAG,CACpB,MAAqB,EACrB,WAAmB,EACY,EAAE,CACjC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAA+B,EAAE,CAC1D,MAAM,CAAC,IAAI,KAAK,mBAAmB,IAAI,CAAC,MAAM,CAAC,WAAW,KAAK,WAAW,IAAI,MAAM,CAAC,EAAE,KAAK,KAAK,CAAC,CACnG,CAAA;AAEH,MAAM,gBAAgB,GAAG,CAAC,MAAqB,EAAmC,EAAE,CAClF,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAiC,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,qBAAqB,CAAC,CAAA;AAEvG,MAAM,CAAC,MAAM,uCAAuC,GAAG,MAAM,CAAC,EAAE,CAC9D,yCAAyC,CAC1C,CAAC,QAAQ,CAAC,EAAC,QAAgC,EAAE,MAAqB;IACjE,MAAM,SAAS,GAAG,aAAa,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAA;IACtD,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CACvB,gBAAgB,CAAC,iDAAiD,QAAQ,CAAC,IAAI,EAAE,CAAC,CACnF,CAAA;IACH,CAAC;IAED,MAAM,YAAY,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAA;IAC7C,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CACvB,gBAAgB,CAAC,qDAAqD,CAAC,CACxE,CAAA;IACH,CAAC;IAED,OAAO,6BAA6B,CAAC,IAAI,CAAC;QACxC,WAAW,EAAE,QAAQ,CAAC,IAAI;QAC1B,cAAc,EAAE,QAAQ,CAAC,OAAO;QAChC,WAAW,EAAE,SAAS,CAAC,EAAE;QACzB,WAAW,EAAE,SAAS,CAAC,QAAQ;QAC/B,cAAc,EAAE,YAAY,CAAC,EAAE;QAC/B,gBAAgB,EAAE,YAAY,CAAC,UAAU;QACzC,GAAG,CAAC,YAAY,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,YAAY,CAAC,QAAQ,EAAE,CAAC;QACzF,mBAAmB,EAAE,YAAY,CAAC,KAAK,IAAI,KAAK;KACjD,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,MAAM,YAAY,GAAG,CAAC,MAAc,EAAE,MAAc,EAAW,EAAE;IAC/D,MAAM,IAAI,GAAG,GAAG,MAAM,KAAK,MAAM,EAAE,CAAC,WAAW,EAAE,CAAA;IACjD,OAAO,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;AACpF,CAAC,CAAA;AAED,MAAM,mBAAmB,GAAG,CAAC,MAAuF,EAAU,EAAE;IAC9H,MAAM,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE;SAC/C,KAAK,CAAC,OAAO,CAAC;SACd,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IACpC,OAAO,KAAK,CAAC,CAAC,CAAC,IAAI,aAAa,MAAM,CAAC,QAAQ,EAAE,CAAA;AACnD,CAAC,CAAA;AAED,MAAM,cAAc,GAAG,CAAC,KAAoC,EAAe,EAAE,CAC3E,WAAW,CAAC,IAAI,CAAC;IACf,UAAU,EAAE,KAAK;IACjB,IAAI,EAAE;QACJ,MAAM;QACN,GAAG,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,cAAc,EAAE;QAC9C,SAAS;QACT,YAAY;QACZ,KAAK,CAAC,WAAW;KAClB;IACD,WAAW,EAAE,EAAE;IACf,WAAW,EAAE,EAAE;CAChB,CAAC,CAAA;AAEJ,MAAM,wBAAwB,GAAG,CAAC,KAAoC,EAAe,EAAE,CACrF,WAAW,CAAC,IAAI,CAAC;IACf,UAAU,EAAE,IAAI;IAChB,IAAI,EAAE;QACJ,SAAS;QACT,MAAM;QACN,IAAI,KAAK,CAAC,cAAc,EAAE;QAC1B,QAAQ;QACR,KAAK,CAAC,gBAAgB;QACtB,QAAQ;QACR,6BAA6B;KAC9B;IACD,WAAW,EAAE,KAAK,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC;IAC7E,WAAW,EAAE,KAAK,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC;CAC9E,CAAC,CAAA;AAEJ,MAAM,yBAAyB,GAAG,MAAM,CAAC,EAAE,CAAC,2BAA2B,CAAC,CAAC,QAAQ,CAAC,EAChF,KAAoC,EACpC,MAAc;IAEd,MAAM,MAAM,GAAY,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;QACxC,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;QAC7B,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CACf,gBAAgB,CACd,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EACtD,KAAK,CAAC,cAAc,CACrB;KACJ,CAAC,CAAA;IAEF,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,kCAAkC,CAAC,MAAM,CAAC,CAAC,IAAI,CACrE,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CACxB,gBAAgB,CAAC,4CAA4C,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,CACpG,CACF,CAAA;IACD,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAA;AACjD,CAAC,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAAG,MAAM,CAAC,EAAE,CAAC,wBAAwB,CAAC,CAAC,QAAQ,CAAC,EACjF,KAAoC;IAEpC,MAAM,aAAa,GAAG,KAAK,CAAC,CAAC,oBAAoB,CAAA;IACjD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,aAAa,CAAC,UAAU,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAA;IACrE,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,WAAoC,CAAA;IAC7C,CAAC;IACD,IAAI,YAAY,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/C,OAAO,SAAkC,CAAA;IAC3C,CAAC;IACD,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CACvB,gBAAgB,CACd,iDAAiD,mBAAmB,CAAC,MAAM,CAAC,EAAE,EAC9E,KAAK,CAAC,WAAW,CAClB,CACF,CAAA;AACH,CAAC,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,iCAAiC,GAAG,MAAM,CAAC,EAAE,CAAC,mCAAmC,CAAC,CAAC,QAAQ,CAAC,EACvG,KAAoC;IAEpC,MAAM,aAAa,GAAG,KAAK,CAAC,CAAC,oBAAoB,CAAA;IACjD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,aAAa,CAAC,UAAU,CAAC,wBAAwB,CAAC,KAAK,CAAC,CAAC,CAAA;IAC/E,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC,CAAC,yBAAyB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,CAAA;IAC/D,CAAC;IACD,IAAI,YAAY,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/C,OAAO,SAA6C,CAAA;IACtD,CAAC;IACD,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CACvB,gBAAgB,CACd,wDAAwD,mBAAmB,CAAC,MAAM,CAAC,EAAE,EACrF,KAAK,CAAC,cAAc,CACrB,CACF,CAAA;AACH,CAAC,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,uBAAuB,GAAG,MAAM,CAAC,EAAE,CAAC,yBAAyB,CAAC,CAAC,QAAQ,CAAC,EACnF,KAAoC;IAEpC,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAA;IAChD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,iCAAiC,CAAC,KAAK,CAAC,CAAA;IAC9D,OAAO,wBAAwB,CAC7B,uBAAuB,CAAC,IAAI,CAAC;QAC3B,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,mBAAmB,EAAE,KAAK,CAAC,mBAAmB;QAC9C,GAAG;QACH,MAAM;KACP,CAAC,CACH,CAAA;AACH,CAAC,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,QAAoC,EAAU,EAAE,CAC3F,8BAA8B,QAAQ,CAAC,MAAM,mBAAmB,QAAQ,CAAC,aAAa,WAAW,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAA;AAEtI,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,QAAoC,EAAU,EAAE,CAC3F,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-plan.d.ts","sourceRoot":"","sources":["../../src/planner/render-plan.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAElD,mBAAmB,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"render-plan.d.ts","sourceRoot":"","sources":["../../src/planner/render-plan.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAElD,mBAAmB,6BAA6B,CAAA;AAuChD,eAAO,MAAM,cAAc,GAAI,MAAM,WAAW,KAAG,MACb,CAAA;AAEtC,eAAO,MAAM,cAAc,GAAI,MAAM,WAAW,KAAG,MAoDlD,CAAA"}
|
|
@@ -1,10 +1,33 @@
|
|
|
1
1
|
const commandLine = (operation) => [operation.command.executable, ...operation.command.args].join(" ");
|
|
2
|
+
const commandArgv = (operation) => [
|
|
3
|
+
operation.command.executable,
|
|
4
|
+
...operation.command.args
|
|
5
|
+
];
|
|
2
6
|
const artifactLine = (artifact) => {
|
|
3
7
|
const checksum = artifact.checksum === undefined
|
|
4
8
|
? "checksum=none"
|
|
5
9
|
: `checksum=${artifact.checksum.algorithm}:${artifact.checksum.value}`;
|
|
6
10
|
return `- ${artifact.id} ${artifact.path} [${artifact.format}] size=${artifact.sizeBytes} ${checksum}`;
|
|
7
11
|
};
|
|
12
|
+
const capabilitySetupFields = (capability) => {
|
|
13
|
+
const setup = capability.authSetup;
|
|
14
|
+
if (setup === undefined) {
|
|
15
|
+
return "";
|
|
16
|
+
}
|
|
17
|
+
const permissions = setup.requiredPermissions
|
|
18
|
+
.map((permission) => `required-permission=${permission.name}:${permission.value}`)
|
|
19
|
+
.join(" ");
|
|
20
|
+
const prerequisites = setup.prerequisites
|
|
21
|
+
.map((prerequisite) => prerequisite === "npm-package-exists" ? "package-prerequisite=exists" : `prerequisite=${prerequisite}`)
|
|
22
|
+
.join(" ");
|
|
23
|
+
return [
|
|
24
|
+
`runs-in=${setup.runsIn}`,
|
|
25
|
+
`provider=${setup.provider}`,
|
|
26
|
+
`workflow=${setup.workflow}`,
|
|
27
|
+
permissions,
|
|
28
|
+
prerequisites
|
|
29
|
+
].filter((field) => field.length > 0).join(" ");
|
|
30
|
+
};
|
|
8
31
|
export const renderPlanJson = (plan) => `${JSON.stringify(plan, null, 2)}\n`;
|
|
9
32
|
export const renderPlanText = (plan) => {
|
|
10
33
|
const lines = [
|
|
@@ -23,7 +46,9 @@ export const renderPlanText = (plan) => {
|
|
|
23
46
|
lines.push("");
|
|
24
47
|
lines.push("targets:");
|
|
25
48
|
for (const capability of plan.targetCapabilities) {
|
|
49
|
+
const setupFields = capabilitySetupFields(capability);
|
|
26
50
|
lines.push(` - ${capability.targetId} [${capability.targetTag}] auth=${capability.authRequirement} ` +
|
|
51
|
+
`${setupFields.length === 0 ? "" : `${setupFields} `}` +
|
|
27
52
|
`dry-run=${capability.dryRunSupport} strategy=${capability.validationStrategy} ` +
|
|
28
53
|
`mutability=${capability.mutability} recovery=${capability.recovery}`);
|
|
29
54
|
}
|
|
@@ -33,6 +58,7 @@ export const renderPlanText = (plan) => {
|
|
|
33
58
|
lines.push(` - ${operation.id} [${operation.risk}] ${operation.description}`);
|
|
34
59
|
if ("command" in operation) {
|
|
35
60
|
lines.push(` command: ${commandLine(operation)}`);
|
|
61
|
+
lines.push(` argv: ${JSON.stringify(commandArgv(operation))}`);
|
|
36
62
|
}
|
|
37
63
|
if (operation._tag === "RenderFileOperation") {
|
|
38
64
|
lines.push(` write: ${operation.path}`);
|
|
@@ -40,6 +66,10 @@ export const renderPlanText = (plan) => {
|
|
|
40
66
|
if (operation._tag === "ValidationNoteOperation") {
|
|
41
67
|
lines.push(` note: ${operation.message}`);
|
|
42
68
|
}
|
|
69
|
+
if (operation._tag === "VerifyHttpOperation") {
|
|
70
|
+
lines.push(` http: ${operation.request.method} ${operation.request.url}`);
|
|
71
|
+
lines.push(` expect: status ${operation.expectedStatus}, checks ${operation.checks.length}`);
|
|
72
|
+
}
|
|
43
73
|
if (operation.gate.requiresExecute) {
|
|
44
74
|
lines.push(` gate: execute${operation.gate.requiresIrreversibleApproval ? " + irreversible approval" : ""}`);
|
|
45
75
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render-plan.js","sourceRoot":"","sources":["../../src/planner/render-plan.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,GAAG,CAAC,SAAgE,EAAU,EAAE,CAC/F,CAAC,SAAS,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAErE,MAAM,YAAY,GAAG,CAAC,QAA0C,EAAU,EAAE;IAC1E,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,KAAK,SAAS;QAC9C,CAAC,CAAC,eAAe;QACjB,CAAC,CAAC,YAAY,QAAQ,CAAC,QAAQ,CAAC,SAAS,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAA;IACxE,OAAO,KAAK,QAAQ,CAAC,EAAE,IAAI,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,MAAM,UAAU,QAAQ,CAAC,SAAS,IAAI,QAAQ,EAAE,CAAA;AACxG,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,IAAiB,EAAU,EAAE,CAC1D,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAA;AAEtC,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,IAAiB,EAAU,EAAE;IAC1D,MAAM,KAAK,GAAkB;QAC3B,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE;QAChD,WAAW,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE;QACjC,aAAa,IAAI,CAAC,iBAAiB,EAAE;QACrC,cAAc,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE;QACrC,YAAY,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE;QACjC,eAAe,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE;QACvC,EAAE;KACH,CAAA;IAED,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;IACxB,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACtC,KAAK,CAAC,IAAI,CAAC,KAAK,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;IAC3C,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAEd,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IACtB,KAAK,MAAM,UAAU,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACjD,KAAK,CAAC,IAAI,CACR,OAAO,UAAU,CAAC,QAAQ,KAAK,UAAU,CAAC,SAAS,UAAU,UAAU,CAAC,eAAe,GAAG;YACxF,WAAW,UAAU,CAAC,aAAa,aAAa,UAAU,CAAC,kBAAkB,GAAG;YAChF,cAAc,UAAU,CAAC,UAAU,aAAa,UAAU,CAAC,QAAQ,EAAE,CACxE,CAAA;IACH,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAEd,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;IACzB,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;QACxC,KAAK,CAAC,IAAI,CAAC,OAAO,SAAS,CAAC,EAAE,KAAK,SAAS,CAAC,IAAI,KAAK,SAAS,CAAC,WAAW,EAAE,CAAC,CAAA;QAC9E,IAAI,SAAS,IAAI,SAAS,EAAE,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,cAAc,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"render-plan.js","sourceRoot":"","sources":["../../src/planner/render-plan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
3
|
+
import { EvidenceBundle, ReleaseWorkflowEvidence } from "../domain/evidence.js";
|
|
4
|
+
import { Operation } from "../domain/operation.js";
|
|
5
|
+
import { ReleasePlan } from "../domain/release.js";
|
|
6
|
+
import { ReleaseStatusReport, ReleaseWorkflowPhase } from "../domain/status.js";
|
|
7
|
+
import { EvidenceReadError, OperationFailedError, ResumeBlockedError } from "./errors.js";
|
|
8
|
+
export type * from "../types/effect-internal.js";
|
|
9
|
+
export interface WorkflowEvidencePaths {
|
|
10
|
+
readonly render: string;
|
|
11
|
+
readonly validation: string;
|
|
12
|
+
readonly execution: string;
|
|
13
|
+
readonly verification: string;
|
|
14
|
+
}
|
|
15
|
+
export interface OptionalWorkflowEvidence {
|
|
16
|
+
readonly render?: EvidenceBundle | undefined;
|
|
17
|
+
readonly validation?: EvidenceBundle | undefined;
|
|
18
|
+
readonly execution?: EvidenceBundle | undefined;
|
|
19
|
+
readonly verification?: EvidenceBundle | undefined;
|
|
20
|
+
}
|
|
21
|
+
declare const ReleaseResumeOptions_base: Schema.Class<ReleaseResumeOptions, Schema.Struct<{
|
|
22
|
+
readonly execute: Schema.Boolean;
|
|
23
|
+
readonly approveIrreversible: Schema.Boolean;
|
|
24
|
+
}>, {}>;
|
|
25
|
+
export declare class ReleaseResumeOptions extends ReleaseResumeOptions_base {
|
|
26
|
+
}
|
|
27
|
+
export declare const workflowEvidencePaths: (plan: ReleasePlan) => WorkflowEvidencePaths;
|
|
28
|
+
export declare const loadWorkflowEvidence: (plan: ReleasePlan) => Effect.Effect<{
|
|
29
|
+
render: EvidenceBundle | undefined;
|
|
30
|
+
validation: EvidenceBundle | undefined;
|
|
31
|
+
execution: EvidenceBundle | undefined;
|
|
32
|
+
verification: EvidenceBundle | undefined;
|
|
33
|
+
}, EvidenceReadError, import("effect/FileSystem").FileSystem | import("effect/Path").Path>;
|
|
34
|
+
export declare const phaseOperations: (plan: ReleasePlan, phase: ReleaseWorkflowPhase) => ReadonlyArray<Operation>;
|
|
35
|
+
export declare const summarizeReleaseStatus: (plan: ReleasePlan, evidence: OptionalWorkflowEvidence) => ReleaseStatusReport;
|
|
36
|
+
export declare const statusReleasePlan: (plan: ReleasePlan) => Effect.Effect<ReleaseStatusReport, EvidenceReadError, import("effect/FileSystem").FileSystem | import("effect/Path").Path>;
|
|
37
|
+
export declare const renderReleaseStatusText: (report: ReleaseStatusReport) => string;
|
|
38
|
+
export declare const renderReleaseStatusJson: (report: ReleaseStatusReport) => string;
|
|
39
|
+
export declare const resumeApprovedReleaseWorkflow: (plan: ReleasePlan, options: ReleaseResumeOptions) => Effect.Effect<ReleaseWorkflowEvidence, import("../domain/operation.js").ExecutionApprovalError | EvidenceReadError | import("./errors.js").WorkspaceWriteError | ResumeBlockedError | OperationFailedError | import("../host/host.js").CommandRunnerError, import("effect/FileSystem").FileSystem | import("effect/Path").Path | import("../host/host.js").ReleaseCommandRunner | import("../host/http.js").ReleaseHttp>;
|
|
40
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/planner/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,cAAc,EAAkB,uBAAuB,EAAkC,MAAM,uBAAuB,CAAA;AAC/H,OAAO,EAAqB,SAAS,EAAE,MAAM,wBAAwB,CAAA;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAClD,OAAO,EAML,mBAAmB,EACnB,oBAAoB,EACrB,MAAM,qBAAqB,CAAA;AAa5B,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAEzF,mBAAmB,6BAA6B,CAAA;AAEhD,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAC9B;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,MAAM,CAAC,EAAE,cAAc,GAAG,SAAS,CAAA;IAC5C,QAAQ,CAAC,UAAU,CAAC,EAAE,cAAc,GAAG,SAAS,CAAA;IAChD,QAAQ,CAAC,SAAS,CAAC,EAAE,cAAc,GAAG,SAAS,CAAA;IAC/C,QAAQ,CAAC,YAAY,CAAC,EAAE,cAAc,GAAG,SAAS,CAAA;CACnD;;;;;AASD,qBAAa,oBAAqB,SAAQ,yBAGxC;CAAG;AAIL,eAAO,MAAM,qBAAqB,GAAI,MAAM,WAAW,KAAG,qBAKxD,CAAA;AAsBF,eAAO,MAAM,oBAAoB;;;;;0FAiB/B,CAAA;AAEF,eAAO,MAAM,eAAe,GAAI,MAAM,WAAW,EAAE,OAAO,oBAAoB,KAAG,aAAa,CAAC,SAAS,CAWvG,CAAA;AAqMD,eAAO,MAAM,sBAAsB,GACjC,MAAM,WAAW,EACjB,UAAU,wBAAwB,KACjC,mBAoBF,CAAA;AAED,eAAO,MAAM,iBAAiB,mJAG5B,CAAA;AAEF,eAAO,MAAM,uBAAuB,GAAI,QAAQ,mBAAmB,KAAG,MAoBrE,CAAA;AAED,eAAO,MAAM,uBAAuB,GAAI,QAAQ,mBAAmB,KAAG,MAC9B,CAAA;AAwFxC,eAAO,MAAM,6BAA6B,gdA2DxC,CAAA"}
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
3
|
+
import { ReleaseWorkflowEvidence, ReleaseWorkflowFailureEvidence } from "../domain/evidence.js";
|
|
4
|
+
import { ExecutionApproval } from "../domain/operation.js";
|
|
5
|
+
import { ReleaseOperationStatusRecord, ReleasePhaseStatusRecord, ReleaseStatusReport } from "../domain/status.js";
|
|
6
|
+
import { emptyEvidenceBundle, mergeEvidenceBundles, tryReadEvidenceBundle } from "./evidence-recorder.js";
|
|
7
|
+
import { publishOperations, renderOperations, runOperations, validationOperations, verificationOperations } from "./executor.js";
|
|
8
|
+
import { EvidenceReadError, OperationFailedError, ResumeBlockedError } from "./errors.js";
|
|
9
|
+
export class ReleaseResumeOptions extends Schema.Class("ReleaseResumeOptions")({
|
|
10
|
+
execute: Schema.Boolean,
|
|
11
|
+
approveIrreversible: Schema.Boolean
|
|
12
|
+
}) {
|
|
13
|
+
}
|
|
14
|
+
const workflowPhases = ["render", "validation", "execution", "verification"];
|
|
15
|
+
export const workflowEvidencePaths = (plan) => ({
|
|
16
|
+
render: `${plan.evidenceDirectory}/render.json`,
|
|
17
|
+
validation: `${plan.evidenceDirectory}/validation.json`,
|
|
18
|
+
execution: `${plan.evidenceDirectory}/execution.json`,
|
|
19
|
+
verification: `${plan.evidenceDirectory}/verification.json`
|
|
20
|
+
});
|
|
21
|
+
const ensureBundleMatchesPlan = (plan, pathName, bundle) => {
|
|
22
|
+
if (bundle === undefined) {
|
|
23
|
+
return Effect.void;
|
|
24
|
+
}
|
|
25
|
+
if (bundle.releaseName === plan.identity.name && bundle.releaseVersion === plan.identity.version) {
|
|
26
|
+
return Effect.void;
|
|
27
|
+
}
|
|
28
|
+
return Effect.fail(EvidenceReadError.make({
|
|
29
|
+
path: pathName,
|
|
30
|
+
reason: `Evidence bundle is for ${bundle.releaseName}@${bundle.releaseVersion}, expected ${plan.identity.name}@${plan.identity.version}.`
|
|
31
|
+
}));
|
|
32
|
+
};
|
|
33
|
+
export const loadWorkflowEvidence = Effect.fn("loadWorkflowEvidence")(function* (plan) {
|
|
34
|
+
const paths = workflowEvidencePaths(plan);
|
|
35
|
+
const render = yield* tryReadEvidenceBundle(paths.render, plan.source.root);
|
|
36
|
+
yield* ensureBundleMatchesPlan(plan, paths.render, render);
|
|
37
|
+
const validation = yield* tryReadEvidenceBundle(paths.validation, plan.source.root);
|
|
38
|
+
yield* ensureBundleMatchesPlan(plan, paths.validation, validation);
|
|
39
|
+
const execution = yield* tryReadEvidenceBundle(paths.execution, plan.source.root);
|
|
40
|
+
yield* ensureBundleMatchesPlan(plan, paths.execution, execution);
|
|
41
|
+
const verification = yield* tryReadEvidenceBundle(paths.verification, plan.source.root);
|
|
42
|
+
yield* ensureBundleMatchesPlan(plan, paths.verification, verification);
|
|
43
|
+
return {
|
|
44
|
+
render,
|
|
45
|
+
validation,
|
|
46
|
+
execution,
|
|
47
|
+
verification
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
export const phaseOperations = (plan, phase) => {
|
|
51
|
+
switch (phase) {
|
|
52
|
+
case "render":
|
|
53
|
+
return renderOperations(plan);
|
|
54
|
+
case "validation":
|
|
55
|
+
return validationOperations(plan);
|
|
56
|
+
case "execution":
|
|
57
|
+
return publishOperations(plan);
|
|
58
|
+
case "verification":
|
|
59
|
+
return verificationOperations(plan);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
const evidenceBundleForPhase = (evidence, phase) => {
|
|
63
|
+
switch (phase) {
|
|
64
|
+
case "render":
|
|
65
|
+
return evidence.render;
|
|
66
|
+
case "validation":
|
|
67
|
+
return evidence.validation;
|
|
68
|
+
case "execution":
|
|
69
|
+
return evidence.execution;
|
|
70
|
+
case "verification":
|
|
71
|
+
return evidence.verification;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
const recordOperationId = (record) => "operationId" in record ? record.operationId : undefined;
|
|
75
|
+
const matchesOperation = (operation, record) => {
|
|
76
|
+
const operationId = recordOperationId(record);
|
|
77
|
+
if (operationId !== undefined) {
|
|
78
|
+
return operationId === operation.id;
|
|
79
|
+
}
|
|
80
|
+
return operation._tag === "ValidationNoteOperation" && record.id === `${operation.id}:validation`;
|
|
81
|
+
};
|
|
82
|
+
const latestEvidenceRecord = (operation, bundle) => {
|
|
83
|
+
if (bundle === undefined) {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
let latest;
|
|
87
|
+
for (const record of bundle.records) {
|
|
88
|
+
if (matchesOperation(operation, record)) {
|
|
89
|
+
latest = record;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return latest;
|
|
93
|
+
};
|
|
94
|
+
const statusFromEvidence = (record) => {
|
|
95
|
+
switch (record.status) {
|
|
96
|
+
case "passed":
|
|
97
|
+
return "passed";
|
|
98
|
+
case "warning":
|
|
99
|
+
return "warning";
|
|
100
|
+
case "skipped":
|
|
101
|
+
return "skipped";
|
|
102
|
+
case "failed":
|
|
103
|
+
return "failed";
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
const failedResumeAction = (operation) => {
|
|
107
|
+
switch (operation._tag) {
|
|
108
|
+
case "PublishCommandOperation":
|
|
109
|
+
return "block";
|
|
110
|
+
case "ValidateCommandOperation":
|
|
111
|
+
case "ValidationNoteOperation":
|
|
112
|
+
case "VerifyRemoteOperation":
|
|
113
|
+
case "VerifyHttpOperation":
|
|
114
|
+
return "retry-read-only";
|
|
115
|
+
case "RenderFileOperation":
|
|
116
|
+
return "run";
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const failedStatus = (operation) => operation._tag === "PublishCommandOperation" ? "blocked" : "failed";
|
|
120
|
+
const failedReason = (operation) => {
|
|
121
|
+
switch (operation._tag) {
|
|
122
|
+
case "PublishCommandOperation":
|
|
123
|
+
return "Previous publish evidence failed; inspect remote state before retry.";
|
|
124
|
+
case "ValidateCommandOperation":
|
|
125
|
+
case "ValidationNoteOperation":
|
|
126
|
+
return "Previous validation evidence failed; resume can rerun this read-only operation.";
|
|
127
|
+
case "VerifyRemoteOperation":
|
|
128
|
+
case "VerifyHttpOperation":
|
|
129
|
+
return "Previous verification evidence failed; resume can rerun this read-only operation.";
|
|
130
|
+
case "RenderFileOperation":
|
|
131
|
+
return "Previous render evidence failed; resume can rerun this local render operation.";
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
const operationStatusRecord = (operation, phase, bundle) => {
|
|
135
|
+
const record = latestEvidenceRecord(operation, bundle);
|
|
136
|
+
if (record === undefined) {
|
|
137
|
+
return ReleaseOperationStatusRecord.make({
|
|
138
|
+
operationId: operation.id,
|
|
139
|
+
...(operation.targetId === undefined ? {} : { targetId: operation.targetId }),
|
|
140
|
+
phase,
|
|
141
|
+
risk: operation.risk,
|
|
142
|
+
status: "pending",
|
|
143
|
+
resumeAction: "run"
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
if (record.status === "failed") {
|
|
147
|
+
return ReleaseOperationStatusRecord.make({
|
|
148
|
+
operationId: operation.id,
|
|
149
|
+
...(operation.targetId === undefined ? {} : { targetId: operation.targetId }),
|
|
150
|
+
phase,
|
|
151
|
+
risk: operation.risk,
|
|
152
|
+
status: failedStatus(operation),
|
|
153
|
+
resumeAction: failedResumeAction(operation),
|
|
154
|
+
evidenceId: record.id,
|
|
155
|
+
reason: failedReason(operation)
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
return ReleaseOperationStatusRecord.make({
|
|
159
|
+
operationId: operation.id,
|
|
160
|
+
...(operation.targetId === undefined ? {} : { targetId: operation.targetId }),
|
|
161
|
+
phase,
|
|
162
|
+
risk: operation.risk,
|
|
163
|
+
status: statusFromEvidence(record),
|
|
164
|
+
resumeAction: "skip",
|
|
165
|
+
evidenceId: record.id
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
const completedOperation = (status) => status === "passed" || status === "warning" || status === "skipped";
|
|
169
|
+
const phaseStatus = (records) => {
|
|
170
|
+
if (records.length === 0) {
|
|
171
|
+
return "passed";
|
|
172
|
+
}
|
|
173
|
+
if (records.some((record) => record.status === "blocked")) {
|
|
174
|
+
return "blocked";
|
|
175
|
+
}
|
|
176
|
+
if (records.some((record) => record.status === "failed")) {
|
|
177
|
+
return "failed";
|
|
178
|
+
}
|
|
179
|
+
if (records.some((record) => record.status === "pending")) {
|
|
180
|
+
return "pending";
|
|
181
|
+
}
|
|
182
|
+
if (records.some((record) => record.status === "warning")) {
|
|
183
|
+
return "warning";
|
|
184
|
+
}
|
|
185
|
+
if (records.every((record) => record.status === "skipped")) {
|
|
186
|
+
return "skipped";
|
|
187
|
+
}
|
|
188
|
+
return "passed";
|
|
189
|
+
};
|
|
190
|
+
const phaseStatusRecord = (phase, records) => ReleasePhaseStatusRecord.make({
|
|
191
|
+
phase,
|
|
192
|
+
status: phaseStatus(records),
|
|
193
|
+
completed: records.filter((record) => completedOperation(record.status)).length,
|
|
194
|
+
total: records.length
|
|
195
|
+
});
|
|
196
|
+
const overallStatus = (evidence, operations, phases) => {
|
|
197
|
+
const evidenceFilesExist = workflowPhases.some((phase) => evidenceBundleForPhase(evidence, phase) !== undefined);
|
|
198
|
+
const operationEvidenceExists = operations.some((operation) => operation.evidenceId !== undefined);
|
|
199
|
+
if (!evidenceFilesExist && !operationEvidenceExists) {
|
|
200
|
+
return "not-started";
|
|
201
|
+
}
|
|
202
|
+
if (operations.some((operation) => operation.status === "blocked")) {
|
|
203
|
+
return "blocked";
|
|
204
|
+
}
|
|
205
|
+
if (operations.some((operation) => operation.status === "failed")) {
|
|
206
|
+
return "failed";
|
|
207
|
+
}
|
|
208
|
+
if (phases.every((phase) => phase.completed === phase.total)) {
|
|
209
|
+
return "complete";
|
|
210
|
+
}
|
|
211
|
+
return "in-progress";
|
|
212
|
+
};
|
|
213
|
+
const canResumeReport = (overall, operations) => overall !== "blocked" &&
|
|
214
|
+
overall !== "complete" &&
|
|
215
|
+
operations.some((operation) => operation.resumeAction === "run" || operation.resumeAction === "retry-read-only");
|
|
216
|
+
export const summarizeReleaseStatus = (plan, evidence) => {
|
|
217
|
+
const operations = [];
|
|
218
|
+
const phases = [];
|
|
219
|
+
for (const phase of workflowPhases) {
|
|
220
|
+
const bundle = evidenceBundleForPhase(evidence, phase);
|
|
221
|
+
const records = phaseOperations(plan, phase).map((operation) => operationStatusRecord(operation, phase, bundle));
|
|
222
|
+
operations.push(...records);
|
|
223
|
+
phases.push(phaseStatusRecord(phase, records));
|
|
224
|
+
}
|
|
225
|
+
const overall = overallStatus(evidence, operations, phases);
|
|
226
|
+
return ReleaseStatusReport.make({
|
|
227
|
+
schemaVersion: "release-status/v1",
|
|
228
|
+
releaseName: plan.identity.name,
|
|
229
|
+
releaseVersion: plan.identity.version,
|
|
230
|
+
overallStatus: overall,
|
|
231
|
+
canResume: canResumeReport(overall, operations),
|
|
232
|
+
evidenceDirectory: plan.evidenceDirectory,
|
|
233
|
+
phases,
|
|
234
|
+
operations
|
|
235
|
+
});
|
|
236
|
+
};
|
|
237
|
+
export const statusReleasePlan = Effect.fn("statusReleasePlan")(function* (plan) {
|
|
238
|
+
const evidence = yield* loadWorkflowEvidence(plan);
|
|
239
|
+
return summarizeReleaseStatus(plan, evidence);
|
|
240
|
+
});
|
|
241
|
+
export const renderReleaseStatusText = (report) => {
|
|
242
|
+
const lines = [
|
|
243
|
+
`${report.releaseName}@${report.releaseVersion} status=${report.overallStatus} can-resume=${report.canResume} evidence=${report.evidenceDirectory}`,
|
|
244
|
+
"",
|
|
245
|
+
"phases:"
|
|
246
|
+
];
|
|
247
|
+
for (const phase of report.phases) {
|
|
248
|
+
lines.push(` ${phase.phase}: ${phase.status} ${phase.completed}/${phase.total}`);
|
|
249
|
+
}
|
|
250
|
+
lines.push("", "operations:");
|
|
251
|
+
for (const operation of report.operations) {
|
|
252
|
+
const fields = [
|
|
253
|
+
` - ${operation.operationId} [${operation.phase}] ${operation.status}`,
|
|
254
|
+
`resume=${operation.resumeAction}`,
|
|
255
|
+
operation.evidenceId === undefined ? "" : `evidence=${operation.evidenceId}`,
|
|
256
|
+
operation.reason === undefined ? "" : `reason=${JSON.stringify(operation.reason)}`
|
|
257
|
+
].filter((field) => field.length > 0);
|
|
258
|
+
lines.push(fields.join(" "));
|
|
259
|
+
}
|
|
260
|
+
return `${lines.join("\n")}\n`;
|
|
261
|
+
};
|
|
262
|
+
export const renderReleaseStatusJson = (report) => `${JSON.stringify(report, null, 2)}\n`;
|
|
263
|
+
const approvalForPhase = (phase, options) => {
|
|
264
|
+
switch (phase) {
|
|
265
|
+
case "render":
|
|
266
|
+
return ExecutionApproval.make({
|
|
267
|
+
execute: options.execute,
|
|
268
|
+
approveIrreversible: false
|
|
269
|
+
});
|
|
270
|
+
case "validation":
|
|
271
|
+
case "verification":
|
|
272
|
+
return ExecutionApproval.none;
|
|
273
|
+
case "execution":
|
|
274
|
+
return ExecutionApproval.make({
|
|
275
|
+
execute: options.execute,
|
|
276
|
+
approveIrreversible: options.approveIrreversible
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
const shouldRunOperation = (report, operation) => {
|
|
281
|
+
const status = report.operations.find((record) => record.operationId === operation.id);
|
|
282
|
+
return status?.resumeAction === "run" || status?.resumeAction === "retry-read-only";
|
|
283
|
+
};
|
|
284
|
+
const failWorkflowOperation = (error, workflowEvidence) => Effect.fail(OperationFailedError.make({
|
|
285
|
+
operationId: error.operationId,
|
|
286
|
+
...(error.exitCode === undefined ? {} : { exitCode: error.exitCode }),
|
|
287
|
+
...(error.responseStatus === undefined ? {} : { responseStatus: error.responseStatus }),
|
|
288
|
+
reason: error.reason,
|
|
289
|
+
...(error.evidence === undefined ? {} : { evidence: error.evidence }),
|
|
290
|
+
workflowEvidence
|
|
291
|
+
}));
|
|
292
|
+
const workflowFailureEvidence = (prefix, phase, current) => ReleaseWorkflowFailureEvidence.make({
|
|
293
|
+
...(prefix.render === undefined ? {} : { render: prefix.render }),
|
|
294
|
+
...(prefix.validation === undefined ? {} : { validation: prefix.validation }),
|
|
295
|
+
...(prefix.execution === undefined ? {} : { execution: prefix.execution }),
|
|
296
|
+
...(prefix.verification === undefined ? {} : { verification: prefix.verification }),
|
|
297
|
+
...(phase === "render" && current !== undefined ? { render: current } : {}),
|
|
298
|
+
...(phase === "validation" && current !== undefined ? { validation: current } : {}),
|
|
299
|
+
...(phase === "execution" && current !== undefined ? { execution: current } : {}),
|
|
300
|
+
...(phase === "verification" && current !== undefined ? { verification: current } : {})
|
|
301
|
+
});
|
|
302
|
+
const resumePhase = Effect.fn("resumePhase")(function* (plan, phase, existing, operations, approval, report, prefix) {
|
|
303
|
+
const runnable = operations.filter((operation) => shouldRunOperation(report, operation));
|
|
304
|
+
if (runnable.length === 0) {
|
|
305
|
+
return existing ?? emptyEvidenceBundle(plan);
|
|
306
|
+
}
|
|
307
|
+
const fresh = yield* runOperations(plan, runnable, approval).pipe(Effect.catchTag("OperationFailedError", (error) => Effect.gen(function* () {
|
|
308
|
+
const current = error.evidence === undefined
|
|
309
|
+
? existing
|
|
310
|
+
: yield* mergeEvidenceBundles(plan, existing, error.evidence);
|
|
311
|
+
return yield* failWorkflowOperation(error, workflowFailureEvidence(prefix, phase, current));
|
|
312
|
+
})));
|
|
313
|
+
return yield* mergeEvidenceBundles(plan, existing, fresh);
|
|
314
|
+
});
|
|
315
|
+
export const resumeApprovedReleaseWorkflow = Effect.fn("resumeApprovedReleaseWorkflow")(function* (plan, options) {
|
|
316
|
+
const evidence = yield* loadWorkflowEvidence(plan);
|
|
317
|
+
const report = summarizeReleaseStatus(plan, evidence);
|
|
318
|
+
const blocked = report.operations.find((operation) => operation.resumeAction === "block");
|
|
319
|
+
if (blocked !== undefined) {
|
|
320
|
+
return yield* Effect.fail(ResumeBlockedError.make({
|
|
321
|
+
operationId: blocked.operationId,
|
|
322
|
+
reason: blocked.reason ?? "Resume is blocked by previous failed publish evidence."
|
|
323
|
+
}));
|
|
324
|
+
}
|
|
325
|
+
const render = yield* resumePhase(plan, "render", evidence.render, renderOperations(plan), approvalForPhase("render", options), report, {});
|
|
326
|
+
const validation = yield* resumePhase(plan, "validation", evidence.validation, validationOperations(plan), approvalForPhase("validation", options), report, { render });
|
|
327
|
+
const execution = yield* resumePhase(plan, "execution", evidence.execution, publishOperations(plan), approvalForPhase("execution", options), report, { render, validation });
|
|
328
|
+
const verification = yield* resumePhase(plan, "verification", evidence.verification, verificationOperations(plan), approvalForPhase("verification", options), report, { render, validation, execution });
|
|
329
|
+
return ReleaseWorkflowEvidence.make({
|
|
330
|
+
render,
|
|
331
|
+
validation,
|
|
332
|
+
execution,
|
|
333
|
+
verification
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
//# sourceMappingURL=status.js.map
|