@git.zone/cli 6.0.1 → 6.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/helpers.workflow.d.ts +2 -0
- package/dist_ts/helpers.workflow.js +96 -25
- package/dist_ts/mod_config/index.js +176 -88
- package/dist_ts/mod_release/classes.releasejournal.d.ts +60 -10
- package/dist_ts/mod_release/classes.releasejournal.js +513 -21
- package/dist_ts/mod_release/helpers.releasepublication.d.ts +5 -3
- package/dist_ts/mod_release/helpers.releasepublication.js +390 -13
- package/dist_ts/mod_release/helpers.tsdockerprotocol.d.ts +60 -0
- package/dist_ts/mod_release/helpers.tsdockerprotocol.js +442 -0
- package/dist_ts/mod_release/index.d.ts +1 -1
- package/dist_ts/mod_release/index.js +101 -19
- package/dist_ts/plugins.d.ts +5 -1
- package/dist_ts/plugins.js +5 -2
- package/package.json +2 -1
- package/readme.hints.md +43 -17
- package/readme.md +83 -75
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/helpers.workflow.ts +191 -49
- package/ts/mod_config/index.ts +320 -146
- package/ts/mod_release/classes.releasejournal.ts +939 -70
- package/ts/mod_release/helpers.releasepublication.ts +786 -49
- package/ts/mod_release/helpers.tsdockerprotocol.ts +806 -0
- package/ts/mod_release/index.ts +231 -74
- package/ts/plugins.ts +20 -0
package/readme.md
CHANGED
|
@@ -50,21 +50,21 @@ gitzone release
|
|
|
50
50
|
|
|
51
51
|
## Commands
|
|
52
52
|
|
|
53
|
-
| Command
|
|
54
|
-
|
|
|
55
|
-
| `commit`
|
|
56
|
-
| `release`
|
|
57
|
-
| `format`
|
|
58
|
-
| `config`
|
|
59
|
-
| `services`
|
|
60
|
-
| `tools`
|
|
61
|
-
| `template`
|
|
62
|
-
| `meta`
|
|
63
|
-
| `open`
|
|
64
|
-
| `docker`
|
|
65
|
-
| `deprecate` | Deprecate npm packages across registries
|
|
66
|
-
| `start`
|
|
67
|
-
| `helpers`
|
|
53
|
+
| Command | Purpose |
|
|
54
|
+
| ----------- | --------------------------------------------------------------------------- |
|
|
55
|
+
| `commit` | Analyze changes and create one semantic source commit |
|
|
56
|
+
| `release` | Turn pending changelog entries into a versioned release and publish targets |
|
|
57
|
+
| `format` | Plan or apply project formatting and standardization |
|
|
58
|
+
| `config` | Inspect, update, and migrate `.smartconfig.json` |
|
|
59
|
+
| `services` | Manage local MongoDB, ObjectStorage, and Elasticsearch containers |
|
|
60
|
+
| `tools` | Manage the global `@git.zone` toolchain |
|
|
61
|
+
| `template` | Scaffold projects from built-in templates |
|
|
62
|
+
| `meta` | Manage multi-repository workspaces |
|
|
63
|
+
| `open` | Open repository assets like CI pages |
|
|
64
|
+
| `docker` | Report and reclaim Docker resources created by git.zone tooling |
|
|
65
|
+
| `deprecate` | Deprecate npm packages across registries |
|
|
66
|
+
| `start` | Prepare an existing project for local work |
|
|
67
|
+
| `helpers` | Run small helper utilities |
|
|
68
68
|
|
|
69
69
|
Global flags include `--help`, `--json`, `--plain`, `--agent`, `--no-interactive`, and `--no-check-updates`.
|
|
70
70
|
|
|
@@ -119,15 +119,15 @@ The commit flow:
|
|
|
119
119
|
|
|
120
120
|
Commit flags:
|
|
121
121
|
|
|
122
|
-
| Flag
|
|
123
|
-
|
|
|
124
|
-
| `-y`, `--yes`
|
|
122
|
+
| Flag | Meaning |
|
|
123
|
+
| ------------------ | -------------------------------------------------------------- |
|
|
124
|
+
| `-y`, `--yes` | Auto-accept safe recommendations |
|
|
125
125
|
| `--allow-breaking` | Allow `-y`/`--yes` to accept `BREAKING CHANGE` recommendations |
|
|
126
|
-
| `-t`, `--test`
|
|
127
|
-
| `-b`, `--build`
|
|
128
|
-
| `-p`, `--push`
|
|
129
|
-
| `-f`, `--format`
|
|
130
|
-
| `--plan`
|
|
126
|
+
| `-t`, `--test` | Add test step |
|
|
127
|
+
| `-b`, `--build` | Add build step |
|
|
128
|
+
| `-p`, `--push` | Push after the source commit |
|
|
129
|
+
| `-f`, `--format` | Run `gitzone format --write` before commit |
|
|
130
|
+
| `--plan` | Show resolved workflow only |
|
|
131
131
|
|
|
132
132
|
`-r` is intentionally not part of commit anymore. Use `gitzone release`.
|
|
133
133
|
|
|
@@ -137,7 +137,9 @@ Commit flags:
|
|
|
137
137
|
|
|
138
138
|
`--merge` is intentionally strict: the source and existing `main` worktree must be clean, the source must be linearly rebased onto current remote `main`, and the Git target must push both the branch and tag. GitZone pins the single configured push URL, compare-and-swap fast-forwards local `main`, updates its verified worktree, and lease-pushes that exact source commit before creating release metadata. It rejects diverged or merged history, local replacement refs or grafts, stale worktrees, incomplete shallow history, changed destinations, and remote races. If the pre-release push fails, GitZone restores local `main` only when doing so cannot overwrite concurrent work. This plumbing-level integration intentionally does not run merge hooks or write `ORIG_HEAD`.
|
|
139
139
|
|
|
140
|
-
`gitzone release` performs the release core once, then publishes to configured targets. An active Git target must succeed and be verified before npm publication can start.
|
|
140
|
+
`gitzone release` performs the release core once, then publishes to configured targets. When Docker is active, its immutable candidate must qualify before any Git, npm, or OCI destination publication. An active Git target must then succeed and be verified before npm or OCI publication can start.
|
|
141
|
+
|
|
142
|
+
Before interactive confirmation or source mutation, a Docker release verifies the project-local tSDocker capabilities and validates the deterministic request against the source commit. After the release commit and configured build, GitZone validates the final request against the release commit immediately before atomically installing the schema-2 journal.
|
|
141
143
|
|
|
142
144
|
The release core is not configurable plumbing. It always follows the same professional release transaction:
|
|
143
145
|
|
|
@@ -149,15 +151,18 @@ The release core is not configurable plumbing. It always follows the same profes
|
|
|
149
151
|
6. Move pending changelog entries into the new version section.
|
|
150
152
|
7. Create the local release commit and tag on `main`.
|
|
151
153
|
8. Run the configured release build, require a clean tree, and revalidate the release checkout.
|
|
152
|
-
9. When npm is selected, pack it once into an exact tarball.
|
|
154
|
+
9. When npm is selected, pack it once into an exact tarball.
|
|
155
|
+
10. Atomically install a durable journal before final publication. Non-Docker releases use journal schema 1; Docker releases use schema 2 in the same storage tree.
|
|
156
|
+
11. For schema 2, run the project-local tSDocker qualification build and configured image tests, then durably record the candidate digest graph and complete ordered promotion set.
|
|
157
|
+
12. Publish and verify Git, then each npm registry, then each Docker destination. Remove only the exact qualified candidate after every destination is verified, or after a terminal qualification destination conflict before Git/npm publication.
|
|
153
158
|
|
|
154
159
|
Targets decide what happens after that:
|
|
155
160
|
|
|
156
|
-
| Target
|
|
157
|
-
|
|
|
158
|
-
| `git`
|
|
159
|
-
| `npm`
|
|
160
|
-
| `docker` |
|
|
161
|
+
| Target | What it does |
|
|
162
|
+
| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
163
|
+
| `git` | Atomically pushes the exact `main` release commit and new tag, often triggering remote CI release builds |
|
|
164
|
+
| `npm` | Publishes the same journaled tarball to every configured registry and verifies its bytes anonymously |
|
|
165
|
+
| `docker` | Qualifies immutable OCI digest graphs with project-local tSDocker, promotes each journaled destination under single-writer alias fencing, verifies exact digests, then cleans the candidate; a terminal qualification conflict permits cleanup only |
|
|
161
166
|
|
|
162
167
|
```bash
|
|
163
168
|
# Preview the resolved release plan
|
|
@@ -181,38 +186,38 @@ gitzone release --minor
|
|
|
181
186
|
|
|
182
187
|
Release flags:
|
|
183
188
|
|
|
184
|
-
| Flag
|
|
185
|
-
|
|
|
186
|
-
| `-y`, `--yes`
|
|
187
|
-
| `-t`, `--test`
|
|
188
|
-
| `-b`, `--build`
|
|
189
|
-
| `-p`, `--push`
|
|
190
|
-
| `--target <csv>`
|
|
191
|
-
| `--npm`
|
|
192
|
-
| `--docker`
|
|
193
|
-
| `--no-publish`
|
|
194
|
-
| `--no-build`
|
|
195
|
-
| `--merge`
|
|
196
|
-
| `--major`, `--minor`, `--patch` | Override inferred semver level
|
|
197
|
-
| `--plan`
|
|
198
|
-
| `inspect [version]`
|
|
199
|
-
| `resume <version>`
|
|
200
|
-
| `--recover-attempt <id>`
|
|
201
|
-
|
|
202
|
-
### Exact
|
|
203
|
-
|
|
204
|
-
Journal schema
|
|
189
|
+
| Flag | Meaning |
|
|
190
|
+
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
191
|
+
| `-y`, `--yes` | Run without interactive confirmation |
|
|
192
|
+
| `-t`, `--test` | Enable preflight tests |
|
|
193
|
+
| `-b`, `--build` | Enable the release build after local release metadata is created |
|
|
194
|
+
| `-p`, `--push` | Explicitly select the `git` target; combine with other target flags as needed |
|
|
195
|
+
| `--target <csv>` | Replace configured targets with a non-empty comma-separated subset of `git`, `npm`, and `docker`; empty or unknown elements fail |
|
|
196
|
+
| `--npm` | Explicitly select the `npm` target; combine with other target flags as needed |
|
|
197
|
+
| `--docker` | Explicitly select digest-qualified Docker publication; requires project-local `@git.zone/tsdocker >= 3.5.1` with protocol v1 |
|
|
198
|
+
| `--no-publish` | Remove npm and Docker from the resolved target set without implicitly enabling Git |
|
|
199
|
+
| `--no-build` | Disable the separate post-metadata project build; Docker qualification still performs its required image build |
|
|
200
|
+
| `--merge` | Fast-forward and lease-push a cleanly rebased feature branch into `main`, then release from `main`; incompatible with an active Docker target |
|
|
201
|
+
| `--major`, `--minor`, `--patch` | Override inferred semver level |
|
|
202
|
+
| `--plan` | Show the resolved workflow without fetching or mutating refs, files, the index, or worktrees |
|
|
203
|
+
| `inspect [version]` | Read one or all durable release journals; add `--json` for machine-readable output |
|
|
204
|
+
| `resume <version>` | Resume journaled Git, npm, qualification, promotion, or cleanup work; fresh-release overrides are rejected, and `--json` remains inspect-only |
|
|
205
|
+
| `--recover-attempt <id>` | Recover the exact 32-character lowercase hexadecimal `attempt.id` shown by `inspect --json`, after independently proving its publisher stopped |
|
|
206
|
+
|
|
207
|
+
### Exact artifacts and release journals
|
|
208
|
+
|
|
209
|
+
Journal schema 1 supports Git and public npm publication. Schema 2 adds Docker qualification, promotion, and cleanup evidence without changing the storage path or schema-1 parsing and resume behavior. Fresh and resumed npm publication requires the resolved `pnpm` binary to be exactly the version pinned by this package (`11.21.0`). Registries must be unique canonical credential-free HTTP(S) URLs, and `release.targets.npm.accessLevel` must be `public` so verification does not depend on publisher credentials.
|
|
205
210
|
|
|
206
211
|
A selected Git target requires a canonical remote name and exactly one resolved push URL. Before any remote contact, including during `--plan`, GitZone rejects HTTP(S) usernames or passwords, embedded passwords, query strings, fragments, and unsupported protocols. Canonical local paths and SSH destinations may retain their required SSH username.
|
|
207
212
|
|
|
208
|
-
Every release that reaches final
|
|
213
|
+
Every release that reaches final publication stores `journal.json` under the repository's Git common directory. npm releases additionally run `pnpm pack` once and store the resulting `package.tgz` beside it. The `v1` path names the storage format and intentionally contains both journal schemas:
|
|
209
214
|
|
|
210
215
|
```text
|
|
211
216
|
<git-common-dir>/gitzone/releases/v1/v<version>/package.tgz
|
|
212
217
|
<git-common-dir>/gitzone/releases/v1/v<version>/journal.json
|
|
213
218
|
```
|
|
214
219
|
|
|
215
|
-
The canonical journal binds the release commit, annotated tag object, hashed Git destination, npm registries, per-target attempt states, and, when npm is selected, the package identity, tarball size, SHA-1, SHA-256, and SHA-512 integrity. Journal updates use revision compare-and-swap under an interprocess lock. Release identity and destinations cannot change after journal creation. Inspect and resume reject malformed, future, noncanonical, duplicate-key, or merely reformatted journal JSON rather than repairing or normalizing it.
|
|
220
|
+
The canonical journal binds the release commit, annotated tag object, hashed Git destination, npm registries, per-target attempt states, and, when npm is selected, the package identity, tarball size, SHA-1, SHA-256, and SHA-512 integrity. Schema 2 also binds the exact tSDocker request, candidate identity, qualification result, ordered destination promotions, probe or promotion evidence, and terminal cleanup result. Journal updates use revision compare-and-swap under an interprocess lock. Release identity and destinations cannot change after journal creation. Qualification and promotion evidence is append-only, destination conflicts are terminal, and completed Docker state cannot regress. Inspect and resume reject malformed, future, noncanonical, duplicate-key, or merely reformatted journal JSON rather than repairing or normalizing it.
|
|
216
221
|
|
|
217
222
|
Packing must leave the release tree clean. Each registry receives that exact stored tarball through `pnpm publish <tarball> --ignore-scripts`, so publish lifecycle scripts do not run. Success is recorded only after anonymous no-redirect probes verify version metadata, SHA-1 and integrity fields, the downloaded tarball bytes, and the `latest` dist-tag. A matching pre-existing version follows `release.targets.npm.alreadyPublished`; conflicting bytes always stop the release, while transient responses and dist-tag propagation remain retryable.
|
|
218
223
|
|
|
@@ -232,11 +237,11 @@ gitzone release resume 6.0.0 -y
|
|
|
232
237
|
gitzone release resume 6.0.0 -y --recover-attempt="$ATTEMPT_ID"
|
|
233
238
|
```
|
|
234
239
|
|
|
235
|
-
Resume requires the local `main` commit, annotated release tag, and each journaled destination setting to remain exact. The Git remote must still match when Git is journaled; npm registry, access, and already-published settings must still match when npm is journaled. Non-journaled target configuration is ignored, so releases created with a target subset remain resumable. Fresh-release overrides such as targets, integration, build, test, and version flags are rejected even in negated forms such as `--no-git`, `--no-publish`, and `--no-build`; JSON output is available only through `inspect --json`.
|
|
240
|
+
Resume requires the local `main` commit, annotated release tag, and each journaled destination setting to remain exact. The Git remote must still match when Git is journaled; npm registry, access, and already-published settings must still match when npm is journaled. Before any recovery or publication work, a nonterminal schema-2 resume canonical-compares the current Docker request with the journal, verifies every required project-local tSDocker protocol capability, and validates the journaled request; additive future capabilities are accepted. A completed schema-2 journal trusts its terminal cleanup evidence and does not require candidate state or current Docker configuration. Non-journaled target configuration is ignored, so releases created with a target subset remain resumable. Fresh-release overrides such as targets, integration, build, test, and version flags are rejected even in negated forms such as `--no-git`, `--no-publish`, and `--no-build`; JSON output is available only through `inspect --json`.
|
|
236
241
|
|
|
237
|
-
|
|
242
|
+
Resume probes remotely observable targets before acting. Exact state is accepted without republishing; positive byte, metadata, ref, or digest conflicts fail closed, while inconclusive results do not overwrite a previously verified state. A Git, npm, Docker qualification, promotion, or cleanup target left in `publishing` state retains authority until the exact recorded 32-character lowercase hexadecimal `attempt.id` is supplied after independently proving its publisher stopped. An interrupted tSDocker `preparing` record has no qualification evidence and cannot be promoted; when tSDocker reports `RECOVERY_REQUIRED`, the exact recovered qualification owner removes only that incomplete deterministic preparation before claiming a fresh qualification attempt. Promotion recovery first reclaims the retired owner's request files and probes the journaled destination; an exact qualified digest is accepted, pending state retires the old attempt and claims a fresh owner before retry, an inconclusive probe retains the old journal owner, and conflicts halt permanently. Final cleanup of a qualified candidate starts only after every promotion is verified and is itself idempotently recoverable. A terminal qualification destination conflict has no promotions, blocks Git/npm, and permits only exact qualified-candidate cleanup.
|
|
238
243
|
|
|
239
|
-
|
|
244
|
+
Schema 1 resumes final Git release-ref and npm publication after the journal is atomically installed. Schema 2 additionally resumes Docker qualification, ordered promotion, and cleanup. Neither schema recovers local version, changelog, commit, tag, build, or pack failures that occur before installation. `--merge` remains available only for non-Docker releases because its pre-release Git push would violate qualification-before-publication ordering. Pre-installation failures require explicit operator reconciliation.
|
|
240
245
|
|
|
241
246
|
## Standard Changelog
|
|
242
247
|
|
|
@@ -258,7 +263,7 @@ The standard buckets are `Breaking Changes`, `Features`, `Fixes`, `Documentation
|
|
|
258
263
|
|
|
259
264
|
## Configuration
|
|
260
265
|
|
|
261
|
-
CLI workflow config lives under `@git.zone/cli` in `.smartconfig.json`.
|
|
266
|
+
CLI workflow config lives under `@git.zone/cli` in `.smartconfig.json`. Docker release selection lives under `@git.zone/cli.release.targets.docker`; canonical registries, repository mappings, and platforms remain owned by `@git.zone/tsdocker`.
|
|
262
267
|
|
|
263
268
|
```json
|
|
264
269
|
{
|
|
@@ -324,18 +329,21 @@ values should be hosts without `http://` or `https://`:
|
|
|
324
329
|
@git.zone/tsdocker.registries
|
|
325
330
|
```
|
|
326
331
|
|
|
327
|
-
Docker release configuration uses the project's
|
|
328
|
-
|
|
329
|
-
|
|
332
|
+
Docker release configuration uses the project's absolute project-local
|
|
333
|
+
`node_modules/.bin/tsdocker`. Install tSDocker `3.5.1` or newer in each
|
|
334
|
+
Docker-producing project; fresh release and nonterminal resume verify matching
|
|
335
|
+
package and binary versions plus the required protocol-v1 capabilities before
|
|
336
|
+
use. Additional future protocol capabilities are accepted:
|
|
330
337
|
|
|
331
338
|
```bash
|
|
332
|
-
pnpm add --save-dev @git.zone/tsdocker
|
|
339
|
+
pnpm add --save-dev @git.zone/tsdocker@3.5.1
|
|
333
340
|
```
|
|
334
341
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
342
|
+
`release.targets.docker.registry`, `buildRegistries`, `test`, `patterns`,
|
|
343
|
+
`cached`, `parallel`, and `context` become part of the immutable qualification
|
|
344
|
+
request. `noBuild` must remain false or absent because digest qualification
|
|
345
|
+
always builds. The separate release `--no-build` flag only disables the normal
|
|
346
|
+
project build step and does not bypass Docker qualification.
|
|
339
347
|
|
|
340
348
|
Useful config commands:
|
|
341
349
|
|
|
@@ -495,11 +503,11 @@ Formatters include cleanup, smartconfig normalization, dependency license checks
|
|
|
495
503
|
|
|
496
504
|
Supported services:
|
|
497
505
|
|
|
498
|
-
| Service
|
|
499
|
-
|
|
|
500
|
-
| MongoDB
|
|
506
|
+
| Service | Lifecycle/log aliases |
|
|
507
|
+
| ----------------------------- | --------------------- |
|
|
508
|
+
| MongoDB | `mongo`, `mongodb` |
|
|
501
509
|
| ObjectStorage (S3-compatible) | `objectstorage`, `s3` |
|
|
502
|
-
| Elasticsearch
|
|
510
|
+
| Elasticsearch | `elasticsearch`, `es` |
|
|
503
511
|
|
|
504
512
|
Service-selection commands such as `set`, `enable`, and `disable` also accept
|
|
505
513
|
`elastic`; lifecycle and log commands do not.
|
|
@@ -620,12 +628,12 @@ than interpreting legacy evidence as an active service.
|
|
|
620
628
|
|
|
621
629
|
Cleanup is tiered, from fully resumable to irreversible:
|
|
622
630
|
|
|
623
|
-
| Command
|
|
624
|
-
|
|
|
625
|
-
| `gitzone services stop`
|
|
626
|
-
| `gitzone services remove` | removed
|
|
627
|
-
| `gitzone services clean`
|
|
628
|
-
| `gitzone services prune`
|
|
631
|
+
| Command | Containers | Data | Notes |
|
|
632
|
+
| ------------------------- | -------------- | ----------- | -------------------------------------------- |
|
|
633
|
+
| `gitzone services stop` | kept (stopped) | kept | fully resumable |
|
|
634
|
+
| `gitzone services remove` | removed | kept | resumable; `--yes` to skip the prompt |
|
|
635
|
+
| `gitzone services clean` | removed | **removed** | irreversible; needs a typed `yes` or `--yes` |
|
|
636
|
+
| `gitzone services prune` | see below | see below | machine-wide; dry run unless `--apply` |
|
|
629
637
|
|
|
630
638
|
`clean` and `prune` first persist an exact deletion intent, then atomically rename
|
|
631
639
|
the canonical directory to a tokenized sibling quarantine before deleting any
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@git.zone/cli',
|
|
6
|
-
version: '6.
|
|
6
|
+
version: '6.1.1',
|
|
7
7
|
description: 'A comprehensive CLI tool for enhancing and managing local development workflows with gitzone utilities, focusing on project setup, version control, code formatting, and template management.'
|
|
8
8
|
}
|
package/ts/helpers.workflow.ts
CHANGED
|
@@ -3,13 +3,7 @@ import { getCliConfigValue } from "./helpers.smartconfig.js";
|
|
|
3
3
|
export type TConfirmationMode = "prompt" | "auto" | "plan";
|
|
4
4
|
|
|
5
5
|
export type TCommitStep =
|
|
6
|
-
| "
|
|
7
|
-
| "analyze"
|
|
8
|
-
| "test"
|
|
9
|
-
| "build"
|
|
10
|
-
| "changelog"
|
|
11
|
-
| "commit"
|
|
12
|
-
| "push";
|
|
6
|
+
"format" | "analyze" | "test" | "build" | "changelog" | "commit" | "push";
|
|
13
7
|
|
|
14
8
|
export type TReleaseTarget = "git" | "npm" | "docker";
|
|
15
9
|
|
|
@@ -134,6 +128,18 @@ export interface IResolvedReleaseResumeConfiguration {
|
|
|
134
128
|
npmAlreadyPublished: "success" | "error";
|
|
135
129
|
}
|
|
136
130
|
|
|
131
|
+
export type TResolvedReleaseDockerConfiguration = Pick<
|
|
132
|
+
IResolvedReleaseWorkflow,
|
|
133
|
+
| "dockerRegistry"
|
|
134
|
+
| "dockerBuildRegistries"
|
|
135
|
+
| "dockerTest"
|
|
136
|
+
| "dockerPatterns"
|
|
137
|
+
| "dockerCached"
|
|
138
|
+
| "dockerParallel"
|
|
139
|
+
| "dockerContext"
|
|
140
|
+
| "dockerNoBuild"
|
|
141
|
+
>;
|
|
142
|
+
|
|
137
143
|
interface ICliWorkflowConfig {
|
|
138
144
|
commit?: ICommitWorkflowConfig;
|
|
139
145
|
release?: IReleaseWorkflowConfig;
|
|
@@ -179,9 +185,9 @@ export const supportsTsdockerMinimumVersion = (
|
|
|
179
185
|
versionOutputArg: string,
|
|
180
186
|
minimumVersionArg: TTsdockerMinimumVersion,
|
|
181
187
|
): boolean => {
|
|
182
|
-
const match = versionOutputArg
|
|
183
|
-
|
|
184
|
-
|
|
188
|
+
const match = versionOutputArg
|
|
189
|
+
.trim()
|
|
190
|
+
.match(/(?:^|\s)(\d+)\.(\d+)\.(\d+)(?:\s|$)/);
|
|
185
191
|
if (!match) {
|
|
186
192
|
return false;
|
|
187
193
|
}
|
|
@@ -207,6 +213,94 @@ export const resolveDockerTestSetting = (valueArg: unknown): boolean => {
|
|
|
207
213
|
return valueArg;
|
|
208
214
|
};
|
|
209
215
|
|
|
216
|
+
const resolveDockerConfiguration = (
|
|
217
|
+
dockerConfigArg: IReleaseDockerTargetConfig,
|
|
218
|
+
): TResolvedReleaseDockerConfiguration => {
|
|
219
|
+
if (
|
|
220
|
+
dockerConfigArg.engine !== undefined &&
|
|
221
|
+
dockerConfigArg.engine !== "tsdocker"
|
|
222
|
+
) {
|
|
223
|
+
throw new Error("release.targets.docker.engine must be tsdocker");
|
|
224
|
+
}
|
|
225
|
+
if (
|
|
226
|
+
dockerConfigArg.registry !== undefined &&
|
|
227
|
+
(typeof dockerConfigArg.registry !== "string" ||
|
|
228
|
+
!dockerConfigArg.registry.trim())
|
|
229
|
+
) {
|
|
230
|
+
throw new Error(
|
|
231
|
+
"release.targets.docker.registry must be a non-empty string",
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
const normalizeStringList = (
|
|
235
|
+
valueArg: unknown,
|
|
236
|
+
settingArg: "buildRegistries" | "patterns",
|
|
237
|
+
): string[] => {
|
|
238
|
+
if (valueArg === undefined) return [];
|
|
239
|
+
if (
|
|
240
|
+
!Array.isArray(valueArg) ||
|
|
241
|
+
valueArg.some(
|
|
242
|
+
(entryArg) => typeof entryArg !== "string" || !entryArg.trim(),
|
|
243
|
+
)
|
|
244
|
+
) {
|
|
245
|
+
throw new Error(
|
|
246
|
+
`release.targets.docker.${settingArg} must be an array of non-empty strings`,
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
const normalized = valueArg.map((entryArg) => entryArg.trim());
|
|
250
|
+
if (new Set(normalized).size !== normalized.length) {
|
|
251
|
+
throw new Error(
|
|
252
|
+
`release.targets.docker.${settingArg} must contain unique values`,
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
return normalized;
|
|
256
|
+
};
|
|
257
|
+
if (
|
|
258
|
+
dockerConfigArg.cached !== undefined &&
|
|
259
|
+
typeof dockerConfigArg.cached !== "boolean"
|
|
260
|
+
) {
|
|
261
|
+
throw new Error("release.targets.docker.cached must be true or false");
|
|
262
|
+
}
|
|
263
|
+
if (
|
|
264
|
+
dockerConfigArg.parallel !== undefined &&
|
|
265
|
+
dockerConfigArg.parallel !== false &&
|
|
266
|
+
dockerConfigArg.parallel !== true &&
|
|
267
|
+
(!Number.isSafeInteger(dockerConfigArg.parallel) ||
|
|
268
|
+
(dockerConfigArg.parallel as number) <= 0)
|
|
269
|
+
) {
|
|
270
|
+
throw new Error(
|
|
271
|
+
"release.targets.docker.parallel must be false, true, or a positive integer",
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
if (
|
|
275
|
+
dockerConfigArg.context !== undefined &&
|
|
276
|
+
(typeof dockerConfigArg.context !== "string" ||
|
|
277
|
+
!dockerConfigArg.context.trim())
|
|
278
|
+
) {
|
|
279
|
+
throw new Error(
|
|
280
|
+
"release.targets.docker.context must be a non-empty string",
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
if (
|
|
284
|
+
dockerConfigArg.noBuild !== undefined &&
|
|
285
|
+
typeof dockerConfigArg.noBuild !== "boolean"
|
|
286
|
+
) {
|
|
287
|
+
throw new Error("release.targets.docker.noBuild must be true or false");
|
|
288
|
+
}
|
|
289
|
+
return {
|
|
290
|
+
dockerRegistry: dockerConfigArg.registry?.trim() || undefined,
|
|
291
|
+
dockerBuildRegistries: normalizeStringList(
|
|
292
|
+
dockerConfigArg.buildRegistries,
|
|
293
|
+
"buildRegistries",
|
|
294
|
+
),
|
|
295
|
+
dockerTest: resolveDockerTestSetting(dockerConfigArg.test),
|
|
296
|
+
dockerPatterns: normalizeStringList(dockerConfigArg.patterns, "patterns"),
|
|
297
|
+
dockerCached: dockerConfigArg.cached ?? false,
|
|
298
|
+
dockerParallel: dockerConfigArg.parallel ?? false,
|
|
299
|
+
dockerContext: dockerConfigArg.context?.trim() || undefined,
|
|
300
|
+
dockerNoBuild: dockerConfigArg.noBuild ?? false,
|
|
301
|
+
};
|
|
302
|
+
};
|
|
303
|
+
|
|
210
304
|
const resolveGitPushSetting = (
|
|
211
305
|
valueArg: unknown,
|
|
212
306
|
settingArg: "pushBranch" | "pushTags",
|
|
@@ -223,11 +317,19 @@ const resolveGitPushSetting = (
|
|
|
223
317
|
return true;
|
|
224
318
|
};
|
|
225
319
|
|
|
226
|
-
const isStringRecord = (
|
|
227
|
-
|
|
320
|
+
const isStringRecord = (
|
|
321
|
+
valueArg: unknown,
|
|
322
|
+
): valueArg is Record<string, unknown> => {
|
|
323
|
+
return (
|
|
324
|
+
typeof valueArg === "object" &&
|
|
325
|
+
valueArg !== null &&
|
|
326
|
+
!Array.isArray(valueArg)
|
|
327
|
+
);
|
|
228
328
|
};
|
|
229
329
|
|
|
230
|
-
export const getDeclaredTsdockerRange = (
|
|
330
|
+
export const getDeclaredTsdockerRange = (
|
|
331
|
+
packageJsonArg: unknown,
|
|
332
|
+
): string | undefined => {
|
|
231
333
|
if (!isStringRecord(packageJsonArg)) {
|
|
232
334
|
return undefined;
|
|
233
335
|
}
|
|
@@ -256,14 +358,24 @@ const normalizeConfirmation = (
|
|
|
256
358
|
|
|
257
359
|
const normalizeRegistryUrl = (url: string): string => {
|
|
258
360
|
let normalizedUrl = url.trim();
|
|
259
|
-
if (
|
|
361
|
+
if (
|
|
362
|
+
!normalizedUrl.startsWith("http://") &&
|
|
363
|
+
!normalizedUrl.startsWith("https://")
|
|
364
|
+
) {
|
|
260
365
|
normalizedUrl = `https://${normalizedUrl}`;
|
|
261
366
|
}
|
|
262
|
-
return normalizedUrl.endsWith("/")
|
|
367
|
+
return normalizedUrl.endsWith("/")
|
|
368
|
+
? normalizedUrl.slice(0, -1)
|
|
369
|
+
: normalizedUrl;
|
|
263
370
|
};
|
|
264
371
|
|
|
265
372
|
const isDisabled = (argvArg: any, ...keys: string[]): boolean => {
|
|
266
|
-
return keys.some(
|
|
373
|
+
return keys.some(
|
|
374
|
+
(key) =>
|
|
375
|
+
argvArg[key] === false ||
|
|
376
|
+
argvArg[`no-${key}`] ||
|
|
377
|
+
argvArg[`no${key[0].toUpperCase()}${key.slice(1)}`],
|
|
378
|
+
);
|
|
267
379
|
};
|
|
268
380
|
|
|
269
381
|
const readCliWorkflowConfig = async (): Promise<ICliWorkflowConfig> => {
|
|
@@ -298,13 +410,13 @@ const getOrderedShortFlags = (commandName: string): string[] => {
|
|
|
298
410
|
const hasExplicitCommitWorkflowFlags = (argvArg: any): boolean => {
|
|
299
411
|
return Boolean(
|
|
300
412
|
argvArg.f ||
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
413
|
+
argvArg.format ||
|
|
414
|
+
argvArg.t ||
|
|
415
|
+
argvArg.test ||
|
|
416
|
+
argvArg.b ||
|
|
417
|
+
argvArg.build ||
|
|
418
|
+
argvArg.p ||
|
|
419
|
+
argvArg.push,
|
|
308
420
|
);
|
|
309
421
|
};
|
|
310
422
|
|
|
@@ -397,13 +509,23 @@ const buildReleasePlan = (options: {
|
|
|
397
509
|
if (options.runBuild) plan.push("core.build");
|
|
398
510
|
if (options.targets.includes("npm")) plan.push("core.packNpmArtifact");
|
|
399
511
|
plan.push("core.writeReleaseJournal");
|
|
512
|
+
if (options.targets.includes("docker")) {
|
|
513
|
+
plan.push("target.docker.qualify");
|
|
514
|
+
}
|
|
400
515
|
for (const target of options.targets) {
|
|
401
|
-
plan.push(
|
|
516
|
+
plan.push(
|
|
517
|
+
target === "docker" ? "target.docker.promote" : `target.${target}`,
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
if (options.targets.includes("docker")) {
|
|
521
|
+
plan.push("target.docker.cleanup");
|
|
402
522
|
}
|
|
403
523
|
return plan;
|
|
404
524
|
};
|
|
405
525
|
|
|
406
|
-
export const resolveCommitWorkflow = async (
|
|
526
|
+
export const resolveCommitWorkflow = async (
|
|
527
|
+
argvArg: any,
|
|
528
|
+
): Promise<IResolvedCommitWorkflow> => {
|
|
407
529
|
const cliConfig = await readCliWorkflowConfig();
|
|
408
530
|
const commitConfig = cliConfig.commit || {};
|
|
409
531
|
const releaseFlagRequested = Boolean(argvArg.r || argvArg.release);
|
|
@@ -431,7 +553,10 @@ export const resolveCommitWorkflow = async (argvArg: any): Promise<IResolvedComm
|
|
|
431
553
|
if (argvArg.push && !rawSteps.includes("push")) rawSteps.push("push");
|
|
432
554
|
rawSteps.push("changelog");
|
|
433
555
|
rawSteps.push("commit");
|
|
434
|
-
} else if (
|
|
556
|
+
} else if (
|
|
557
|
+
Array.isArray(commitConfig.steps) &&
|
|
558
|
+
commitConfig.steps.length > 0
|
|
559
|
+
) {
|
|
435
560
|
rawSteps = commitConfig.steps;
|
|
436
561
|
} else {
|
|
437
562
|
rawSteps = ["analyze"];
|
|
@@ -456,7 +581,9 @@ export const resolveCommitWorkflow = async (argvArg: any): Promise<IResolvedComm
|
|
|
456
581
|
};
|
|
457
582
|
};
|
|
458
583
|
|
|
459
|
-
export const resolveReleaseWorkflow = async (
|
|
584
|
+
export const resolveReleaseWorkflow = async (
|
|
585
|
+
argvArg: any,
|
|
586
|
+
): Promise<IResolvedReleaseWorkflow> => {
|
|
460
587
|
const cliConfig = await readCliWorkflowConfig();
|
|
461
588
|
const releaseConfig = cliConfig.release || {};
|
|
462
589
|
const targetConfig = releaseConfig.targets || {};
|
|
@@ -481,7 +608,10 @@ export const resolveReleaseWorkflow = async (argvArg: any): Promise<IResolvedRel
|
|
|
481
608
|
gitEnabled,
|
|
482
609
|
);
|
|
483
610
|
|
|
484
|
-
let confirmation = normalizeConfirmation(
|
|
611
|
+
let confirmation = normalizeConfirmation(
|
|
612
|
+
releaseConfig.confirmation,
|
|
613
|
+
"prompt",
|
|
614
|
+
);
|
|
485
615
|
if (argvArg.plan) {
|
|
486
616
|
confirmation = "plan";
|
|
487
617
|
} else if (argvArg.y || argvArg.yes) {
|
|
@@ -517,7 +647,9 @@ export const resolveReleaseWorkflow = async (argvArg: any): Promise<IResolvedRel
|
|
|
517
647
|
(!Array.isArray(rawNpmRegistries) ||
|
|
518
648
|
rawNpmRegistries.some((registryArg) => typeof registryArg !== "string"))
|
|
519
649
|
) {
|
|
520
|
-
throw new Error(
|
|
650
|
+
throw new Error(
|
|
651
|
+
"release.targets.npm.registries must be an array of strings",
|
|
652
|
+
);
|
|
521
653
|
}
|
|
522
654
|
npmRegistries = (rawNpmRegistries || []).map(normalizeRegistryUrl);
|
|
523
655
|
if (new Set(npmRegistries).size !== npmRegistries.length) {
|
|
@@ -527,6 +659,19 @@ export const resolveReleaseWorkflow = async (argvArg: any): Promise<IResolvedRel
|
|
|
527
659
|
}
|
|
528
660
|
}
|
|
529
661
|
const mergeRequested = argvArg.merge === true;
|
|
662
|
+
const dockerConfiguration: TResolvedReleaseDockerConfiguration =
|
|
663
|
+
targets.includes("docker")
|
|
664
|
+
? resolveDockerConfiguration(dockerConfig)
|
|
665
|
+
: {
|
|
666
|
+
dockerRegistry: undefined,
|
|
667
|
+
dockerBuildRegistries: [],
|
|
668
|
+
dockerTest: false,
|
|
669
|
+
dockerPatterns: [],
|
|
670
|
+
dockerCached: false,
|
|
671
|
+
dockerParallel: false,
|
|
672
|
+
dockerContext: undefined,
|
|
673
|
+
dockerNoBuild: false,
|
|
674
|
+
};
|
|
530
675
|
|
|
531
676
|
return {
|
|
532
677
|
confirmation,
|
|
@@ -549,23 +694,7 @@ export const resolveReleaseWorkflow = async (argvArg: any): Promise<IResolvedRel
|
|
|
549
694
|
npmAlreadyPublished: npmConfig.alreadyPublished || "success",
|
|
550
695
|
dockerEnabled,
|
|
551
696
|
dockerEngine: "tsdocker",
|
|
552
|
-
|
|
553
|
-
? dockerConfig.registry.trim()
|
|
554
|
-
: undefined,
|
|
555
|
-
dockerBuildRegistries: Array.isArray(dockerConfig.buildRegistries)
|
|
556
|
-
? dockerConfig.buildRegistries
|
|
557
|
-
.filter((registryArg): registryArg is string => typeof registryArg === "string")
|
|
558
|
-
.map((registryArg) => registryArg.trim())
|
|
559
|
-
.filter(Boolean)
|
|
560
|
-
: [],
|
|
561
|
-
dockerTest: resolveDockerTestSetting(dockerConfig.test),
|
|
562
|
-
dockerPatterns: Array.isArray(dockerConfig.patterns) ? dockerConfig.patterns : [],
|
|
563
|
-
dockerCached: dockerConfig.cached ?? false,
|
|
564
|
-
dockerParallel: dockerConfig.parallel ?? false,
|
|
565
|
-
dockerContext: typeof dockerConfig.context === "string" && dockerConfig.context.trim()
|
|
566
|
-
? dockerConfig.context.trim()
|
|
567
|
-
: undefined,
|
|
568
|
-
dockerNoBuild: dockerConfig.noBuild ?? false,
|
|
697
|
+
...dockerConfiguration,
|
|
569
698
|
};
|
|
570
699
|
};
|
|
571
700
|
|
|
@@ -591,9 +720,13 @@ export const resolveReleaseResumeConfiguration = async (optionsArg: {
|
|
|
591
720
|
if (
|
|
592
721
|
npmConfig.registries !== undefined &&
|
|
593
722
|
(!Array.isArray(npmConfig.registries) ||
|
|
594
|
-
npmConfig.registries.some(
|
|
723
|
+
npmConfig.registries.some(
|
|
724
|
+
(registryArg) => typeof registryArg !== "string",
|
|
725
|
+
))
|
|
595
726
|
) {
|
|
596
|
-
throw new Error(
|
|
727
|
+
throw new Error(
|
|
728
|
+
"release.targets.npm.registries must be an array of strings",
|
|
729
|
+
);
|
|
597
730
|
}
|
|
598
731
|
npmRegistries = (npmConfig.registries || []).map(normalizeRegistryUrl);
|
|
599
732
|
if (new Set(npmRegistries).size !== npmRegistries.length) {
|
|
@@ -606,7 +739,9 @@ export const resolveReleaseResumeConfiguration = async (optionsArg: {
|
|
|
606
739
|
npmConfig.accessLevel !== "public" &&
|
|
607
740
|
npmConfig.accessLevel !== "private"
|
|
608
741
|
) {
|
|
609
|
-
throw new Error(
|
|
742
|
+
throw new Error(
|
|
743
|
+
"release.targets.npm.accessLevel must be public or private",
|
|
744
|
+
);
|
|
610
745
|
}
|
|
611
746
|
if (
|
|
612
747
|
npmConfig.alreadyPublished !== undefined &&
|
|
@@ -628,3 +763,10 @@ export const resolveReleaseResumeConfiguration = async (optionsArg: {
|
|
|
628
763
|
npmAlreadyPublished,
|
|
629
764
|
};
|
|
630
765
|
};
|
|
766
|
+
|
|
767
|
+
export const resolveReleaseDockerConfiguration =
|
|
768
|
+
async (): Promise<TResolvedReleaseDockerConfiguration> => {
|
|
769
|
+
const cliConfig = await readCliWorkflowConfig();
|
|
770
|
+
const dockerConfig = cliConfig.release?.targets?.docker || {};
|
|
771
|
+
return resolveDockerConfiguration(dockerConfig);
|
|
772
|
+
};
|