# Release Guide (发布手册) > For **maintainers** — how to publish taskswarm to the npm registry so others > can `dsh plugin add dsh-taskswarm` or `npx taskswarm-dashboard`. Bilingual standard: > 中文版见 [release.zh-CN.md](release.zh-CN.md). > > User-facing install instructions (npm / GitHub / local) live in the README; > this guide is about getting a new version out. ## Distribution channels | Channel | How users install | When | |---|---|---| | **npm** (primary) | `dsh plugin add dsh-taskswarm` / `npx --package dsh-taskswarm taskswarm-dashboard` | every release | | GitHub | `dsh plugin add https://github.com/february2015/dsh-taskswarm.git` | keep in sync (push master) | | local / offline | clone or zip + `npm install && npm run build && dsh plugin add ` | development | ## Prerequisites - An npm account with **publish rights** on `taskswarm` (unscoped → public by default). - A **Granular Access Token** — a classic token will be rejected with `E403 ... Two-factor authentication or granular access token with bypass 2fa enabled is required` when the account has 2FA enabled (it does, by default): - https://www.npmjs.com/settings//tokens → Generate New Token → **Granular Access Token** - Packages and scopes: `dsh-taskswarm`; Permissions: **Read and write** - **Must check the 2FA bypass option** ("bypass" for publish) — otherwise publishing still fails. - Store the token (the published steps below assume it's in `~/.npmrc`): ```bash npm config set //registry.npmjs.org/:_authToken npm whoami # should print your npm username ``` ## Release steps ```bash # 1. Build & test — the runtime loads lib/, so a fresh build is mandatory npm run build npm test # 2. Bump the version in package.json (semver; npm never allows overwriting a published version) # e.g. 0.1.0 → 0.1.1 (or: npm version patch) # 3. Sanity-check what would ship (files must include lib/, dashboard/, docs/, # templates/, cordis.patch.yml, README.md, README.zh-CN.md) npm pack --dry-run # 4. Publish (public by default for unscoped packages; no extra flag needed) npm publish # 5. Verify npm view dsh-taskswarm version # → 0.1.1 npm view dsh-taskswarm bin # → { 'taskswarm-dashboard': 'dashboard/server.mjs' } # 6. Only after everything above passes, push to GitHub (ordering rule below) git push origin master ``` > ⚠️ **Publish-before-push ordering (mandatory since 2026-08-15)**: **`npm publish` must > succeed and be verified BEFORE `git push` to GitHub.** > - Installing from GitHub depends on the version already published to npm; do not > push code that is not yet visible on npm ("repo has new code, installs get the old package"). > - Pushing never triggers an automatic publish; each version can be published exactly > once (npm rejects duplicates), so the order cannot be fixed afterwards. > - If a problem surfaces after the push: bump with `npm version patch` and republish; > never try to overwrite an existing version. ## Verifying from the user side ```bash dsh plugin --profile web add dsh-taskswarm # install as plugin (restart dsh web) npx --package dsh-taskswarm taskswarm-dashboard --root # standalone dashboard CLI ``` ### Upgrading an existing install `dsh plugin --profile web add dsh-taskswarm` **does not bump** a dependency whose lockfile version already satisfies the declared range (`^0.2.9` is satisfied by `0.2.9`, so pnpm reports "Already up to date" and stays on the old version). To pull a newer version within the range, pin it explicitly: ```bash dsh plugin --profile web add dsh-taskswarm@0.2.10 # explicit version always resolves # or, inside the profile dir: pnpm update --latest ``` Then restart `dsh web` for the new version to take effect. ## Versioning rules - Semantic versioning; during 0.x any minor bump is acceptable for breaking changes. - **Every publish must bump the version** — the registry rejects re-publishing an existing version. - The published tarball is built from the working tree at publish time; if `lib/` is stale (e.g. you changed `src/`), bump + rebuild + republish. ## Security notes - **Two tokens, different rules** (verified 2026-08-17): - **Publish**: Granular Access Token with **2FA bypass** (no OTP prompt on publish). - **Unpublish (delete a version)**: npm security policy **forbids bypass-2FA tokens from deleting versions** — `E403 Granular access tokens that bypass two-factor authentication may not perform this action`. Use a **classic token (no 2FA bypass)**; with 2FA enabled npm will ask for a one-time code: ```bash npm unpublish dsh-taskswarm@0.2.2 # enter the OTP when prompted # or supply it up front: npm unpublish dsh-taskswarm@0.2.2 --otp=<6-digit code> ``` - `~/.npmrc` currently holds the classic token (for unpublish). If publish fails with `Two-factor authentication ... is required`, temporarily switch back to the bypass token or go through OTP. - Use the least-privilege token: scoped to `dsh-taskswarm` only, Read and write. - The token is stored in `~/.npmrc` — treat it like a password. - If a token leaks or you stop using it: revoke it at https://www.npmjs.com/settings//tokens and remove it locally with `npm config delete //registry.npmjs.org/:_authToken`. - **72-hour unpublish window**: npm only allows deleting a version within **72 hours** of publishing; older versions can only be `npm deprecate`d (marked deprecated), never truly removed. Bulk-delete history: ```bash for v in 0.2.1 0.2.2 ... 0.2.37; do npm unpublish dsh-taskswarm@$v; done ``` ## Release checklist - [ ] `npm run build` passes - [ ] `npm test` passes - [ ] version bumped in `package.json` - [ ] `npm pack --dry-run` shows the expected files (lib/dashboard/docs/templates/cordis.patch.yml/README\*) - [ ] `npm publish` succeeds - [ ] `npm view dsh-taskswarm` confirms the new version + bin - [ ] **(only after all of the above)** `git push origin master` to GitHub