@crvouga/personal 0.0.0-stage → 0.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/README.md +237 -2
- package/cli.mjs +4207 -0
- package/commands.json +491 -0
- package/package.json +42 -4
- package/probe.mjs +1010 -0
- package/releasing.md +160 -0
- package/schema.json +56 -0
package/releasing.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Automated npm releases
|
|
2
|
+
|
|
3
|
+
The source workspace stays private. Only the allowlisted contents of
|
|
4
|
+
`packages/personal-cli/dist` are packed as **`@crvouga/personal`**.
|
|
5
|
+
|
|
6
|
+
## One-time bootstrap
|
|
7
|
+
|
|
8
|
+
Node, npm and Bun must be on PATH through the shared mise setup. npm 11.15+ is
|
|
9
|
+
needed for the bootstrap's `npm trust` command. Start with a read-only plan:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
bash scripts/dev/bootstrap.sh
|
|
13
|
+
exec "$SHELL" -l
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The machine bootstrap needs no Bun or npm, installs the shared locked mise
|
|
17
|
+
toolchain and frozen workspace dependencies, and configures persistent shell
|
|
18
|
+
PATH. On native Windows use `& ./scripts/dev/bootstrap.ps1`, then reopen your
|
|
19
|
+
terminal. See [machine setup](../../scripts/dev/README.md) for new/remote nodes.
|
|
20
|
+
After refreshing your terminal, run:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
bun run personal:release:plan
|
|
24
|
+
bun run personal:publish:bootstrap --apply --yes --npm-login
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Release progress on stderr identifies each stage (`npm-version`, `build`,
|
|
28
|
+
`pack`, `consumer-install`, consumer commands and publisher verification).
|
|
29
|
+
Failures name the stage without exposing raw subprocess output or credentials.
|
|
30
|
+
The npm runner supports mise shims, shell launchers and native executables;
|
|
31
|
+
Windows `.cmd` launchers use npm's adjacent JavaScript entry point through Node.
|
|
32
|
+
To validate the complete build/pack/anonymous-consumer path without logging in,
|
|
33
|
+
publishing or changing trusted publishers, run:
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
bun run personal:build
|
|
37
|
+
bun run --cwd packages/personal-cli smoke
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The bootstrap builds and installs/tests the exact tarball in an anonymous isolated
|
|
41
|
+
consumer, prints npm's account authorization URL, publishes the package if necessary,
|
|
42
|
+
and configures/verifies its GitHub trusted publisher. Approve npm login and any
|
|
43
|
+
npm account/2FA approval in your own terminal/browser. No npm password, recovery
|
|
44
|
+
code or long-lived publishing token needs to be supplied to this project.
|
|
45
|
+
The trusted publisher is **`crvouga/personal` / `ci.yml`**, with direct publishing
|
|
46
|
+
permission. The publishing job runs directly in `ci.yml` so the registered
|
|
47
|
+
workflow and executing workflow have the same identity. Do not move publication
|
|
48
|
+
into a reusable workflow: its OIDC identity can differ from the registered publisher.
|
|
49
|
+
|
|
50
|
+
Bootstrap is retryable: if content is already published, it only checks/configures
|
|
51
|
+
the publisher. Existing publishers are preserved; no connection is revoked or
|
|
52
|
+
replaced. npm account authorization uses an isolated temporary 0600 npmrc, which
|
|
53
|
+
is removed with its logs/cache when the command exits. Initial publication also
|
|
54
|
+
inherits your terminal so npm can request its publishing-specific 2FA approval.
|
|
55
|
+
Global npm login/config is
|
|
56
|
+
not changed. A hard process kill can leave a temporary directory; remove any
|
|
57
|
+
abandoned `personal-npm-auth-*` directory in your OS temporary directory.
|
|
58
|
+
|
|
59
|
+
### Remote Mac / SSH browser approval
|
|
60
|
+
|
|
61
|
+
Run the bootstrap **on the Mac Mini** in an interactive remote terminal. If you
|
|
62
|
+
connect with SSH, use `ssh -t YOUR_ALIAS` to allocate a terminal. No browser is
|
|
63
|
+
needed on the Mini, and no port forwarding or copying npm credentials is needed.
|
|
64
|
+
|
|
65
|
+
1. Run `bun run personal:publish:bootstrap --apply --yes --npm-login` on the Mini.
|
|
66
|
+
2. When npm prints its HTTPS login URL, copy/open that **new URL on your MacBook
|
|
67
|
+
Air**, sign in to npm and complete the account/2FA approval there.
|
|
68
|
+
3. **Leave the Mini's terminal running**. It waits for npm's approval and resumes
|
|
69
|
+
automatically; no Enter-to-open-browser prompt is used. Ctrl+C cancels login.
|
|
70
|
+
4. Publishing may print another approval URL or ask for a one-time code. Open
|
|
71
|
+
that URL on the Air too, or enter the requested code in the Mini's terminal.
|
|
72
|
+
|
|
73
|
+
Browser launch is disabled for the entire isolated npm session, including
|
|
74
|
+
publish-time web 2FA and trusted-publisher setup. npm update notices/progress are
|
|
75
|
+
disabled too; keep npm versions owned by the shared mise lock, not a global
|
|
76
|
+
`npm install -g`. After cancellation or expiry, rerun the command and use its new
|
|
77
|
+
URL, not an earlier session's URL. Treat approval links as private; never paste
|
|
78
|
+
them into reports or shared logs. Registry verification retries version and latest-tag propagation with 100 seconds of bounded propagation waits without republishing. Transient read failures are retried within that same bound. The browser approval is not proof of publication;
|
|
79
|
+
wait for registry-integrity and trusted-publisher verification to finish.
|
|
80
|
+
|
|
81
|
+
## Vault username/password login
|
|
82
|
+
|
|
83
|
+
If a suitable npm publishing token already exists in Vault, use:
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
bun run personal:publish:bootstrap --apply --yes --login
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
This prompts for **Vault username and password**, not a Vault token. The default
|
|
90
|
+
username is `crvouga`; set `PERSONAL_VAULT_USERNAME` to change it. Login creates a
|
|
91
|
+
temporary in-memory userpass session, reads the requested credential and revokes
|
|
92
|
+
the session immediately afterward. Nothing is written to `~/.vault-token`.
|
|
93
|
+
`PERSONAL_VAULT_PASSWORD_FILE` supports a private secret-delivery file for agents;
|
|
94
|
+
the caller owns deleting that file immediately after use. Never pass passwords as
|
|
95
|
+
command arguments. Noninteractive calls never prompt.
|
|
96
|
+
|
|
97
|
+
Defaults: Vault `https://vault.chrisvouga.dev`, document
|
|
98
|
+
`secret/data/personal/prd`, field `NPM_TOKEN`. Select an **existing** field/document
|
|
99
|
+
using `PERSONAL_NPM_TOKEN_KEY` / `PERSONAL_NPM_VAULT_PATH`. The token must permit
|
|
100
|
+
creating/publishing `@crvouga/personal` and satisfy npm's unattended-publish/2FA
|
|
101
|
+
policy. Recovery codes are not publishing credentials. Missing fields fail
|
|
102
|
+
clearly; no code guesses another secret, seeds values or changes Vault policy.
|
|
103
|
+
Vault auth, policy and secret changes remain exclusively OpenTofu-owned.
|
|
104
|
+
|
|
105
|
+
Existing npm `NODE_AUTH_TOKEN` / `NPM_TOKEN` are also supported for automation.
|
|
106
|
+
Local Vault access always uses username/password, never a user-supplied Vault
|
|
107
|
+
token or a saved `~/.vault-token`. `--login` explicitly chooses username/password
|
|
108
|
+
even when an npm credential is already injected. A publishing
|
|
109
|
+
token is passed only in the npm child environment; its temporary npmrc contains
|
|
110
|
+
an environment-variable reference, not the token itself.
|
|
111
|
+
|
|
112
|
+
## CI: automatic publishing after checks
|
|
113
|
+
|
|
114
|
+
The main **CI** workflow validates the CLI on Linux/macOS/Windows and Node
|
|
115
|
+
22/24/26, including installing the packed distribution into a clean directory
|
|
116
|
+
with anonymous registry configuration and running it on real Node. The required
|
|
117
|
+
**Check** includes this job. After Check/Fleet pass on main, the release job directly in
|
|
118
|
+
`ci.yml`:
|
|
119
|
+
|
|
120
|
+
1. Reads the public registry and hashes the release inputs.
|
|
121
|
+
2. Skips publication when the published content hash already matches.
|
|
122
|
+
3. Uses `0.1.0` initially, then automatically increments the current stable patch
|
|
123
|
+
version. A higher source manifest version deliberately selects a minor/major.
|
|
124
|
+
4. Builds, allowlist-checks, packs and consumer-tests the exact tarball.
|
|
125
|
+
5. Publishes using npm's **GitHub OIDC trusted publishing**, without npm secrets.
|
|
126
|
+
6. Confirms the content hash, SHA-512 integrity and latest tag, with bounded
|
|
127
|
+
read-only retries for registry propagation. Unconfirmed outcomes fail rather
|
|
128
|
+
than blindly republish.
|
|
129
|
+
|
|
130
|
+
Publishing is serialized with non-cancelling concurrency. Dispatch **CI** on main
|
|
131
|
+
to retry. PRs, merge queues, failed checks and non-main dispatches never publish.
|
|
132
|
+
Public-source CI includes npm provenance. Local bootstrap records its revision
|
|
133
|
+
and dirty-checkout status and does not claim CI provenance. Registry versions
|
|
134
|
+
are the checkpoint; no version-bump commits, tags or GitHub write access is needed.
|
|
135
|
+
Atlas and Mockingbird informed the bootstrap/trusted-publisher and exact-consumer
|
|
136
|
+
validation design; their accounts/publishers are never modified.
|
|
137
|
+
|
|
138
|
+
For an explicitly selected existing Vault-backed npm token instead of OIDC, set
|
|
139
|
+
the **repository variable** `PERSONAL_NPM_TOKEN_KEY` to its field name, never its
|
|
140
|
+
value. CI then uses existing GitHub → Vault OIDC (`jwt` / `github-actions`) to read
|
|
141
|
+
that one field from `secret/data/personal/prd`. No duplicate GitHub secret is
|
|
142
|
+
created. Leave the variable unset for default tokenless npm publishing.
|
|
143
|
+
|
|
144
|
+
## Recovery and verification
|
|
145
|
+
|
|
146
|
+
If publication is unconfirmed, rerun the **plan** first. Never guess a new version
|
|
147
|
+
or blindly repeat a publish. Bootstrap can resume publisher setup after a package
|
|
148
|
+
was published successfully. npm account access is a prerequisite for first
|
|
149
|
+
publication; Vault login alone cannot grant npm publishing access.
|
|
150
|
+
|
|
151
|
+
Verify the released consumer independently:
|
|
152
|
+
|
|
153
|
+
```sh
|
|
154
|
+
npx --yes @crvouga/personal@latest commands --json
|
|
155
|
+
npx --yes @crvouga/personal@latest fleet doctor --all --json
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Do not claim release or green CI based on local checks. Deployment of the CI
|
|
159
|
+
workflow requires these source changes to land on main, and remote fleet coverage
|
|
160
|
+
requires actual locally configured SSH aliases and checkout paths.
|
package/schema.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "Developer fleet roster (not infrastructure inventory)",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"additionalProperties": false,
|
|
6
|
+
"required": ["version", "nodes"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"$schema": { "type": "string" },
|
|
9
|
+
"version": { "const": 1 },
|
|
10
|
+
"nodes": {
|
|
11
|
+
"type": "array",
|
|
12
|
+
"minItems": 1,
|
|
13
|
+
"maxItems": 64,
|
|
14
|
+
"items": {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"additionalProperties": false,
|
|
17
|
+
"required": ["id", "ssh", "workspace"],
|
|
18
|
+
"properties": {
|
|
19
|
+
"id": { "type": "string", "pattern": "^[a-z][a-z0-9-]{0,62}$" },
|
|
20
|
+
"ssh": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"pattern": "^[a-zA-Z0-9][a-zA-Z0-9._-]{0,252}$"
|
|
23
|
+
},
|
|
24
|
+
"workspace": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"maxLength": 4096,
|
|
27
|
+
"pattern": "^/[^\\u0000-\\u001f\\u007f]*$"
|
|
28
|
+
},
|
|
29
|
+
"runtime": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"maxLength": 4096,
|
|
32
|
+
"pattern": "^/[^\\u0000-\\u001f\\u007f]*$"
|
|
33
|
+
},
|
|
34
|
+
"required": {
|
|
35
|
+
"type": "array",
|
|
36
|
+
"uniqueItems": true,
|
|
37
|
+
"default": ["github", "vault", "t3", "toolchain"],
|
|
38
|
+
"items": {
|
|
39
|
+
"enum": [
|
|
40
|
+
"github",
|
|
41
|
+
"vault",
|
|
42
|
+
"t3",
|
|
43
|
+
"vscode",
|
|
44
|
+
"toolchain",
|
|
45
|
+
"codex",
|
|
46
|
+
"claude",
|
|
47
|
+
"services",
|
|
48
|
+
"workstation"
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|