This commit is contained in:
@@ -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")
|
||||||
|
|||||||
@@ -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
@@ -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",
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Generated
+14
-14
@@ -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':
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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 $(…)`.
|
||||||
|
|||||||
@@ -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])
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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" }
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user