Ctrl K
ComunidadGuías…

docs/planning/addon-publish-plan.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Publishing jwilleke's add-ons — plan for review

> __Status: approved by the operator, 2026-10-17__ (recorded on [ngdpbase#1863](https://github.com/jwilleke/ngdpbase/issues/1863)). The workflow ships in ngdpbase v5.16.0 (PR #1864): `create:addon --repo` writes it, `create:addon --update-publish` refreshes it. The types package is `@jwilleke/ngdpbase-types`, with a `/testing` export for add-on tests ([ngdpbase#1865](https://github.com/jwilleke/ngdpbase/issues/1865), not built yet). Nothing is published yet.

## What's decided already

- All of jwilleke's add-ons are public: public repos, public npm packages (operator, 2026-10-17).
- Add-ons are published to npm only from GitHub Actions, with provenance (2026-10-15).
- A site installs an add-on pinned to an exact version and checksum, and ngdpbase v5.14.0's installer ([ngdpbase#1830](https://github.com/jwilleke/ngdpbase/issues/1830)) refuses any package whose npm provenance doesn't come from the repository the site names.

## The plan

__One workflow file per add-on repo, generated by ngdpbase.__ `npm run create:addon -- --repo` writes `.github/workflows/publish.yml` into the add-on's repo. The file carries a version marker; `create:addon --update-publish` refreshes it, so a change to how add-ons publish is made once, in ngdpbase, and pulled into each repo.

__Why not one shared workflow in ngdpbase?__ Two reasons:

- npm's trusted publishing checks the calling repo's own workflow file;
- the provenance certificate would name ngdpbase's repo as the builder, and the installer as shipped would then refuse every package.

A shared workflow could come back only if the installer switched to a different provenance check.

### What the workflow does

1. __Starts on a tag `<slug>-v<semver>`__, e.g. `phr-v0.0.1`. A plain `v1.2.3` tag never publishes unless the repo opts in with `--plain-tags`. yourphr doesn't: its `v*` tags are yourPHR's app releases.
2. __Finds the add-on__ in `addons/<slug>/` and takes the package name from its `package.json`.
3. __Checks the package__ before anything else:
   - the tag version equals the package version;
   - it isn't `private`;
   - `ngdpbase.slug` and `ngdpbase.minVersion` are set;
   - `engines.node` is `>=24.15.0`;
   - `repository` names this repo and the folder;
   - an explicit `files` list and a license are present.
4. __Builds and tests:__ `npm ci`, build, then the add-on's own tests. It builds against ngdpbase only when the add-on imports ngdpbase's files directly; phr doesn't, so it builds on its own.
5. __Packs and checks the pack:__ no TypeScript sources (other than type declarations) and no tests in the tarball. Runtime dependencies are bundled, since sites have no npm at runtime.
6. __Publishes__ with `npm publish --provenance --access public` through trusted publishing: a short-lived token from GitHub, nothing stored.
7. __Makes a GitHub release__ with the `.tgz` and its sha512.

## What only the operator can do (once per package)

npm requires a package to exist before trusted publishing can be switched on, and switching it on needs the owner's two-factor sign-in. So, for each new package:

1. Publish an empty placeholder, version `0.0.0`, from the npm account `jswilleke`.
2. Mark it: `npm deprecate <package>@0.0.0 "placeholder, do not install"`.
3. On npmjs.com, add the trusted publisher for the package: this GitHub repo and `publish.yml`.

Sites never install the placeholder: it has no provenance, and every real version starts at `0.0.1` or later.

| Package | Repo | Scope | Status |
|---|---|---|---|
| `@yourphr/phr-addon` | jwilleke/yourphr (`addons/phr`) | `@yourphr` (org created 2026-10-15) | not on npm yet |
| `@jwilleke/accounting-addon` | jwilleke/ngdp-accounting-addons (`addons/accounting`) | `@jwilleke` (already publishes `agent-kit`, `oidc-auth-server`) | not on npm yet; repo is private, so making it public is the operator's step |

## Dry run before anything real

`phr` 0.0.1, which holds no records:

1. tag `phr-v0.0.1`;
2. the workflow publishes;
3. a local ngdpbase ≥5.14 site names it in `ngdpbase.addons.install.phr` (package, version, `repository: github.com/jwilleke/yourphr`);
4. the installer checks provenance and the checksum;
5. phr loads.

Anything that breaks is cheap to fix at this stage.

## phr's own changes, when the operator says go

- `package.json`:
  - remove `"private": true`;
  - add `repository` (`{ url: github.com/jwilleke/yourphr, directory: addons/phr }`);
  - add a `files` list: compiled `.js`, `config/`, `pages/`, `package.json`.

  `engines.node` is already `>=24`; it becomes `>=24.15.0`.
- Add the generated `publish.yml` to yourphr, once PR #1864 is merged and released.

## Open for the operator

1. ~~__Accept this plan?__~~ Approved 2026-10-17.
2. ~~__Accounting's types__~~ Decided 2026-10-17: ngdpbase publishes a types package, and both phr and accounting use it instead of their own copies (see [`ngdp-move.md`](ngdp-move.md)).
3. __When to do the bootstrap__ for `@yourphr/phr-addon` and run the dry run.