Publishing Rerius to npm
This document explains the CI/CD pipeline and how to publish Rerius to npm.
Repository: https://github.com/ECLS-Studio/rerius
Note: this source tree doesn't include a
.github/workflows/directory, so the workflow descriptions below couldn't be checked against the live files in this pass: verify job names, triggers, and matrix entries on GitHub before relying on specifics.
Overview#
Rerius uses a four-workflow CI/CD system:
| Workflow | File | Trigger | Purpose |
|---|---|---|---|
| CI | ci.yml |
push/PR to main | Build + test on Linux, macOS, Node matrix |
| Prebuild | prebuild.yml |
tag push / manual | Build .node binaries for all platforms |
| Publish | publish.yml |
tag push / manual | Full release pipeline → npm + GitHub Release |
| Release | release.yml |
manual | Bump version, commit, tag, trigger publish |
| Nightly | nightly.yml |
daily 02:00 UTC | Regression detection, opens issues on failure |
| CodeQL | codeql.yml |
push/PR/weekly | Static security analysis of C source |
One-Time Setup#
1. Create an npm token#
- Log in to npmjs.com
- Go to your profile → Access Tokens → Generate New Token
- Select Automation type
- Copy the token
2. Add the token to GitHub Secrets#
- Open your repo on GitHub
- Settings → Secrets and variables → Actions
- Click New repository secret
- Name:
NPM_TOKEN - Value: paste your npm token
3. Create the npm-publish environment (optional, recommended)#
Environments add an approval gate before publishing:
- Settings → Environments → New environment
- Name:
npm-publish - Add Required reviewers if you want manual approval before each publish
4. Enable GitHub Actions#
Make sure Actions are enabled for your repo: Settings → Actions → Allow all actions.
Releasing a New Version#
Option A - Automated (recommended)#
Use the Release workflow in the Actions tab:
- Go to Actions → Release → Run workflow
- Enter the new version (e.g.
1.1.0) - Enter optional release notes
- Click Run workflow
The workflow will:
- Update the root package.json version (and js/package.json, kept in sync for reference: see the note under npm Package Contents on why the root file is the one that actually matters for publishing)
- Add a CHANGELOG.md entry
- Commit the changes to main
- Create and push the tag v1.1.0
- The Publish workflow triggers automatically from the tag
Option B - Manual tag push#
# 1. Update version in the root package.json (the one npm actually publishes from)
vim package.json # set "version": "1.1.0"
vim js/package.json # keep this in sync too, for reference/local `cd js && npm test` etc.
# 2. Update CHANGELOG.md
vim CHANGELOG.md # add ## [1.1.0] section
# 3. Commit
git add package.json js/package.json CHANGELOG.md
git commit -m "chore: release v1.1.0"
git push origin main
# 4. Tag
git tag -a v1.1.0 -m "Rerius v1.1.0"
git push origin v1.1.0
# → publish.yml triggers automatically
Publish Pipeline Steps#
When a tag v* is pushed, publish.yml runs:
validate check tag matches package.json version
↓
test build + npm test on Linux x64 + macOS arm64
↓
prebuilds parallel build on 4 platforms:
linux-x64 (ubuntu-latest)
linux-arm64 (ubuntu-24.04-arm)
darwin-arm64 (macos-latest)
darwin-x64 (macos-13)
↓
publish download all prebuilds
add to js/prebuilds/
npm publish --provenance
↓
release create GitHub Release with .node files attached
extract CHANGELOG.md section as release notes
Total duration: ~10-15 minutes.
Prebuilds#
Prebuilds are pre-compiled .node files for each platform. When present, users get a zero-compile npm install:
js/prebuilds/
├── rerius-linux-x64.node
├── rerius-linux-arm64.node
├── rerius-darwin-arm64.node
└── rerius-darwin-x64.node
index.js checks for prebuilds automatically:
// Order of lookup:
// 1. js/rerius.node (local build, takes priority)
// 2. js/prebuilds/rerius-<platform>-<arch>.node
// 3. Throw with install instructions
Generating prebuilds locally#
# Run the prebuild workflow manually (Actions → Prebuild → Run workflow)
# Or build locally and copy:
make
mkdir -p js/prebuilds
cp js/rerius.node js/prebuilds/rerius-linux-x64.node # on Linux x64
# cp js/rerius.node js/prebuilds/rerius-darwin-arm64.node # on macOS M-series
Testing Before Publish#
# Full test suite
node js/test/basic.js
# Dry-run publish (shows what would be uploaded)
cd js && npm pack --dry-run
# Inspect the package contents
cd js && npm pack
tar -tzf rerius-*.tgz
# Test the packed package
npm install ./rerius-1.0.0.tgz
node -e "const n=require('rerius');console.log(n.version());"
npm Package Contents#
There are two package.json files in this repo, and only one of them produces a working package.
js/package.json lists ../src/, ../include/, ../arch/, and ../build_js.sh in its files array, trying to reach outside the js/ directory. npm's files field can't do this: npm pack run from js/ silently drops anything outside js/ (verified directly with npm pack --dry-run). A package published with cd js && npm publish would ship without the C source needed to compile the native addon, so postinstall would fail on any platform without a matching prebuild. Treat js/package.json as legacy/broken for standalone publishing: it's kept for reference, not as the publish target.
The root package.json (main: "js/index.js", files: ["js/", "src/", "include/", "platform/", "scripts/", "build_js.sh", "setup.sh", "LICENSE", "README.md"]) is the one that actually works: publish from the repository root, not from js/. Verified contents (npm pack --dry-run from the repo root):
rerius/
├── package.json root manifest -- publish from here
├── LICENSE
├── README.md
├── setup.sh
├── build_js.sh
├── scripts/theme.sh
├── platform/ 9 per-OS/arch .S / .asm entry stubs
├── include/ core/, formats/, arch/, ui/ headers
├── src/ core/, formats/, arch/, analysis/, emu/, util/, cli/ (all C source)
└── js/
├── index.js JS wrapper class
├── index.d.ts TypeScript declarations
├── package.json
├── README.md
├── scripts/
│ ├── install.js postinstall build script
│ └── build.js npm run build
├── server/
│ └── server.js REST API server (42 endpoints at http://localhost:7070)
├── examples/ 6 example scripts
└── src/rerius_napi.c N-API bindings
The C source (src/, include/, platform/) is included so users without a matching prebuild can still compile on install.
What Happens on npm install rerius#
npm install rerius
↓
npm downloads the package from registry
↓
postinstall: node scripts/install.js
↓
1. Check if rerius.node already exists → done
2. Check js/prebuilds/<platform>-<arch>.node → copy → done
3. Find build_js.sh in repo root → run it → done
4. Inline compile with clang/gcc → done
5. If all fail: print helpful error, exit(0) so npm install succeeds
→ import will throw at runtime with instructions
Adding a New Platform#
To add a new prebuild target (e.g. Windows arm64 in future):
- Add a matrix entry to
prebuild.ymlandpublish.yml - Add the expected filename to the "Verify all expected prebuilds" step
- Add
win32handling tobuild_js.shif needed - Update
index.js_findAddon()platform mapping
Troubleshooting#
npm install rerius fails to compile:
# Check if C compiler is available
clang --version || gcc --version
# Manually trigger compilation
node node_modules/rerius/scripts/install.js
CI fails on arm64 runner:
The ubuntu-24.04-arm runner requires the repo has access to GitHub's ARM64 runner pool. Make sure your plan supports it (GitHub Free includes limited arm64 minutes).
npm publish fails - 403:
- Token type must be Automation (not Read-only or Publish)
- Run npm whoami locally to verify login: npm login
- Check the npm-publish environment protection rules aren't blocking
Version mismatch error:
The tag must match package.json (root) version exactly. Use the Release workflow to keep them in sync, or manually update package.json before tagging.
Note: js/package.json is a legacy file kept for reference. The root package.json is the authoritative npm package configuration.
docs/PUBLISHING.md · Rerius v1.0.0