This document explains the npm package publishing workflow for the tshtml monorepo, covering CI (Continuous Integration), version management, and release automation.
The monorepo uses GitHub Actions to automate:
- Testing on every pull request and commit (CI)
- Version management using Changesets
- Publishing to npm with provenance attestation
- Changesets: Manages version bumping and changelog generation across both packages in sync
- GitHub Actions: Automated workflows that run on specific triggers
- npm trusted publishing (OIDC): Publish from GitHub Actions without long-lived npm tokens
- npm link: Local development with symlinked packages
- npm pack: Optional tarballs for “as-published” testing
- npm provenance: Cryptographic proof a package was built/published from your CI
Three GitHub Actions workflow files control the process:
Triggers: Runs on every:
- Push to
mainordevelopbranches - Pull request to
mainordevelopbranches
What it does:
- Tests on Node.js 18 and 20
- Runs
npm testto execute test suite - Runs
npm run coverageto ensure coverage thresholds are met - Runs
npm auditto check for vulnerable dependencies
Status: Fails if tests fail or coverage thresholds not met - PRs cannot be merged unless CI passes.
Triggers: Runs on pushes to main when changes touch .changeset/, package folders, or the publish workflow itself
Two-phase process:
The changesets/action automatically:
- Detects all changesets in
.changeset/folder - Bumps versions in package.json files (synchronized across both packages)
- Generates/updates CHANGELOG.md files
- Creates a "Publish Packages" PR with all changes
You should:
- Review the PR for correctness
- Merge the PR when ready to publish
- This triggers Phase 2 automatically
After the Version PR is merged, the workflow:
- Builds both packages (
npm run build) - Runs full test suite
- Publishes packages to npm (via Changesets)
Important: Publish only happens after Version PR merge. You control the timing.
Triggers: Runs on pushes to main when changes touch:
- Source files in
tshtml/src/** tshtml/package.json- Documentation in
docs/** - The docs workflow file itself
- Can also be manually triggered via
workflow_dispatch
What it does:
- Builds TypeDoc documentation (
npm run docs) - Uploads documentation artifacts to GitHub Pages
- Deploys to https://xorets.github.io/tshtml/
Status: Documentation is automatically updated on every push to main that affects source code or docs.
For day-to-day development, use the monorepo’s npm link tooling so you can iterate on tshtml / tshtml-loader without publishing.
From the repository root:
npm run linkThis uses the root scripts:
npm run link: linkstshtml, then linkstshtml-loader(and links it against the localtshtml)npm run unlink: removes the global links
In your Angular (or other) project:
npm link tshtml tshtml-loaderWhen done:
npm unlink tshtml tshtml-loaderIf you specifically want to test the exact tarballs that would be published (a closer approximation to npm install), you can still use:
npm run packThis builds both packages and writes dist/*.tgz at the repo root.
When you make changes that should be published, create a changeset:
cd tshtml
npx changeset addThis opens an interactive prompt:
? Which packages would you like to include? (use arrow keys / space to select)
✓ tshtml
✓ tshtml-loader
? Which packages should have a major bump? (none)
? Which packages should have a minor bump?
✓ tshtml
✓ tshtml-loader
? Please enter a summary for this change (what did you change?)
> Add new feature X, fix bug Y, etc.
A new file is created in .changeset/ with a random name (e.g., .changeset/blue-whales-dance.md):
---
"tshtml": minor
"tshtml-loader": minor
---
Added new feature X, fixed bug YCommit this file as part of your PR. Do NOT commit the generated package.json/CHANGELOG files - the workflow creates those automatically.
When your PR is merged to main:
- CI workflow runs (tests, coverage)
- Once merged, publish workflow detects the changeset file
GitHub Actions automatically:
- Creates a new PR titled "Changeset: publish packages"
- Updates all package.json versions (both bump together due to
"fixed"configuration) - Generates CHANGELOG.md entries
- Commits these changes to the PR
Review the PR to ensure versions and changelogs look correct.
When you merge the Version PR:
- The publish workflow automatically runs
- Packages are built and tested again (safety check)
- Both packages published to npm with
--provenance - Versions must be synchronized (1.5.0 for both, never 1.5.0 and 1.4.0)
Both packages always version together. This is configured in .changeset/config.json:
{
"fixed": [["tshtml", "tshtml-loader"]]
}Examples:
- ✅
tshtml@1.4.0+tshtml-loader@1.4.0(good) - ❌
tshtml@1.4.0+tshtml-loader@1.3.0(bad - don't do this)
This ensures users can always use compatible versions together.
When creating a changeset, select one of:
| Type | Version Change | When to Use | Example |
|---|---|---|---|
| Major | 1.0.0 → 2.0.0 | Breaking changes | Removed API |
| Minor | 1.0.0 → 1.1.0 | New features | Added new function |
| Patch | 1.0.0 → 1.0.1 | Bug fixes | Fixed crash |
Located in .changeset/config.json:
{
"$schema": "https://unpkg.com/@changesets/config@3.1.2/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [["tshtml", "tshtml-loader"]],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}Key settings:
"fixed": Both packages version together"access": "public": Publish as public packages on npm"baseBranch": "main": Changesets PR created against main"updateInternalDependencies": "patch": Bump tshtml-loader patch version if tshtml changes
npm run build # Builds both packages
npm run test # Verifies tests passEach package generates:
dist/index.js+dist/index.d.ts(CommonJS with type definitions)- Supporting modules in
dist/ - Files excluded via
.npmignore: source code, tests, coverage, etc.
changeset publishWhen publishing via trusted publishing, npm automatically generates provenance attestations and no npm publish token is required in CI.
What's Published:
- Only files in
dist/folder (per thefilesfield in each package.json) - Type definitions (.d.ts files)
- package.json with metadata
- README.md (if linked in package.json)
Provenance: npm records cryptographic proof that:
- Package came from this GitHub repository
- Built by GitHub Actions workflow (specific commit/run)
- Has full audit trail at npm registry
Users can verify by inspecting npm view tshtml@1.4.0 --json and looking for provenance / attestation-related fields.
npm now recommends trusted publishing for GitHub Actions. This creates a trust relationship between npm and GitHub Actions using OIDC, so publishes do not require a long-lived NPM_TOKEN secret.
High-level flow:
- You configure each package on npmjs.com to trust this repo + workflow filename.
- During
npm publish, the npm CLI detects the GitHub Actions OIDC environment and authenticates using short-lived credentials.
Prerequisites / limitations:
- Requires GitHub-hosted runners (self-hosted runners are not currently supported).
- Requires npm CLI 11.5.1+.
- Trusted publishing applies to
npm publish. If you have private npm dependencies,npm cimay still need a separate read-only token.
Do this for each package (tshtml and tshtml-loader):
- Open the package page on npmjs.com → Settings
- Find Trusted Publisher
- Select GitHub Actions
- Enter:
- Organization/user: your GitHub org/user
- Repository:
tshtml - Workflow filename:
publish.yml - (Optional) Environment name: only if you publish via a GitHub Environment
Note: A package can have only one trusted publisher configuration at a time.
The publish workflow must include:
permissions:
id-token: writeThis repo’s publish workflow already requests this permission. It also pins npm to a version that supports trusted publishing.
If you cannot use trusted publishing (for example, due to CI constraints), you can still publish with an automation token stored as a GitHub secret. Prefer trusted publishing when possible.
Solution:
- Fix the code locally
- Ensure
npm testpasses locally - Push to your PR branch - CI will re-run
Likely cause: Changeset file not committed to repository
Solution:
git status # Verify .changeset/*.md exists
git add .changeset/
git commit -m "Add changeset"
git push origin your-branchCause: Manual package.json edits before changesets ran
Solution: Don't manually edit package versions. Let changesets manage them.
Cause: Trusted publishing not configured correctly (or missing/expired token if using fallback token publishing)
Solution:
- Verify npm trusted publisher settings for both packages (repo + workflow filename must match exactly)
- Ensure the workflow has
permissions: id-token: writeand runs on a GitHub-hosted runner - If using fallback token publishing, verify the token secret exists and is not expired
Cause: Bypassed changeset workflow or manual npm publish
Solution:
- Always use Changeset workflow (don't
npm publishmanually) - The
fixedconfiguration in.changeset/config.jsonenforces sync versions
| File | Purpose |
|---|---|
.github/workflows/ci.yml |
Test workflow (runs on PR/push) |
.github/workflows/publish.yml |
Publish workflow (runs on changeset) |
.changeset/config.json |
Changesets configuration |
.changeset/*.md |
Individual changeset entries |
tshtml/package.json |
Package 1 metadata + version |
tshtml-loader/package.json |
Package 2 metadata + version |
tshtml/.npmignore |
Files to exclude from npm publish |
tshtml-loader/.npmignore |
Files to exclude from npm publish |
package.json (root) |
Monorepo workspace config |
- Changesets Documentation
- GitHub Actions Documentation
- npm Publishing Guide
- npm Provenance
- npm Trusted Publishing (OIDC)
# Create a changeset (do this during development)
npx changeset add
# Test what would be published (don't commit this)
npm pack --dry-run -w tshtml
npm pack --dry-run -w tshtml-loader
# View changeset status
ls .changeset/
# Manually trigger version bump (only for testing)
npx changeset versionCheck workflow runs in GitHub:
- Go to repository → Actions tab
- "Continuous Integration" = PR/push tests
- "Publish Packages" = Publishing workflow
View logs by clicking the workflow run name.
The project uses Dependabot for automated dependency updates. Configuration is in .github/dependabot.yml.
Automatically creates PRs when:
- npm packages have updates available (weekly)
- GitHub Actions need updates (weekly)
- Dependabot creates a PR with dependency update(s)
- CI runs automatically (tests, coverage, audit)
- Review the changes and security advisories
- Merge when ready - no manual version bumping needed
Located in .github/dependabot.yml:
- npm: Weekly updates on Mondays at 3 AM
- github-actions: Weekly updates on Mondays at 4 AM
- Labels: Auto-labeled as
dependenciesorcifor filtering - Ignore Rules: Major Angular updates ignored (review manually)
To disable Dependabot, remove the .github/dependabot.yml file.