@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
package/ARCHITECTURE.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
`@mannyc1/ts-release` is library-first and CLI-second.
|
|
4
|
+
|
|
5
|
+
The package turns release intent into typed data, plans, evidence, and approved operations. The CLI is only an argv, console, and terminal-file adapter over those workflows.
|
|
6
|
+
|
|
7
|
+
## Module Taxonomy
|
|
8
|
+
|
|
9
|
+
- `domain/` contains durable schema-backed data models and errors.
|
|
10
|
+
- `config/` parses and validates release config into domain values.
|
|
11
|
+
- `planner/` normalizes release intent, builds plans, renders plans, executes operation data through injected services, records evidence, reports status, resumes safe work, and reconciles remote state.
|
|
12
|
+
- `targets/` models ecosystem-specific target semantics and produces operation data. Target modules may describe commands and HTTP checks, but they do not execute them.
|
|
13
|
+
- `host/` defines injectable command and HTTP services plus live or test implementations.
|
|
14
|
+
- `workflows/` contains reusable application workflows over config files, evidence files, and live target/HTTP composition. This is the high-level programmatic surface.
|
|
15
|
+
- `runtime/` contains runtime-specific layer assembly. `runtime/bun` is the Bun composition used by the published executable and maintenance scripts.
|
|
16
|
+
- `cli/` parses command-line flags, calls workflows, prints terminal output, and writes user-requested CLI output files.
|
|
17
|
+
- `scripts/` contains repository maintenance checks. Scripts may use runtime layers, but they are not package library code.
|
|
18
|
+
|
|
19
|
+
## Dependency Direction
|
|
20
|
+
|
|
21
|
+
Library modules must not import from `cli/`.
|
|
22
|
+
|
|
23
|
+
The normal flow is:
|
|
24
|
+
|
|
25
|
+
```text
|
|
26
|
+
domain <- config
|
|
27
|
+
domain <- planner <- targets
|
|
28
|
+
domain <- host
|
|
29
|
+
workflows -> config/planner/host/targets
|
|
30
|
+
runtime -> host/workflows/platform layers
|
|
31
|
+
cli -> workflows/runtime boundary
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`src/index.ts` intentionally stays empty. Public API is the explicit subpath list in `package.json`, checked by `scripts/check-package-exports.ts` and `scripts/check-tree-shaking.ts`.
|
|
35
|
+
|
|
36
|
+
## Public Workflow Surface
|
|
37
|
+
|
|
38
|
+
There is no public `./api` facade. The public workflow modules are named after the work they own:
|
|
39
|
+
|
|
40
|
+
- `./workflows/config` for config-file release workflows.
|
|
41
|
+
- `./workflows/evidence` for reusable evidence persistence.
|
|
42
|
+
- `./workflows/live` for runtime-neutral live target and HTTP services.
|
|
43
|
+
- `./runtime/bun` for the Bun runtime composition.
|
|
44
|
+
|
|
45
|
+
Use lower-level `domain/`, `config/`, `planner/`, `host/`, and `targets/` subpaths when a caller needs more control than the workflow modules provide.
|
|
46
|
+
|
|
47
|
+
## Boundary Rules
|
|
48
|
+
|
|
49
|
+
- Publish operations are data until execution is explicitly approved.
|
|
50
|
+
- `Effect.run*` belongs at true runtime boundaries.
|
|
51
|
+
- Layers are provided at CLI, runtime, script, and test boundaries.
|
|
52
|
+
- Config parsing, evidence persistence, status, resume, reconciliation, and release execution are library workflows, not CLI behavior.
|
|
53
|
+
- Terminal formatting, argv parsing, and `--out` file writing belong in `cli/`.
|
package/README.md
CHANGED
|
@@ -4,6 +4,16 @@
|
|
|
4
4
|
|
|
5
5
|
The default workflow is plan-first:
|
|
6
6
|
|
|
7
|
+
```sh
|
|
8
|
+
bun run cli plan --config release.config.json --format text
|
|
9
|
+
bun run cli run --config release.config.json --execute --approve-irreversible
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
The `run` command is the recommended release path: it renders generated files,
|
|
13
|
+
validates every preflight, executes approved publish operations, and verifies
|
|
14
|
+
remote state in order. The primitive commands remain available for review and
|
|
15
|
+
debug flows:
|
|
16
|
+
|
|
7
17
|
```sh
|
|
8
18
|
bun run cli plan --config release.config.json --format text
|
|
9
19
|
bun run cli render --config release.config.json --execute
|
|
@@ -11,9 +21,24 @@ bun run cli validate --config release.config.json
|
|
|
11
21
|
bun run cli print --config release.config.json
|
|
12
22
|
bun run cli execute --config release.config.json --execute --approve-irreversible
|
|
13
23
|
bun run cli verify --config release.config.json
|
|
24
|
+
bun run cli eligibility --config release.config.json --format text
|
|
25
|
+
bun run cli reconcile --config release.config.json --execute
|
|
14
26
|
```
|
|
15
27
|
|
|
16
|
-
Rendering writes generated target files locally and records `render.json` evidence. Publishing is blocked unless execution is explicitly approved. Irreversible operations require a second approval flag.
|
|
28
|
+
Rendering writes generated target files locally and records `render.json` evidence. `execute` is a lower-level primitive that runs publish operations only. Publishing is blocked unless execution is explicitly approved. Irreversible operations require a second approval flag.
|
|
29
|
+
|
|
30
|
+
Status and resume commands use existing `.release/evidence` files to report progress and continue conservative unfinished work:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
bun run cli status --config release.config.json --format text
|
|
34
|
+
bun run cli resume --config release.config.json --execute --approve-irreversible
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Resume skips operations with successful matching evidence, reruns safe read-only failures, and blocks failed publish operations until remote state is reconciled manually.
|
|
38
|
+
`eligibility` checks npm and GitHub remote state before the self-release workflow decides whether to run.
|
|
39
|
+
`reconcile` is separate from resume: it inspects GitHub release state through the API and can publish a matching draft release with explicit `--execute` without republishing immutable npm versions.
|
|
40
|
+
|
|
41
|
+
The executable is an argv and console adapter over TypeScript workflows. Release workflows are modeled as typed functions first, then exposed through the CLI for terminal and CI usage.
|
|
17
42
|
|
|
18
43
|
## Imports
|
|
19
44
|
|
|
@@ -21,12 +46,10 @@ The package intentionally avoids aggregate library barrels. The root `release` e
|
|
|
21
46
|
|
|
22
47
|
```ts
|
|
23
48
|
import * as Effect from "effect/Effect"
|
|
24
|
-
import * as Layer from "effect/Layer"
|
|
25
49
|
import { type ReleaseIntent } from "@mannyc1/ts-release/domain/release"
|
|
26
|
-
import { BunReleaseHostLayer } from "@mannyc1/ts-release/host/bun"
|
|
27
50
|
import { createReleasePlan } from "@mannyc1/ts-release/planner/create-release-plan"
|
|
28
51
|
import { validatePlan } from "@mannyc1/ts-release/planner/executor"
|
|
29
|
-
import {
|
|
52
|
+
import { makeBunReleaseWorkflowRuntimeLayer } from "@mannyc1/ts-release/runtime/bun"
|
|
30
53
|
|
|
31
54
|
const planAndValidate = (intent: ReleaseIntent) =>
|
|
32
55
|
Effect.gen(function*() {
|
|
@@ -34,46 +57,109 @@ const planAndValidate = (intent: ReleaseIntent) =>
|
|
|
34
57
|
const evidence = yield* validatePlan(plan)
|
|
35
58
|
return { plan, evidence }
|
|
36
59
|
}).pipe(
|
|
37
|
-
Effect.provide(
|
|
60
|
+
Effect.provide(makeBunReleaseWorkflowRuntimeLayer())
|
|
38
61
|
)
|
|
39
62
|
```
|
|
40
63
|
|
|
41
|
-
`createReleasePlan` needs a `TargetRegistry` layer. Workflows that
|
|
64
|
+
`createReleasePlan` needs a `TargetRegistry` layer. Command execution needs a `ReleaseCommandRunner` layer, while artifact checks and checksum generation use Effect Platform `FileSystem`, `Path`, and `Crypto` services directly. High-level config-file workflows, render writes, and evidence writes also need Effect Platform `FileSystem` and `Path` services. Workflows that verify HTTP evidence, such as `verifyPlan`, `runApprovedReleaseWorkflow`, or direct `VerifyHttpOperation` execution, also need a `ReleaseHttp` layer. `makeBunReleaseWorkflowRuntimeLayer` is the Bun runtime composition used by the published CLI; applications on other runtimes can compose `@mannyc1/ts-release/workflows/live`, `@mannyc1/ts-release/host/platform`, and their platform services at the edge. Tests can import `makeTestReleaseHttpLayer` from `@mannyc1/ts-release/host/http`. Internal Effect imports use deep module paths such as `effect/Effect` and `effect/Layer` to keep bundlers from depending on broad root-package analysis. See `ARCHITECTURE.md` for the module taxonomy.
|
|
42
65
|
|
|
43
|
-
##
|
|
66
|
+
## TypeScript Workflows
|
|
44
67
|
|
|
45
|
-
Applications can
|
|
68
|
+
Applications can call high-level release workflows without argv arrays or CLI command names:
|
|
46
69
|
|
|
47
70
|
```ts
|
|
48
71
|
import * as Effect from "effect/Effect"
|
|
72
|
+
import { makeBunReleaseWorkflowRuntimeLayer } from "@mannyc1/ts-release/runtime/bun"
|
|
49
73
|
import {
|
|
50
74
|
PlanReleaseConfigOptions,
|
|
75
|
+
ReleaseExecutionOptions,
|
|
76
|
+
ReleaseEligibilityConfigOptions,
|
|
77
|
+
ReleaseReconcileConfigOptions,
|
|
78
|
+
ReleaseResumeConfigOptions,
|
|
79
|
+
ReleaseStatusOptions,
|
|
80
|
+
checkReleaseConfigEligibility,
|
|
81
|
+
reconcileReleaseConfig,
|
|
51
82
|
planReleaseConfig,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
83
|
+
renderReleaseConfigPlan,
|
|
84
|
+
resumeReleaseConfig,
|
|
85
|
+
runReleaseConfig,
|
|
86
|
+
statusReleaseConfig
|
|
87
|
+
} from "@mannyc1/ts-release/workflows/config"
|
|
88
|
+
|
|
89
|
+
const root = "/path/to/release-workspace"
|
|
90
|
+
const RuntimeLayer = makeBunReleaseWorkflowRuntimeLayer({ root })
|
|
91
|
+
|
|
92
|
+
const textPlan = await Effect.runPromise(
|
|
93
|
+
renderReleaseConfigPlan(
|
|
94
|
+
PlanReleaseConfigOptions.make({ root, configPath: "release.config.json", format: "text" })
|
|
95
|
+
).pipe(Effect.provide(RuntimeLayer))
|
|
64
96
|
)
|
|
65
97
|
|
|
66
98
|
const plan = await Effect.runPromise(
|
|
67
|
-
planReleaseConfig(
|
|
68
|
-
|
|
69
|
-
|
|
99
|
+
planReleaseConfig(PlanReleaseConfigOptions.make({ root, configPath: "release.config.json" })).pipe(
|
|
100
|
+
Effect.provide(RuntimeLayer)
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
const evidence = await Effect.runPromise(
|
|
105
|
+
runReleaseConfig(
|
|
106
|
+
ReleaseExecutionOptions.make({
|
|
107
|
+
root,
|
|
108
|
+
configPath: "release.config.json",
|
|
109
|
+
execute: true,
|
|
110
|
+
approveIrreversible: true
|
|
111
|
+
})
|
|
112
|
+
).pipe(
|
|
113
|
+
Effect.provide(RuntimeLayer)
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
const status = await Effect.runPromise(
|
|
118
|
+
statusReleaseConfig(
|
|
119
|
+
ReleaseStatusOptions.make({ root, configPath: "release.config.json", format: "json" })
|
|
120
|
+
).pipe(
|
|
121
|
+
Effect.provide(RuntimeLayer)
|
|
122
|
+
)
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
const resumedEvidence = await Effect.runPromise(
|
|
126
|
+
resumeReleaseConfig(
|
|
127
|
+
ReleaseResumeConfigOptions.make({
|
|
128
|
+
root,
|
|
129
|
+
configPath: "release.config.json",
|
|
130
|
+
execute: true,
|
|
131
|
+
approveIrreversible: true
|
|
132
|
+
})
|
|
133
|
+
).pipe(
|
|
134
|
+
Effect.provide(RuntimeLayer)
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
const eligibility = await Effect.runPromise(
|
|
139
|
+
checkReleaseConfigEligibility(
|
|
140
|
+
ReleaseEligibilityConfigOptions.make({
|
|
141
|
+
root,
|
|
70
142
|
configPath: "release.config.json"
|
|
71
143
|
})
|
|
144
|
+
).pipe(
|
|
145
|
+
Effect.provide(RuntimeLayer)
|
|
146
|
+
)
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
const reconciliationEvidence = await Effect.runPromise(
|
|
150
|
+
reconcileReleaseConfig(
|
|
151
|
+
ReleaseReconcileConfigOptions.make({
|
|
152
|
+
root,
|
|
153
|
+
configPath: "release.config.json",
|
|
154
|
+
execute: true
|
|
155
|
+
})
|
|
156
|
+
).pipe(
|
|
157
|
+
Effect.provide(RuntimeLayer)
|
|
72
158
|
)
|
|
73
159
|
)
|
|
74
160
|
```
|
|
75
161
|
|
|
76
|
-
|
|
162
|
+
Use `@mannyc1/ts-release/workflows/config` for high-level config-file workflows, `@mannyc1/ts-release/workflows/evidence` for reusable evidence persistence, and `@mannyc1/ts-release/workflows/live` for the live target/HTTP layer. Bun callers can use `@mannyc1/ts-release/runtime/bun` for the same runtime layer used by the published binary. Node callers can provide Node platform services and a Node HTTP client instead. Use explicit lower-level planner, config, target, host, and domain subpaths when an application needs finer control over planning, execution, or test layers.
|
|
77
163
|
|
|
78
164
|
## Example Config
|
|
79
165
|
|
|
@@ -105,10 +191,15 @@ The helper provides the Bun host and live target registry internally, so callers
|
|
|
105
191
|
"_tag": "NpmRegistryTarget",
|
|
106
192
|
"id": "npm",
|
|
107
193
|
"registry": "https://registry.npmjs.org",
|
|
194
|
+
"packageName": "@mannyc1/ts-release",
|
|
108
195
|
"packagePath": ".",
|
|
109
|
-
"
|
|
196
|
+
"trustedPublishing": {
|
|
197
|
+
"provider": "github-actions",
|
|
198
|
+
"workflow": "release.yml",
|
|
199
|
+
"packageExists": true,
|
|
200
|
+
"verifyPackageExists": true
|
|
201
|
+
},
|
|
110
202
|
"access": "public",
|
|
111
|
-
"provenance": true,
|
|
112
203
|
"dryRunSupport": "native",
|
|
113
204
|
"mutability": "immutable",
|
|
114
205
|
"recovery": "publish-new-version"
|
|
@@ -117,18 +208,19 @@ The helper provides the Bun host and live target registry internally, so callers
|
|
|
117
208
|
"_tag": "GitHubReleaseTarget",
|
|
118
209
|
"id": "github",
|
|
119
210
|
"repository": "owner/repo",
|
|
211
|
+
"tokenEnv": "GH_TOKEN",
|
|
120
212
|
"draft": true,
|
|
121
|
-
"dryRunSupport": "
|
|
213
|
+
"dryRunSupport": "simulated",
|
|
122
214
|
"mutability": "mutable-release",
|
|
123
215
|
"recovery": "delete-and-recreate"
|
|
124
216
|
}
|
|
125
217
|
],
|
|
126
218
|
"strict": true,
|
|
127
|
-
"evidenceDirectory": ".release/evidence"
|
|
219
|
+
"evidenceDirectory": ".release/evidence/{version}"
|
|
128
220
|
}
|
|
129
221
|
```
|
|
130
222
|
|
|
131
|
-
Paths are release-workspace relative and may not be absolute or contain parent traversal.
|
|
223
|
+
Paths are release-workspace relative and may not be absolute or contain parent traversal. `evidenceDirectory` may include the literal `{version}` placeholder, which is resolved during planning so each release version can use its own evidence directory.
|
|
132
224
|
|
|
133
225
|
Homebrew tap targets model catalog updates as generated files plus an approval-gated push:
|
|
134
226
|
|
|
@@ -148,32 +240,77 @@ Homebrew tap targets model catalog updates as generated files plus an approval-g
|
|
|
148
240
|
}
|
|
149
241
|
```
|
|
150
242
|
|
|
151
|
-
|
|
243
|
+
PyPI registry targets coordinate already-built Python distributions through Twine. They do not build wheels or sdists:
|
|
244
|
+
|
|
245
|
+
```json
|
|
246
|
+
{
|
|
247
|
+
"_tag": "PyPiRegistryTarget",
|
|
248
|
+
"id": "pypi",
|
|
249
|
+
"repositoryUrl": "https://test.pypi.org/legacy/",
|
|
250
|
+
"usernameEnv": "TWINE_USERNAME",
|
|
251
|
+
"passwordEnv": "TWINE_PASSWORD",
|
|
252
|
+
"dryRunSupport": "native",
|
|
253
|
+
"mutability": "immutable",
|
|
254
|
+
"recovery": "publish-new-version"
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Use `TWINE_USERNAME` and `TWINE_PASSWORD` for token-based local publishing so secrets stay in environment variables rather than command arguments. PyPI Trusted Publishing belongs at the CI/auth layer; this adapter records Twine commands and their auth requirements. TestPyPI is a real registry publish target, not a dry-run.
|
|
259
|
+
|
|
260
|
+
Scoop bucket targets model Windows installer catalog updates as generated JSON manifests plus an approval-gated push:
|
|
261
|
+
|
|
262
|
+
```json
|
|
263
|
+
{
|
|
264
|
+
"_tag": "ScoopBucketTarget",
|
|
265
|
+
"id": "scoop",
|
|
266
|
+
"repository": "owner/scoop-bucket",
|
|
267
|
+
"manifestName": "release",
|
|
268
|
+
"manifestPath": ".release/generated/release.json",
|
|
269
|
+
"artifactId": "github-asset",
|
|
270
|
+
"url": "https://github.com/owner/repo/releases/download/v0.1.0/mannyc1-ts-release-0.1.0.zip",
|
|
271
|
+
"bin": "release.exe",
|
|
272
|
+
"dryRunSupport": "simulated",
|
|
273
|
+
"mutability": "mutable-index",
|
|
274
|
+
"recovery": "manual"
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Tap and bucket pushes use the Git credentials configured for the local checkout; `tokenEnv` is not supported for these catalog targets yet.
|
|
279
|
+
|
|
280
|
+
Use `run --execute --approve-irreversible` for the ordered release workflow, or use `plan`, `render --execute`, `validate`, `print`, `execute --execute`, and `verify` separately when generated catalog files need a manual review pause before any tap or bucket update is pushed.
|
|
152
281
|
|
|
153
282
|
## Plan Review
|
|
154
283
|
|
|
155
|
-
Text plans include the release identity, evidence directory, artifact inventory, target capabilities, operation commands, validation notes, and execution gates.
|
|
284
|
+
Text plans include the release identity, evidence directory, artifact inventory, target capabilities, operation commands, HTTP verification requests, validation notes, and execution gates. Command operations include a human command summary plus an `argv:` JSON array that preserves exact argument boundaries for review.
|
|
156
285
|
|
|
157
286
|
```text
|
|
158
287
|
@mannyc1/ts-release@0.1.0
|
|
159
288
|
commit: abc123
|
|
160
|
-
evidence: .release/evidence
|
|
289
|
+
evidence: .release/evidence/0.1.0
|
|
161
290
|
artifacts: 2
|
|
162
291
|
targets: 2
|
|
163
|
-
operations:
|
|
292
|
+
operations: 9
|
|
164
293
|
|
|
165
294
|
targets:
|
|
166
|
-
- github [GitHubReleaseTarget] auth=
|
|
167
|
-
- npm [NpmRegistryTarget] auth=
|
|
295
|
+
- github [GitHubReleaseTarget] auth=env-token dry-run=simulated strategy=simulated-plan mutability=mutable-release recovery=delete-and-recreate
|
|
296
|
+
- npm [NpmRegistryTarget] auth=trusted-publishing runs-in=ci provider=github-actions workflow=release.yml required-permission=id-token:write package-prerequisite=exists dry-run=native strategy=native-command mutability=immutable recovery=publish-new-version
|
|
168
297
|
```
|
|
169
298
|
|
|
170
299
|
JSON plans include the same data in a stable, CI-artifact-friendly shape, including `targetCapabilities`.
|
|
171
300
|
|
|
172
|
-
GitHub release verification
|
|
301
|
+
GitHub release verification uses the GitHub REST API to check the release tag, title, draft flag, prerelease flag, and each uploaded artifact name.
|
|
302
|
+
|
|
303
|
+
## Status and Resume
|
|
304
|
+
|
|
305
|
+
`status` reads local phase evidence and reports each current operation as pending, passed, failed, blocked, or complete without executing anything. The JSON format is schema-backed for CI or dashboards; the text format is intended for terminal review.
|
|
306
|
+
|
|
307
|
+
`resume` is intentionally conservative. It skips successful matching evidence, can rerun missing work and failed read-only validation or verification operations, and never reruns a failed publish operation. A failed publish command can still have changed the outside world, so resume blocks until a maintainer reconciles npm, GitHub, or any other remote state manually.
|
|
308
|
+
|
|
309
|
+
`reconcile` is the narrow remote repair path for GitHub Releases. It reads the GitHub release by tag, blocks on mismatched metadata or assets, skips an already matching published release, and can run `gh release edit <tag> --draft=false` for a matching draft when the target expects a public release. It does not run `npm publish`.
|
|
173
310
|
|
|
174
311
|
## Public API
|
|
175
312
|
|
|
176
|
-
The intentional public API is the explicit subpath list in `package.json`. `release/cli` and `release/cli/command`
|
|
313
|
+
The intentional public API is the explicit subpath list in `package.json`. The npm package exposes the `release` executable through `bin`; programmatic callers should import high-level workflows from `@mannyc1/ts-release/workflows/config` or use the explicit lower-level planner/config/target/status subpaths for finer control. `release/cli` and `release/cli/command` remain public for applications that need to embed the CLI command adapter.
|
|
177
314
|
|
|
178
315
|
The package export checker fails if a new export is added without being added to the intentional API list.
|
|
179
316
|
|
|
@@ -181,22 +318,26 @@ The package export checker fails if a new export is added without being added to
|
|
|
181
318
|
|
|
182
319
|
Runnable example configs live in `examples/`:
|
|
183
320
|
|
|
321
|
+
- `examples/multi-target`
|
|
184
322
|
- `examples/npm-only`
|
|
323
|
+
- `examples/npm-first-publish`
|
|
185
324
|
- `examples/github-release`
|
|
186
325
|
- `examples/homebrew-tap`
|
|
326
|
+
- `examples/pypi-registry`
|
|
327
|
+
- `examples/scoop-bucket`
|
|
187
328
|
- `examples/non-strict-skips`
|
|
188
329
|
|
|
189
|
-
Build the package first, then plan an example from its directory:
|
|
330
|
+
`examples/multi-target` demonstrates one release coordinated across a release host, a package registry, and an installer catalog. Build the package first, then plan an example from its directory:
|
|
190
331
|
|
|
191
332
|
```sh
|
|
192
333
|
bun run build
|
|
193
|
-
cd examples/
|
|
334
|
+
cd examples/multi-target
|
|
194
335
|
bun ../../dist/cli/main.js plan --config release.config.json --format text
|
|
195
336
|
```
|
|
196
337
|
|
|
197
338
|
## Evidence
|
|
198
339
|
|
|
199
|
-
|
|
340
|
+
Render, validation, execution, and verification evidence is written as JSON bundles. Failed commands still preserve partial evidence before the command failure is returned. Use `evidenceDirectory` such as `.release/evidence/{version}` when older local evidence should not collide with the current release version.
|
|
200
341
|
|
|
201
342
|
```json
|
|
202
343
|
{
|
|
@@ -234,16 +375,75 @@ bun run test:integration:tools
|
|
|
234
375
|
RELEASE_INTEGRATION_GITHUB=1 bun run test:integration:tools
|
|
235
376
|
```
|
|
236
377
|
|
|
237
|
-
The first command validates npm adapter operations against the real `npm` CLI. The second also validates GitHub adapter readiness checks against the real `gh` CLI and requires `gh auth status` to succeed.
|
|
378
|
+
The first command validates npm adapter operations against the real `npm` CLI. The second also validates GitHub adapter readiness checks against the real `gh` CLI and requires `gh auth status` to succeed. GitHub release creation itself has no native dry-run; release validation is simulated from the deterministic plan before publish and verified against GitHub only after publish.
|
|
238
379
|
|
|
239
|
-
Example configs are checked through the
|
|
380
|
+
Example configs are checked through the TypeScript workflow path:
|
|
240
381
|
|
|
241
382
|
```sh
|
|
242
383
|
bun run check:examples
|
|
243
384
|
```
|
|
244
385
|
|
|
245
|
-
This repository also includes a first release config at `release.config.json` that targets both npm and GitHub for the scoped `@mannyc1/ts-release` package. The self-release config must pass `bun run check:self-release-config` before release checks proceed. Its `identity.commit` may be the explicit current short commit, or `HEAD`
|
|
386
|
+
This repository also includes a first release config at `release.config.json` that targets both npm and GitHub for the scoped `@mannyc1/ts-release` package. The self-release config must pass `bun run check:self-release-config` before release checks proceed. Its `identity.commit` may be the explicit current short commit, or `HEAD` as a stored-config convenience. Generated plans resolve `HEAD` to the current short commit, and the self-release guard requires a committed Git checkout with clean tracked files.
|
|
246
387
|
|
|
247
388
|
```sh
|
|
248
389
|
bun run check:self-release-config
|
|
249
390
|
```
|
|
391
|
+
|
|
392
|
+
### Self Release
|
|
393
|
+
|
|
394
|
+
The local non-publish gates for this package are:
|
|
395
|
+
|
|
396
|
+
```sh
|
|
397
|
+
bun run check:release
|
|
398
|
+
bun run release:artifacts
|
|
399
|
+
bun dist/cli/main.js plan --config release.config.json --format text
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
`release:artifacts` writes ignored files under `.release/artifacts`: the npm package tarball and standalone CLI executables for Linux, macOS, and Windows. GitHub Actions runs the approved release workflow on protected `main` when the package version has not already been published. The workflow uses npm trusted publishing with GitHub Actions OIDC instead of an npm token, and uploads `.release/evidence/**` for audit.
|
|
403
|
+
|
|
404
|
+
### Local Release Auth
|
|
405
|
+
|
|
406
|
+
Use `.env.example` as the local credential contract. Export `GH_TOKEN`, or copy
|
|
407
|
+
`.env.example` to `.env` and fill in token values locally. `.env` and `.npmrc`
|
|
408
|
+
are ignored intentionally; keep token values out of commits. `.npmrc.example`
|
|
409
|
+
shows npm's `${NPM_TOKEN}` interpolation form for token-based npm targets.
|
|
410
|
+
|
|
411
|
+
For npmjs releases from GitHub Actions, prefer structured `trustedPublishing` on
|
|
412
|
+
the npm target:
|
|
413
|
+
|
|
414
|
+
```json
|
|
415
|
+
{
|
|
416
|
+
"packageName": "@mannyc1/ts-release",
|
|
417
|
+
"trustedPublishing": {
|
|
418
|
+
"provider": "github-actions",
|
|
419
|
+
"workflow": "release.yml",
|
|
420
|
+
"packageExists": true,
|
|
421
|
+
"verifyPackageExists": true
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
Trusted publishing authenticates during `npm publish` with CI OIDC, so
|
|
427
|
+
`ts-release` records that mode in validation evidence instead of running
|
|
428
|
+
`npm whoami`, which does not validate OIDC publishing. Configure npmjs trusted
|
|
429
|
+
publishing for an existing package and use a GitHub-hosted runner with
|
|
430
|
+
`id-token: write`, Node 22.14+ and npm 11.5.1+. The `packageExists` field must be
|
|
431
|
+
`true` as a precondition acknowledgement, not first-publish support. Setting
|
|
432
|
+
`verifyPackageExists` to `true` adds a read-only `npm view <package>` validation
|
|
433
|
+
check. Trusted publishing does not use `NPM_TOKEN` for `npm publish`; token-based
|
|
434
|
+
npm targets may still use `.npmrc.example` and `NPM_TOKEN`.
|
|
435
|
+
|
|
436
|
+
### First npm Publish Bootstrap
|
|
437
|
+
|
|
438
|
+
Trusted publishing is the preferred steady state for GitHub Actions, but npm
|
|
439
|
+
requires the package to exist before trusted publishing can be configured. For a
|
|
440
|
+
new package, use a temporary token-based npm target with `tokenEnv: "NPM_TOKEN"`
|
|
441
|
+
and no `trustedPublishing` object. After the first version exists on npm,
|
|
442
|
+
configure npm trusted publishing for owner `mannyc2`, repository `ts-release`,
|
|
443
|
+
and workflow filename `release.yml`, then switch the target to
|
|
444
|
+
`trustedPublishing`.
|
|
445
|
+
|
|
446
|
+
Do not commit token values. Keep `.env.example` and `.npmrc.example` as
|
|
447
|
+
placeholder contracts only.
|
|
448
|
+
|
|
449
|
+
The first-release GitHub target uses `GH_TOKEN` for both `gh` command authentication and read-only REST API verification. The release workflow sets up a current Node/npm toolchain for trusted publishing and enables npm provenance for CI-based publishes where the registry can generate provenance.
|
package/SPEC.md
CHANGED
|
@@ -19,6 +19,7 @@ release intent
|
|
|
19
19
|
-> validation evidence
|
|
20
20
|
-> gated execution
|
|
21
21
|
-> post-publish verification
|
|
22
|
+
-> status and conservative resume
|
|
22
23
|
```
|
|
23
24
|
|
|
24
25
|
The package owns the model and the orchestration. Ecosystem tools remain the source of truth for ecosystem-specific behavior.
|
|
@@ -51,7 +52,7 @@ Examples:
|
|
|
51
52
|
- A catalog update changes a repository or index that points at artifacts.
|
|
52
53
|
- A deployment promotes already-built assets into an environment.
|
|
53
54
|
|
|
54
|
-
Each target should declare its required inputs, auth requirements, dry-run support, validation strategy, mutability rules, and recovery behavior.
|
|
55
|
+
Each target should declare its required inputs, auth requirements, dry-run support, validation strategy, mutability rules, and recovery behavior. When auth cannot be proven locally, the target should also model the expected execution context, provider-specific setup, and setup prerequisites.
|
|
55
56
|
|
|
56
57
|
### Evidence-driven validation
|
|
57
58
|
|
|
@@ -59,6 +60,8 @@ Validation should produce structured evidence, not just console output.
|
|
|
59
60
|
|
|
60
61
|
Evidence should be machine-readable enough for CI and human-readable enough for debugging. It should include command invocations, tool versions where practical, exit statuses, important paths, skipped checks, warnings, failures, and timestamps.
|
|
61
62
|
|
|
63
|
+
Evidence should also support status reporting and conservative resume after failed or interrupted releases.
|
|
64
|
+
|
|
62
65
|
Strict mode should fail on missing required validators. Non-strict mode may record skips, but skips must be visible in the evidence.
|
|
63
66
|
|
|
64
67
|
### Gated irreversible actions
|
|
@@ -67,6 +70,8 @@ Operations that publish immutable versions, create public releases, overwrite in
|
|
|
67
70
|
|
|
68
71
|
The default behavior should be dry-run or print-only. Execution should require an explicit execute flag, and irreversible operations should require a second confirmation flag or equivalent programmatic approval.
|
|
69
72
|
|
|
73
|
+
Failed publish evidence must not be treated as proof that nothing was published.
|
|
74
|
+
|
|
70
75
|
The package should make it hard to accidentally publish and easy to see exactly what would be published.
|
|
71
76
|
|
|
72
77
|
### Library-first, CLI-second
|
|
@@ -82,6 +87,8 @@ The library should expose APIs for:
|
|
|
82
87
|
- preparing executable operations
|
|
83
88
|
- running approved operations through an injected host interface
|
|
84
89
|
- recording evidence
|
|
90
|
+
- reporting release status from evidence
|
|
91
|
+
- conservatively resuming safe unfinished work
|
|
85
92
|
|
|
86
93
|
The CLI should mainly parse arguments, call the library, and format output.
|
|
87
94
|
|
package/dist/cli/command.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import * as Command from "effect/unstable/cli/Command";
|
|
2
2
|
export type * from "../types/effect-internal.js";
|
|
3
|
-
export declare const cli: Command.Command<"release", {}, {}, import("../
|
|
3
|
+
export declare const cli: Command.Command<"release", {}, {}, import("effect/PlatformError").PlatformError | import("../config/errors.js").ConfigReadError | import("../config/errors.js").ConfigParseError | import("../config/errors.js").ConfigValidationError | import("../domain/operation.js").ExecutionApprovalError | import("../planner/errors.js").ReleaseNormalizationError | import("../planner/errors.js").PlanConstructionError | import("../planner/errors.js").EvidenceWriteError | import("../planner/errors.js").EvidenceReadError | import("../planner/errors.js").WorkspaceWriteError | import("../planner/errors.js").ResumeBlockedError | import("../planner/errors.js").RemoteStateInspectionError | import("../planner/errors.js").ReleaseEligibilityCheckError | import("../planner/errors.js").ReconciliationBlockedError | import("../planner/errors.js").OperationFailedError | import("../targets/registry.js").MissingTargetAdapterError | import("../host/host.js").CommandRunnerError, never>;
|
|
4
4
|
//# sourceMappingURL=command.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/cli/command.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/cli/command.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,OAAO,MAAM,6BAA6B,CAAA;AAqCtD,mBAAmB,6BAA6B,CAAA;AAiQhD,eAAO,MAAM,GAAG,o8BAcf,CAAA"}
|