Repository navigation
Release Process
This is the maintainer-facing release procedure. End users install
the published package per Getting-Started
in the wiki — they don't need to read this.
| Registry | Name | Auth | Audience |
|---|---|---|---|
| npmjs.com (canonical) | hexo-blog-encrypt |
OIDC trusted publishing | All end users. This is the install path advertised in the README. |
| GitHub Packages (mirror) | @d0n9x1n/hexo-blog-encrypt |
GITHUB_TOKEN |
Discoverability only — populates the repo's "Packages" sidebar. Identical tarball, scoped name. |
Both publish flows run with provenance (npm publish --provenance)
so the attestation proves the tarball was built from this repo at the
exact tagged commit.
Prepare these changes on a feature branch, then merge after the PR checks pass.
There is no CHANGELOG file to update: the GitHub Release generated on tag push
is the release history. Make sure the PRs in the release link their issues
with Closes #N so they appear under "Resolved issues" (see
Release notes).
-
demo/package.json— use a published semver range such as^x.y.z, neverfile:... Raise the minimum when the demo exercises the new fix. The release workflow rejectsfile:references; it does not require an exact demo-version match. -
package.jsonversion— should already match the tag from your bump commit; the workflow re-verifies.
Run npm test, npm pack --dry-run --json > pack.json, and
node build/verify-pack.js pack.json before pushing. Keep the generated
pack.json and the built bundle out of commits (package-lock.json is
committed; CI installs with npm ci). Merge the release changes
only after the Tests workflow passes; create the tag from that exact commit.
Heads-up: deploy-demo can fail before publication. The Pages deploy runs
npm installagainst the demo site, which now wantshexo-blog-encrypt@^X.Y.Zfrom npmjs — and that version doesn't exist yet. This is expected. Retrigger deploy-demo once the release workflow has published (see step 3 under Post-publish).
git tag -a vX.Y.Z -m "hexo-blog-encrypt vX.Y.Z
<one-paragraph release summary>
Migration notes / links."
git push origin vX.Y.ZThis single tag push triggers two workflows in parallel:
| Workflow | What it publishes |
|---|---|
.github/workflows/release.yml |
hexo-blog-encrypt@X.Y.Z to npmjs.com via OIDC + provenance |
.github/workflows/publish-gh-packages.yml |
@d0n9x1n/hexo-blog-encrypt@X.Y.Z to npm.pkg.github.com via GITHUB_TOKEN + provenance |
Both run the same gates before publishing: version matches the tag, npm test
(lint + server tests with coverage + e2e), and build/verify-pack.js. A version
that fails canonical validation never reaches the mirror. The mirror keeps its
own tag trigger rather than workflow_run on release.yml, because under
workflow_run the provenance would name master's HEAD, not the tagged commit.
Watch them:
gh run list --workflow=release.yml --limit 1
gh run list --workflow=publish-gh-packages.yml --limit 1The publish-gh-packages.yml job skips publish if the version already exists.
The canonical npm publish step does not: if npm succeeded but GitHub Release
creation failed, re-run only the failed job, not the successful publish job.
Every re-run uses the same tagged commit; newer master changes do not apply.
-
Verify on npm:
curl -s "https://registry.npmjs.org/hexo-blog-encrypt/latest" \ | python3 -c "import json,sys; d=json.load(sys.stdin); print(d['version'])"
-
Verify the GitHub Release. After npm publication,
release.ymlautomatically creates or updates the GitHub Release in itsgithub-releasejob, with notes generated byscripts/release-notes.sh(see Release notes below). Do not create a duplicate release manually.gh release view vX.Y.Z
-
Retrigger deploy-demo so the live site pulls the now-published tarball and refreshes:
gh workflow run deploy-demo.yml --ref master
-
Verify the GH Packages mirror at
https://github.com/D0n9X1n/hexo-blog-encrypt/pkgs/npm/hexo-blog-encryptshows the new version. Confirm the repo's right-hand "Packages" sidebar lists@d0n9x1n/hexo-blog-encrypt.
The github-release job runs bash scripts/release-notes.sh "$GITHUB_REF_NAME"
(the same approach as SonicTerm). GitHub Releases are the project's release
history; there is no CHANGELOG file. The generated notes contain, in order:
-
# hexo-blog-encrypt vX.Y.Z, thenpm install hexo-blog-encrypt@X.Y.Zline, and links to npmjs.com and the GitHub Packages mirror (@d0n9x1n/hexo-blog-encrypt). -
## Resolved issues— issues proven closed by non-reverted commits or merged PRs in the tag range, collected byscripts/release-issues.py. Issues closed by hand (no closing commit or PR recorded by GitHub) are listed separately under Manually closed issues (unverified release linkage). -
## Changes since <previous tag>—git log --no-mergessubjects with short SHAs, plus a compare link. -
## Verification— lint, 100% server coverage, Playwright e2e, and npm provenance.
For an issue to appear under "Resolved issues", link it with a closing
keyword: put Closes #N (or Fixes #N / Resolves #N) in the PR
description or a commit message, so GitHub records the PR or commit as the
issue's closer. Plain mentions such as Refs #N or #N are not counted, and
neither is milestone membership. Reverted commits and PRs are excluded.
The generator is fail-closed. It needs complete history (fetch-depth: 0;
shallow clones are rejected) and a token with issues: read and
pull-requests: read. A GitHub API failure fails the job without publishing
partial notes; re-run the failed github-release job, which uses the same
tagged commit. For the very first tag, or to override the previous tag, set
RELEASE_FIRST=1 or PREVIOUS_TAG=<ref>.
Preview the notes locally (needs gh auth login):
bash scripts/release-notes.sh vX.Y.ZThe release tooling has offline tests that run in the Tests workflow's
release-tooling job:
python3 scripts/release-issues_tests.py
bash scripts/test-release-notes.shscripts/ is not in package.json files, so none of this ships in the npm
tarball.
If a tag already exists but publish-gh-packages.yml didn't run for it
(e.g. the workflow was added later), dispatch it manually:
gh workflow run publish-gh-packages.yml --ref master -f tag=vX.Y.ZThe workflow checks out the requested tag, applies the same scope rewrite, and publishes. If the version is already present, it skips.
Configure on npmjs once per package:
- https://www.npmjs.com/package/hexo-blog-encrypt/access → Trusted Publishers
- Add a GitHub Actions trusted publisher with:
-
Organization or user:
D0n9X1n -
Repository:
hexo-blog-encrypt -
Workflow filename:
release.yml - Environment: (leave blank)
-
Organization or user:
The release workflow then exchanges its short-lived OIDC token for
publish authority — no NPM_TOKEN secret required, no rotation. The
docs test (tests/docs.test.js) fails closed if secrets.NPM_TOKEN or
NODE_AUTH_TOKEN is re-introduced into release.yml.
No setup. The workflow uses the auto-provided GITHUB_TOKEN with
permissions: packages: write declared inline.
OIDC trusted publishing and provenance attestations both require
npm CLI ≥ 11.5.1. Node 20 ships with npm 10.x, so both publish
workflows include an explicit npm install -g npm@^11 step. If that
step disappears, npm publish --provenance fails with ENEEDAUTH
(npm 10 doesn't know how to do the OIDC dance, so it falls back to
checking for a token that isn't there).
GitHub Packages npm requires the package name to be scoped to the repo
owner. hexo-blog-encrypt (unscoped) cannot be published as-is to
npm.pkg.github.com. The publish-gh-packages.yml workflow mutates
package.json in-CI before npm publish:
{
"name": "@d0n9x1n/hexo-blog-encrypt",
"publishConfig": { "registry": "https://npm.pkg.github.com", "access": "public" }
}The runner is ephemeral so this rewrite never reaches git. End users who want to install from GH Packages instead of npmjs use the scoped name (see Getting Started for the install snippet).
| Symptom | Likely cause | Fix |
|---|---|---|
ENEEDAUTH from npm publish in release.yml |
npm CLI < 11.5.1 (the npm install -g npm@^11 step is missing or failed), OR trusted publisher misconfigured on npmjs.com |
Verify the upgrade step ran; double-check the trusted-publisher fields match exactly (workflow filename release.yml, no environment). |
github-release job fails at "Generate release notes" |
GitHub API error/rate limit, missing issues: read / pull-requests: read, or a shallow checkout |
npm is already published — re-run only the failed github-release job; a re-run uses the same tagged commit, not newer master changes. |
| An issue is missing from "Resolved issues" | Its PR or commit did not use a closing keyword (Closes #N), or it was closed by hand |
Link issues with Closes #N in future PRs; manual closures appear only in the unverified section. |
release.yml fails at "Verify demo references published package" |
demo/package.json still says file:..
|
Fix the manifest before tagging a new version; a re-run uses the same tagged commit. |
publish-gh-packages.yml fails with 403 Forbidden
|
The runner's GITHUB_TOKEN lost packages: write (e.g. branch protection stripped it) |
Restore permissions: packages: write at the workflow / job scope. |
| GitHub Packages page returns 404 after publish | Eventually consistent; CDN catches up within seconds. | Wait, refresh. If still 404 after a few minutes, the publish step probably no-op'd — check the workflow log for the "Skip if … already exists" branch. |
| Tag points at the wrong commit (you forgot a fix) | Squash merges drop late commits; the tag was created before the fix | Do not move a published tag or overwrite an npm version. Apply the correction and release a new patch version. Inspect both registries before retrying any failed publication. |
-
Push pre-flight + verify CI green BEFORE creating the tag. The tag
triggers a real publish; once
npm publishsucceeds you cannot republish that version. Rolling back means a new patch version. - Squash-merging a PR mid-flight orphans late commits on the source branch. If you push a fix to a feature branch after the squash merge, that fix lives only on the dead branch — it never reaches master. Always push fixes BEFORE merging, or apply them as a fresh commit on master after merge.
- Both publish jobs are independent, so a failure in one doesn't abort the other. Don't assume "both green" from a single ✓ — check each workflow's run.
- Architecture — codebase orientation.
- Development — workflow rules + commands.
-
.github/workflows/release.yml— npmjs publish workflow. -
.github/workflows/publish-gh-packages.yml— GH Packages mirror workflow. - GitHub Releases — release history.
-
scripts/release-notes.sh— release-notes generator.