Skip to content

Release Workflow and NPM Publishing ​

Relevant Source Files

The PALEE release pipeline is a highly automated, multi-stage workflow designed to ensure that every version published to the NPM registry is verified, consistent, and functional across platforms. The process is primarily driven by GitHub Actions and triggered by versioned Git tags.

Release Pipeline Architecture ​

The workflow is defined in .github/workflows/release.yml and consists of four distinct jobs that enforce strict quality gates before a package is made public.

Workflow Trigger and Concurrency ​

The pipeline is triggered by the push of a tag matching the v*.*.* pattern or via manual workflow_dispatch.github/workflows/release.yml To prevent race conditions during publishing, it uses a concurrency group release-production with cancel-in-progress: false.github/workflows/release.yml

Release Data Flow and Code Entities ​

The following diagram illustrates how the release workflow interacts with codebase entities and external registries.

"Release Process Data Flow"

Sources: .github/workflows/release.ymlscripts/verify-tarball.js

Job Details and Implementation ​

1. Verify, Build & Pack (verify-and-pack) ​

This job prepares the distribution artifact. It performs three critical validation steps:

  • Version Consistency Check: It extracts the version from the Git tag and compares it against the version field in package.json. If they do not match, the pipeline fails immediately .github/workflows/release.yml
  • Quality Gates: It executes npm run check (Lint/Typecheck) and npm run test:coverage (Unit/Invariant tests) to ensure the code is stable .github/workflows/release.yml
  • Artifact Validation: After running npm pack, it invokes a custom validation script, scripts/verify-tarball.js.github/workflows/release.yml

verify-tarball.js Implementation ​

The validation script ensures the tarball contains only necessary production files and excludes source code or internal configuration scripts/verify-tarball.js

CategoryFiles/Directories
Requiredpackage.json, dist/, README.md, LICENSE, dist/bin/palee.jsscripts/verify-tarball.js
Forbiddensrc/, test/, .github/, planning/, coverage/scripts/verify-tarball.js

Sources: .github/workflows/release.ymlscripts/verify-tarball.js

2. Idempotent NPM Publishing (publish-npm) ​

Publishing waits on two jobs: verify-and-pack, which produces the artifact it uploads, and test-platforms, which runs the same operating-system and Node matrix CI runs .github/workflows/release.yml. The gate lives here rather than depending on CI's tag run because a GitHub Actions job cannot depend on another workflow's result — without it, a macOS or Windows failure could occur while the ubuntu-only release path published anyway. test/ci-workflow-matrix.test.ts asserts the two matrices stay identical, so the release cannot quietly gate a narrower claim than CI tests.

The publishing job uses the npm-release environment and requires id-token: write for OIDC-based authentication .github/workflows/release.yml

To ensure the pipeline is idempotent (safe to re-run), it performs a pre-check using npm view. It queries the registry for the specific version being released; if the version already exists, the publish step is skipped to avoid "cannot modify existing version" errors .github/workflows/release.yml

Manifest values are data, never shell syntax (ADR-0037) ​

Every value this pipeline reads from package.json — the version and the tarball filename — reaches its run: block through env: and is quoted at the point of use, never interpolated. scripts/release-manifest.js validates both before scripts/pack-package.js writes the GITHUB_OUTPUT lines every downstream job consumes, so a version that is not a release semver, or a name the registry would not accept, fails the pack job instead of reaching a credentialed runner. scripts/verify-tarball.js passes the filename to tar as a single argv element (execFileSync), so there is no shell to parse it.

Sources: scripts/release-manifest.js, docs/adr/0037-manifest-values-are-data.md, .github/workflows/release.yml

3. GitHub Release Creation (github-release) ​

Once the NPM publish is successful, the workflow creates a GitHub Release using softprops/action-gh-release. This step:

Sources: .github/workflows/release.yml

4. Post-Release Smoke Test (smoke-test) ​

The final job runs on windows-latest to verify the package's behavior in a non-POSIX environment.

Because the NPM registry often has a slight propagation delay (replication lag), the smoke test implements a retry loop. It attempts to install the package globally up to 30 times, waiting 10 seconds between each attempt .github/workflows/release.yml

The real-command smoke ships with the repository, not with the published package, so the job's first step is a pinned actions/checkout .github/workflows/release.yml

After installation, it verifies:

  1. Binary Availability: Running palee --version.github/workflows/release.yml
  2. Version Accuracy: The CLI output must exactly match the release version .github/workflows/release.yml
  3. Command Execution: Running palee --help to ensure dependencies like commander are correctly resolved .github/workflows/release.yml
  4. Real Command: Runs scripts/smoke-installed.js, which executes palee next --json against an isolated temporary vault outside the checkout and asserts the documented empty-state JSON contract .github/workflows/release.yml

"Smoke Test Execution Flow"

Sources: .github/workflows/release.yml

Distribution Configuration ​

The package contents are defined by the files allowlist in package.json; there is no .npmignore. The allowlist publishes the compiled output and changelog, and npm always includes package.json, README.md, and LICENSE:

  • package.json: Always included by npm. It defines the entry points for the compiled code, and the bin field maps the palee command to dist/bin/palee.js package.json
  • files allowlist: Publishes dist/src, dist/bin, dist/package.json, and CHANGELOG.md package.json. Source, tests, and planning documents are excluded by omission rather than by an ignore file.

Sources: package.json

Released under the MIT License.