fixing documentation
Publish snapshot / snapshot (push) Successful in 1m2s

This commit is contained in:
Benjamin Diedrichsen
2026-07-29 13:21:04 +02:00
parent 7e703c93b1
commit 4c0fe528dc
14 changed files with 74 additions and 74 deletions
+1 -1
View File
@@ -116,7 +116,7 @@ jobs:
# Pass 1: stamp every manifest before anything is packed. `pnpm # Pass 1: stamp every manifest before anything is packed. `pnpm
# publish` substitutes `workspace:*` with the version the linked # publish` substitutes `workspace:*` with the version the linked
# package declares at pack time, so nopy-cube has to be carrying its # package declares at pack time, so nopy-cubes has to be carrying its
# snapshot version by the time nopy is packed. # snapshot version by the time nopy is packed.
for dir in $dirs; do for dir in $dirs; do
base=$(node -p "require('./${dir}/package.json').version") base=$(node -p "require('./${dir}/package.json').version")
+3 -3
View File
@@ -1,13 +1,13 @@
# Tag-driven release of a single package. # Tag-driven release of a single package.
# #
# git tag nopy-v1.2.0 && git push origin nopy-v1.2.0 # git tag nopy-v1.2.0 && git push origin nopy-v1.2.0
# git tag nopy-cube-v1.2.0 && git push origin nopy-cube-v1.2.0 # git tag nopy-cubes-v1.2.0 && git push origin nopy-cubes-v1.2.0
# git tag keyman-v1.2.0 && git push origin keyman-v1.2.0 # git tag keyman-v1.2.0 && git push origin keyman-v1.2.0
# #
# The tag is the source of truth for *which* package ships; package.json is the # The tag is the source of truth for *which* package ships; package.json is the
# source of truth for the version, and the two must agree or the run fails. # source of truth for the version, and the two must agree or the run fails.
# #
# Packages that link to each other release dependency-first — `nopy-cube` before # Packages that link to each other release dependency-first — `nopy-cubes` before
# `nopy` — because the linked version is resolved at pack time. The run refuses # `nopy` — because the linked version is resolved at pack time. The run refuses
# to publish otherwise. # to publish otherwise.
# A version with a prerelease part (1.2.0-rc.1) publishes under `next` instead # A version with a prerelease part (1.2.0-rc.1) publishes under `next` instead
@@ -135,7 +135,7 @@ jobs:
# package declares at this commit. If that version is not on the # package declares at this commit. If that version is not on the
# registry yet, the release installs to a broken tree — and npmjs # registry yet, the release installs to a broken tree — and npmjs
# only lets you unpublish for 72 hours. Release the dependency first: # only lets you unpublish for 72 hours. Release the dependency first:
# nopy-cube, then nopy, then any bundle. # nopy-cubes, then nopy, then any bundle.
# #
# npmjs only: it is the irreversible one, and it needs no credentials # npmjs only: it is the irreversible one, and it needs no credentials
# to read, which this step does not have yet. # to read, which this step does not have yet.
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"hosts": [], "hosts": [],
"cubeDirs": [], "cubeDirs": [],
"cubePackages": ["@bitsquare/cubes-core"], "cubePackages": ["@bitsquare/nopy-cubes-core"],
"env": {}, "env": {},
"log": { "log": {
"verbosity": "info", "verbosity": "info",
+19 -19
View File
@@ -7,18 +7,18 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
A pnpm workspace holding two independently published CLIs, the authoring package A pnpm workspace holding two independently published CLIs, the authoring package
their deployment units are written against, and one bundle of those units: their deployment units are written against, and one bundle of those units:
| Path | Package | Binary | Role | | Path | Package | Binary | Role |
| --------------------- | ---------------------- | -------- | -------------------------------------------------------- | | -------------------------- | ----------------------------- | -------- | -------------------------------------------------------- |
| `packages/nopy` | `@bitsquare/nopy` | `nopy` | interactive pyinfra script management and execution | | `packages/nopy` | `@bitsquare/nopy` | `nopy` | interactive pyinfra script management and execution |
| `packages/keyman` | `@bitsquare/keyman` | `keyman` | SSH key management, shelling out to `age` / `ssh-keygen` | | `packages/keyman` | `@bitsquare/keyman` | `keyman` | SSH key management, shelling out to `age` / `ssh-keygen` |
| `packages/nopy-cube` | `@bitsquare/nopy-cube` | — | the authoring surface a `manifest.mjs` imports | | `packages/nopy-cubes` | `@bitsquare/nopy-cubes` | — | the authoring surface a `manifest.mjs` imports |
| `packages/cubes-core` | `@bitsquare/cubes-core`| — | the core cube bundle (22 cubes), no TypeScript | | `packages/nopy-cubes-core` | `@bitsquare/nopy-cubes-core` | — | the core cube bundle (22 cubes), no TypeScript |
The root package is private; everything under `packages/` ships. `keyman` stands The root package is private; everything under `packages/` ships. `keyman` stands
alone, but `nopy` and `cubes-core` both depend on `nopy-cube` (`workspace:*`), so alone, but `nopy` and `nopy-cubes-core` both depend on `nopy-cubes` (`workspace:*`), so
publish order matters — see *Releasing*. publish order matters — see *Releasing*.
`cubes-core` is consumed the way a third party would consume it: the root `nopy-cubes-core` is consumed the way a third party would consume it: the root
`.nopyrc.json` names it in `cubePackages`, and the loader reads it out of `.nopyrc.json` names it in `cubePackages`, and the loader reads it out of
`node_modules`. There is no `cubes/` directory at the repo root any more. `node_modules`. There is no `cubes/` directory at the repo root any more.
@@ -76,11 +76,11 @@ locally and on the runner. Barrel files (`src/index.ts`, `src/cubes/index.ts`,
`src/nopy.cubes.ts`) and the Commander argv wiring (`src/*.cli.ts`) are excluded; `src/nopy.cubes.ts`) and the Commander argv wiring (`src/*.cli.ts`) are excluded;
adding logic to those files means moving it somewhere covered. adding logic to those files means moving it somewhere covered.
nopy's vitest config aliases `@bitsquare/nopy-cube` to that package's **source**, nopy's vitest config aliases `@bitsquare/nopy-cubes` to that package's **source**,
not to the workspace link (which points at a `dist` that only exists after a not to the workspace link (which points at a `dist` that only exists after a
build), so the gate does not depend on build ordering and can never run against a build), so the gate does not depend on build ordering and can never run against a
stale artefact. The same config excludes `**/nopy-cube/**` from coverage — without stale artefact. The same config excludes `**/nopy-cubes/**` from coverage — without
it nopy's numbers absorb another package's files. `cubes-core` has no tests of its it nopy's numbers absorb another package's files. `nopy-cubes-core` has no tests of its
own; the loader tests in nopy cover the contract it implements. own; the loader tests in nopy cover the contract it implements.
The three TS packages set `pool: 'forks'` because tests use `process.chdir()` — most The three TS packages set `pool: 'forks'` because tests use `process.chdir()` — most
@@ -184,15 +184,15 @@ items 6 and 7.
A cube directory holds `manifest.mjs` + `deploy.py`; anything else in it is A cube directory holds `manifest.mjs` + `deploy.py`; anything else in it is
ignored by the loader but reachable from the script, which runs with the cube ignored by the loader but reachable from the script, which runs with the cube
directory as its cwd. Manifests are ESM, import `Manifest` from directory as its cwd. Manifests are ESM, import `Manifest` from
`@bitsquare/nopy-cube`, and declare `id`, `name`, a Zod `schema` (each field `@bitsquare/nopy-cubes`, and declare `id`, `name`, a Zod `schema` (each field
`.describe()`d — the description is the prompt label — and `.default()`ed), plus `.describe()`d — the description is the prompt label — and `.default()`ed), plus
optional `secrets`/`dependencies`/`before`/`after`. optional `secrets`/`dependencies`/`before`/`after`.
Import from **`@bitsquare/nopy-cube`**, not `@bitsquare/nopy`. The authoring Import from **`@bitsquare/nopy-cubes`**, not `@bitsquare/nopy`. The authoring
surface is types and a factory, with zod as its only peer — no CLI, no prompts, surface is types and a factory, with zod as its only peer — no CLI, no prompts,
no process spawning — so a bundle can depend on it without dragging the CLI in. no process spawning — so a bundle can depend on it without dragging the CLI in.
`@bitsquare/nopy` re-exports all of it (`cubes.Manifest`, `cubes.uniqid`, …), so `@bitsquare/nopy` re-exports all of it (`cubes.Manifest`, `cubes.uniqid`, …), so
the older form still works; every cube in `packages/cubes-core` has been moved to the older form still works; every cube in `packages/nopy-cubes-core` has been moved to
the new one. the new one.
Manifests are resolved by ordinary Node resolution **from the manifest's own Manifests are resolved by ordinary Node resolution **from the manifest's own
@@ -200,7 +200,7 @@ directory**, which used to mean a hand-written local cube failed with
`ERR_MODULE_NOT_FOUND` unless you linked the package. `cubes/resolve-hook.mjs` `ERR_MODULE_NOT_FOUND` unless you linked the package. `cubes/resolve-hook.mjs`
retires that: `loadCubes()` registers a `module.register()` resolve hook that retires that: `loadCubes()` registers a `module.register()` resolve hook that
tries normal resolution *first* and only on failure falls back to resolving tries normal resolution *first* and only on failure falls back to resolving
`@bitsquare/nopy-cube`, `@bitsquare/nopy` and `zod` from the running CLI's own `@bitsquare/nopy-cubes`, `@bitsquare/nopy` and `zod` from the running CLI's own
`node_modules`. Ordinary-resolution-first is the load-bearing part — a cube that `node_modules`. Ordinary-resolution-first is the load-bearing part — a cube that
ships its own zod keeps it. The hook is a convenience, never load-bearing: ships its own zod keeps it. The hook is a convenience, never load-bearing:
registration is wrapped in a `try`, and a bundle installed properly never reaches registration is wrapped in a `try`, and a bundle installed properly never reaches
@@ -242,7 +242,7 @@ The install command uses `--@bitsquare:registry=<url>`, never `--registry`:
Gitea serves the `@bitsquare` scope and does **not** proxy npmjs, so a global Gitea serves the `@bitsquare` scope and does **not** proxy npmjs, so a global
`--registry` would send every transitive dependency to a registry that has never `--registry` would send every transitive dependency to a registry that has never
heard of them. Verified — `npm i -g @bitsquare/nopy@main --@bitsquare:registry=…` heard of them. Verified — `npm i -g @bitsquare/nopy@main --@bitsquare:registry=…`
pulls `nopy-cube` from Gitea and the other 55 packages from npmjs. pnpm accepts pulls `nopy-cubes` from Gitea and the other 55 packages from npmjs. pnpm accepts
the same flag; the `npm_config_@bitsquare:registry` env var does not work with the same flag; the `npm_config_@bitsquare:registry` env var does not work with
pnpm and is not used. pnpm and is not used.
@@ -330,7 +330,7 @@ Three things the `workspace:*` links added, all of them non-obvious:
`workspace:` range survived into a tarball. It runs in both publish workflows. `workspace:` range survived into a tarball. It runs in both publish workflows.
Note `pnpm pack` has no `--ignore-scripts` flag, so `prepack` does rebuild — Note `pnpm pack` has no `--ignore-scripts` flag, so `prepack` does rebuild —
which means the artefact under test is the one publish ships. which means the artefact under test is the one publish ships.
- **Order.** `packages/*/` sorts `nopy` before `nopy-cube`, which is backwards. - **Order.** `packages/*/` sorts `nopy` before `nopy-cubes`, which is backwards.
`scripts/publish-order.mjs` topologically sorts over the `workspace:` edges; `scripts/publish-order.mjs` topologically sorts over the `workspace:` edges;
the snapshot workflow stamps *every* version first and only then publishes in the snapshot workflow stamps *every* version first and only then publishes in
that order, because `pnpm publish` reads the linked package's version at pack that order, because `pnpm publish` reads the linked package's version at pack
@@ -347,9 +347,9 @@ currently have no effect. Treat `docs/REFACTORING.md` as a plan, not a record.
The publish lane has now run against the Gitea registry: all four packages are The publish lane has now run against the Gitea registry: all four packages are
there under `@main`, and `pnpm run try:snapshot` installs them into a throwaway there under `@main`, and `pnpm run try:snapshot` installs them into a throwaway
project with npm and runs the binary. The npmjs lane has only ever published project with npm and runs the binary. The npmjs lane has only ever published
`@bitsquare/nopy`; `keyman`, `nopy-cube` and `cubes-core` have never been `@bitsquare/nopy`; `keyman`, `nopy-cubes` and `nopy-cubes-core` have never been
released there, so the *check linked deps are released* guard in `release.yml` released there, so the *check linked deps are released* guard in `release.yml`
will stop the first `nopy` release until `nopy-cube` ships. will stop the first `nopy` release until `nopy-cubes` ships.
Nothing checks that a bundle and the CLI reading it are compatible versions; Nothing checks that a bundle and the CLI reading it are compatible versions;
`nopy.engines` was considered and deferred. `docs/CUBE-PACKAGES.md` is where all `nopy.engines` was considered and deferred. `docs/CUBE-PACKAGES.md` is where all
+1 -1
View File
@@ -206,7 +206,7 @@ Two pairs are inverted, and both have consequences:
> to deploy a cube whose required key nothing supplied. Verified against all 22 > to deploy a cube whose required key nothing supplied. Verified against all 22
> cubes in `cubes/`: 19 build a complete `-D` run, the 3 below abort by name. > cubes in `cubes/`: 19 build a complete `-D` run, the 3 below abort by name.
> >
> Re-measured against the 25 cubes now in `packages/cubes-core/cubes`: 20 build a > Re-measured against the 25 cubes now in `packages/nopy-cubes-core/cubes`: 20 build a
> complete `-D` run and 5 abort by name. Two of the additions are deliberate — > complete `-D` run and 5 abort by name. Two of the additions are deliberate —
> `user:add` lost its `PUBKEY` default (it was a specific personal key), and > `user:add` lost its `PUBKEY` default (it was a specific personal key), and
> `ssh:keygen` inherits that failure because it declares `dependencies: () => > `ssh:keygen` inherits that failure because it declares `dependencies: () =>
+23 -23
View File
@@ -28,8 +28,8 @@ shipped. If you only want to cut a release, jump to
| --------------------- | ----------------------- | -------- | ------------------------ | | --------------------- | ----------------------- | -------- | ------------------------ |
| `packages/nopy` | `@bitsquare/nopy` | `nopy` | CLI | | `packages/nopy` | `@bitsquare/nopy` | `nopy` | CLI |
| `packages/keyman` | `@bitsquare/keyman` | `keyman` | CLI | | `packages/keyman` | `@bitsquare/keyman` | `keyman` | CLI |
| `packages/nopy-cube` | `@bitsquare/nopy-cube` | — | library (cube authoring) | | `packages/nopy-cubes` | `@bitsquare/nopy-cubes` | — | library (cube authoring) |
| `packages/cubes-core` | `@bitsquare/cubes-core` | — | cube bundle (no build) | | `packages/nopy-cubes-core` | `@bitsquare/nopy-cubes-core` | — | cube bundle (no build) |
All are ESM and declare `engines.node >= 22`. The two CLIs expose a single All are ESM and declare `engines.node >= 22`. The two CLIs expose a single
executable through `bin`, so `npm install -g` puts `nopy` / `keyman` on the executable through `bin`, so `npm install -g` puts `nopy` / `keyman` on the
@@ -37,7 +37,7 @@ executable through `bin`, so `npm install -g` puts `nopy` / `keyman` on the
The tarball contents are pinned by `files` — for the three TypeScript packages The tarball contents are pinned by `files` — for the three TypeScript packages
that is `["dist", "README.md", "LICENSE"]`, so sources and tests are not shipped. that is `["dist", "README.md", "LICENSE"]`, so sources and tests are not shipped.
`cubes-core` ships `["cubes", "!cubes/**/*.log", "README.md", "LICENSE"]`: the `nopy-cubes-core` ships `["cubes", "!cubes/**/*.log", "README.md", "LICENSE"]`: the
negation matters, because a cube that has been run leaves a `pyinfra-debug.log` negation matters, because a cube that has been run leaves a `pyinfra-debug.log`
next to its `deploy.py`, and `.gitignore` does not filter an npm tarball. next to its `deploy.py`, and `.gitignore` does not filter an npm tarball.
`publishConfig.access: "public"` is what makes a scoped package publishable to `publishConfig.access: "public"` is what makes a scoped package publishable to
@@ -45,7 +45,7 @@ npmjs without an extra flag; the workflows pass `--access public` anyway.
### Dependencies between them ### Dependencies between them
`keyman` stands alone. `nopy` and `cubes-core` both depend on `nopy-cube` through `keyman` stands alone. `nopy` and `nopy-cubes-core` both depend on `nopy-cubes` through
`workspace:*`, which drives three rules the rest of this document keeps coming `workspace:*`, which drives three rules the rest of this document keeps coming
back to: back to:
@@ -55,7 +55,7 @@ back to:
through verbatim and the install fails with `EUNSUPPORTEDPROTOCOL`. `pnpm through verbatim and the install fails with `EUNSUPPORTEDPROTOCOL`. `pnpm
pack` and `pnpm publish` substitute the concrete version at pack time. Both pack` and `pnpm publish` substitute the concrete version at pack time. Both
workflows use `pnpm publish --ignore-scripts --no-git-checks`. workflows use `pnpm publish --ignore-scripts --no-git-checks`.
2. **`nopy-cube` publishes before anything that depends on it.** 2. **`nopy-cubes` publishes before anything that depends on it.**
`node scripts/publish-order.mjs` prints the publishable directories in `node scripts/publish-order.mjs` prints the publishable directories in
dependency order — note that plain alphabetical `packages/*/` gets this dependency order — note that plain alphabetical `packages/*/` gets this
backwards, putting `nopy` first. backwards, putting `nopy` first.
@@ -124,7 +124,7 @@ The publish step is **two passes** over `node scripts/publish-order.mjs`: the
first stamps the snapshot version into every manifest with `npm pkg set`, the first stamps the snapshot version into every manifest with `npm pkg set`, the
second publishes. They cannot be one loop — `pnpm publish` reads a linked second publishes. They cannot be one loop — `pnpm publish` reads a linked
package's version out of its manifest at pack time, so stamping and publishing package's version out of its manifest at pack time, so stamping and publishing
one package at a time would bake the *old* `nopy-cube` version into `nopy`'s one package at a time would bake the *old* `nopy-cubes` version into `nopy`'s
tarball. tarball.
### `release.yml` ### `release.yml`
@@ -144,7 +144,7 @@ the whole gate.
*Check linked deps are released* asks npmjs whether every `workspace:` dependency *Check linked deps are released* asks npmjs whether every `workspace:` dependency
of the package being released already exists at the version pnpm is about to of the package being released already exists at the version pnpm is about to
bake in (`scripts/linked-deps.mjs``npm view`). Tagging `nopy-v1.3.0` while bake in (`scripts/linked-deps.mjs``npm view`). Tagging `nopy-v1.3.0` while
`@bitsquare/nopy-cube@1.1.0` is still unpublished would otherwise ship a tarball `@bitsquare/nopy-cubes@1.1.0` is still unpublished would otherwise ship a tarball
nobody can install, and npmjs only lets you unpublish for 72 hours. The check is nobody can install, and npmjs only lets you unpublish for 72 hours. The check is
npmjs-only: it runs before any credentials are written, and npmjs is the registry npmjs-only: it runs before any credentials are written, and npmjs is the registry
where the mistake is permanent. where the mistake is permanent.
@@ -253,26 +253,26 @@ the same way:
```sh ```sh
git tag nopy-v1.2.0 git tag nopy-v1.2.0
git tag keyman-v1.2.0 git tag keyman-v1.2.0
git tag nopy-cube-v1.2.0 git tag nopy-cubes-v1.2.0
git tag cubes-core-v1.2.0 git tag nopy-cubes-core-v1.2.0
``` ```
### Ordering when more than one package changed ### Ordering when more than one package changed
Tags are independent, but the dependency graph is not. If a release touches Tags are independent, but the dependency graph is not. If a release touches
`nopy-cube` *and* something that depends on it, release them in this order, `nopy-cubes` *and* something that depends on it, release them in this order,
waiting for each run to go green: waiting for each run to go green:
``` ```
nopy-cube → nopy, cubes-core (these two are independent of each other) nopy-cubes → nopy, nopy-cubes-core (these two are independent of each other)
``` ```
Release `nopy` first and the run stops at the *check linked deps* step, telling Release `nopy` first and the run stops at the *check linked deps* step, telling
you the `nopy-cube` version it wanted is not on npmjs. That is the guard working; you the `nopy-cubes` version it wanted is not on npmjs. That is the guard working;
release `nopy-cube`, then re-tag. `node scripts/publish-order.mjs` prints the release `nopy-cubes`, then re-tag. `node scripts/publish-order.mjs` prints the
order if you would rather not reason about it. order if you would rather not reason about it.
Bumping `nopy-cube` means bumping the packages that depend on it in the same Bumping `nopy-cubes` means bumping the packages that depend on it in the same
change — the `workspace:*` range resolves to whatever version is in the workspace change — the `workspace:*` range resolves to whatever version is in the workspace
at pack time, so their next release picks it up whether or not you meant it to. at pack time, so their next release picks it up whether or not you meant it to.
@@ -387,12 +387,12 @@ npm install -g @bitsquare/nopy @bitsquare/keyman
``` ```
The other two go into a project. A cube bundle is a dev dependency of whatever The other two go into a project. A cube bundle is a dev dependency of whatever
repo describes your infrastructure; `nopy-cube` is only needed if you are writing repo describes your infrastructure; `nopy-cubes` is only needed if you are writing
cubes of your own: cubes of your own:
```sh ```sh
pnpm add -D @bitsquare/cubes-core # then name it in .nopyrc.json cubePackages pnpm add -D @bitsquare/nopy-cubes-core # then name it in .nopyrc.json cubePackages
pnpm add -D @bitsquare/nopy-cube zod # authoring your own manifests pnpm add -D @bitsquare/nopy-cubes zod # authoring your own manifests
``` ```
From the Gitea registry, which holds every snapshot plus a mirror of every From the Gitea registry, which holds every snapshot plus a mirror of every
@@ -525,9 +525,9 @@ pnpm run try:snapshot -- --keep # keep the directory
`scripts/try-snapshot.mjs` builds a throwaway project in a temp directory, `scripts/try-snapshot.mjs` builds a throwaway project in a temp directory,
points the `@bitsquare` scope at the registry, installs `@bitsquare/nopy` and points the `@bitsquare` scope at the registry, installs `@bitsquare/nopy` and
`@bitsquare/cubes-core` at that tag, and then: `@bitsquare/nopy-cubes-core` at that tag, and then:
- asserts the installed `nopy` declares a **concrete** `nopy-cube` version - asserts the installed `nopy` declares a **concrete** `nopy-cubes` version
rather than a leaked `workspace:*` range; rather than a leaked `workspace:*` range;
- prints the three resolved versions, so you can see which commit you are on; - prints the three resolved versions, so you can see which commit you are on;
- runs `nopy --version`; - runs `nopy --version`;
@@ -662,13 +662,13 @@ Use **npm**, not pnpm: npm is the one that rejects a leaked `workspace:` range,
so a clean install here is the real proof. so a clean install here is the real proof.
```sh ```sh
pnpm --filter @bitsquare/nopy-cube pack --pack-destination /tmp/tgz pnpm --filter @bitsquare/nopy-cubes pack --pack-destination /tmp/tgz
pnpm --filter @bitsquare/nopy pack --pack-destination /tmp/tgz pnpm --filter @bitsquare/nopy pack --pack-destination /tmp/tgz
pnpm --filter @bitsquare/cubes-core pack --pack-destination /tmp/tgz pnpm --filter @bitsquare/nopy-cubes-core pack --pack-destination /tmp/tgz
mkdir /tmp/try && cd /tmp/try && npm init -y mkdir /tmp/try && cd /tmp/try && npm init -y
npm install /tmp/tgz/*.tgz npm install /tmp/tgz/*.tgz
echo '{"hosts":["h"],"cubePackages":["@bitsquare/cubes-core"]}' > .nopyrc.json echo '{"hosts":["h"],"cubePackages":["@bitsquare/nopy-cubes-core"]}' > .nopyrc.json
./node_modules/.bin/nopy install -l session.json -P -D ./node_modules/.bin/nopy install -l session.json -P -D
``` ```
@@ -713,7 +713,7 @@ pnpm run registry:status
| Snapshot workflow green, nothing installable | Snapshots are only on Gitea and only under `@main`. Point the scope at the Gitea registry. | | Snapshot workflow green, nothing installable | Snapshots are only on Gitea and only under `@main`. Point the scope at the Gitea registry. |
| The release workflow did not trigger | The tag must match `*-v*` and must be pushed (`git push origin <tag>`), not just created. | | The release workflow did not trigger | The tag must match `*-v*` and must be pushed (`git push origin <tag>`), not just created. |
| `EUNSUPPORTEDPROTOCOL` / `Unsupported URL Type "workspace:"` on install | A `workspace:` range reached a tarball — something published with `npm publish` instead of `pnpm publish`. `verify-pack.mjs` exists to catch this before it ships. | | `EUNSUPPORTEDPROTOCOL` / `Unsupported URL Type "workspace:"` on install | A `workspace:` range reached a tarball — something published with `npm publish` instead of `pnpm publish`. `verify-pack.mjs` exists to catch this before it ships. |
| `... is not published yet on npmjs` before the gate runs | Releasing a package before its `nopy-cube` dependency. Tag and release `nopy-cube` first, then re-tag. | | `... is not published yet on npmjs` before the gate runs | Releasing a package before its `nopy-cubes` dependency. Tag and release `nopy-cubes` first, then re-tag. |
| `verify-pack.mjs` fails locally with a build error | `pnpm pack` runs `prepack`, so a broken build fails the check. Fix the build; there is no skip flag. | | `verify-pack.mjs` fails locally with a build error | `pnpm pack` runs `prepack`, so a broken build fails the check. Fix the build; there is no skip flag. |
## Recovering from a bad publish ## Recovering from a bad publish
+1 -1
View File
@@ -4,7 +4,7 @@
"description": "The core nopy cube bundle: apt, users, ssh, networking, services and runtimes.", "description": "The core nopy cube bundle: apt, users, ssh, networking, services and runtimes.",
"keywords": [ "keywords": [
"nopy", "nopy",
"nopy-cubess", "nopy-cubes",
"pyinfra", "pyinfra",
"deployment", "deployment",
"infrastructure" "infrastructure"
+14 -14
View File
@@ -17,9 +17,9 @@ importers:
'@biomejs/biome': '@biomejs/biome':
specifier: ^2.5.5 specifier: ^2.5.5
version: 2.5.5 version: 2.5.5
'@bitsquare/cubes-core': '@bitsquare/nopy-cubes-core':
specifier: workspace:* specifier: workspace:*
version: link:packages/cubes-core version: link:packages/nopy-cubes-core
'@logtape/logtape': '@logtape/logtape':
specifier: ^2.2.4 specifier: ^2.2.4
version: 2.2.4 version: 2.2.4
@@ -33,15 +33,6 @@ importers:
specifier: ^7.0.2 specifier: ^7.0.2
version: 7.0.2 version: 7.0.2
packages/cubes-core:
dependencies:
'@bitsquare/nopy-cube':
specifier: workspace:*
version: link:../nopy-cube
zod:
specifier: ^4.4.3
version: 4.4.3
packages/keyman: packages/keyman:
dependencies: dependencies:
execa: execa:
@@ -78,9 +69,9 @@ importers:
packages/nopy: packages/nopy:
dependencies: dependencies:
'@bitsquare/nopy-cube': '@bitsquare/nopy-cubes':
specifier: workspace:* specifier: workspace:*
version: link:../nopy-cube version: link:../nopy-cubes
'@logtape/logtape': '@logtape/logtape':
specifier: ^2.2.4 specifier: ^2.2.4
version: 2.2.4 version: 2.2.4
@@ -128,7 +119,7 @@ importers:
specifier: ^4.1.10 specifier: ^4.1.10
version: 4.1.10(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(tsx@4.23.1)) version: 4.1.10(@types/node@26.1.1)(@vitest/coverage-v8@4.1.10)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(tsx@4.23.1))
packages/nopy-cube: packages/nopy-cubes:
devDependencies: devDependencies:
'@types/node': '@types/node':
specifier: ^26.1.1 specifier: ^26.1.1
@@ -146,6 +137,15 @@ importers:
specifier: ^4.4.3 specifier: ^4.4.3
version: 4.4.3 version: 4.4.3
packages/nopy-cubes-core:
dependencies:
'@bitsquare/nopy-cubes':
specifier: workspace:*
version: link:../nopy-cubes
zod:
specifier: ^4.4.3
version: 4.4.3
packages: packages:
'@babel/helper-string-parser@7.29.7': '@babel/helper-string-parser@7.29.7':
+1 -1
View File
@@ -26,7 +26,7 @@ if (!target) {
const read = (dir) => JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf-8')); const read = (dir) => JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf-8'));
// Resolved by name rather than by directory: nothing guarantees that // Resolved by name rather than by directory: nothing guarantees that
// `@bitsquare/nopy-cube` lives in `packages/nopy-cube`. // `@bitsquare/nopy-cubes` lives in `packages/nopy-cubes`.
const versionByName = new Map( const versionByName = new Map(
fs fs
.readdirSync(PACKAGES_DIR) .readdirSync(PACKAGES_DIR)
+1 -1
View File
@@ -3,7 +3,7 @@
/** /**
* Prints the publishable workspace package directories, dependencies first. * Prints the publishable workspace package directories, dependencies first.
* *
* `packages/*` in alphabetical order puts `nopy` ahead of the `nopy-cube` it * `packages/*` in alphabetical order puts `nopy` ahead of the `nopy-cubes` it
* depends on, which leaves a window where the registry holds a package whose * depends on, which leaves a window where the registry holds a package whose
* dependency does not exist yet. Ordering the publish by the workspace graph * dependency does not exist yet. Ordering the publish by the workspace graph
* closes it. One directory per line, so the caller can `for dir in $(…)`. * closes it. One directory per line, so the caller can `for dir in $(…)`.
+6 -6
View File
@@ -5,7 +5,7 @@
* project, and runs it. * project, and runs it.
* *
* This is the rehearsal that the local `pnpm pack` check cannot be: it goes to * This is the rehearsal that the local `pnpm pack` check cannot be: it goes to
* the real registry, resolves the real `@bitsquare/nopy-cube` version that * the real registry, resolves the real `@bitsquare/nopy-cubes` version that
* `pnpm publish` baked into the tarball, and puts a real `nopy` binary on disk. * `pnpm publish` baked into the tarball, and puts a real `nopy` binary on disk.
* A tarball that installs here is one a user can install. * A tarball that installs here is one a user can install.
* *
@@ -26,7 +26,7 @@ import path from 'node:path';
const SCOPE = '@bitsquare'; const SCOPE = '@bitsquare';
const DEFAULT_REGISTRY = 'https://gitea.bitsquare.dev/api/packages/BitSquare/npm/'; const DEFAULT_REGISTRY = 'https://gitea.bitsquare.dev/api/packages/BitSquare/npm/';
const CLI_PACKAGE = '@bitsquare/nopy'; const CLI_PACKAGE = '@bitsquare/nopy';
const BUNDLE_PACKAGE = '@bitsquare/cubes-core'; const BUNDLE_PACKAGE = '@bitsquare/nopy-cubes-core';
const args = process.argv.slice(2); const args = process.argv.slice(2);
@@ -101,17 +101,17 @@ try {
// The whole point of packing with pnpm: this must be a concrete version, not // The whole point of packing with pnpm: this must be a concrete version, not
// the literal string `workspace:*`. // the literal string `workspace:*`.
const linked = installed.dependencies?.['@bitsquare/nopy-cube']; const linked = installed.dependencies?.['@bitsquare/nopy-cubes'];
if (!linked || linked.startsWith('workspace:')) { if (!linked || linked.startsWith('workspace:')) {
throw new Error( throw new Error(
`${CLI_PACKAGE} declares nopy-cube as "${linked}" — a workspace range escaped.` `${CLI_PACKAGE} declares nopy-cubes as "${linked}" — a workspace range escaped.`
); );
} }
console.log('\n--- versions ---'); console.log('\n--- versions ---');
console.log(`${CLI_PACKAGE}@${installed.version}`); console.log(`${CLI_PACKAGE}@${installed.version}`);
console.log(`${BUNDLE_PACKAGE}@${bundle.version}`); console.log(`${BUNDLE_PACKAGE}@${bundle.version}`);
console.log(` -> @bitsquare/nopy-cube ${linked}`); console.log(` -> @bitsquare/nopy-cubes ${linked}`);
console.log('\n--- nopy --version ---'); console.log('\n--- nopy --version ---');
console.log(capture(path.join(dir, 'node_modules', '.bin', 'nopy'), ['--version'])); console.log(capture(path.join(dir, 'node_modules', '.bin', 'nopy'), ['--version']));
@@ -139,7 +139,7 @@ try {
[ [
...discovery ...discovery
.replace(ansi, '') .replace(ansi, '')
.matchAll(/([a-z0-9:_-]+) - [^\n]*\(@bitsquare\/cubes-core\)/g), .matchAll(/([a-z0-9:_-]+) - [^\n]*\(@bitsquare\/nopy-cubes-core\)/g),
].map((match) => match[1]) ].map((match) => match[1])
), ),
]; ];
+1 -1
View File
@@ -3,7 +3,7 @@
/** /**
* Asserts that every publishable package is installable once packed. * Asserts that every publishable package is installable once packed.
* *
* `@bitsquare/nopy` depends on `@bitsquare/nopy-cube` through `workspace:*`, and * `@bitsquare/nopy` depends on `@bitsquare/nopy-cubes` through `workspace:*`, and
* `link-workspace-packages` is off, so a plain semver range would resolve from * `link-workspace-packages` is off, so a plain semver range would resolve from
* the registry instead of linking the workspace copy — the protocol is not * the registry instead of linking the workspace copy — the protocol is not
* optional. But npm has no idea what `workspace:` means: a tarball that still * optional. But npm has no idea what `workspace:` means: a tarball that still
+1 -1
View File
@@ -13,7 +13,7 @@
"resolveJsonModule": true, "resolveJsonModule": true,
"paths": { "paths": {
"@bitsquare/nopy": ["./packages/nopy/src"], "@bitsquare/nopy": ["./packages/nopy/src"],
"@bitsquare/nopy-cube": ["./packages/nopy-cube/src"], "@bitsquare/nopy-cubes": ["./packages/nopy-cubes/src"],
"@bitsquare/keyman": ["./packages/keyman/src"] "@bitsquare/keyman": ["./packages/keyman/src"]
} }
}, },
+1 -1
View File
@@ -2,7 +2,7 @@
"extends": "./tsconfig.base.json", "extends": "./tsconfig.base.json",
"files": [], "files": [],
"references": [ "references": [
{ "path": "./packages/nopy-cube" }, { "path": "./packages/nopy-cubes" },
{ "path": "./packages/nopy" }, { "path": "./packages/nopy" },
{ "path": "./packages/keyman" } { "path": "./packages/keyman" }
] ]