GitHub Workflows Guide for Automated Publishing
January 21, 2026 · View on GitHub
This document explains the npm package publishing workflow for the tshtml monorepo, covering CI (Continuous Integration), version management, and release automation.
Overview
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
Key Technologies
- 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
Workflow Files
Three GitHub Actions workflow files control the process:
1. .github/workflows/ci.yml - Continuous Integration
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.
2. .github/workflows/publish.yml - Automated Publishing
Triggers: Runs on pushes to main when changes touch .changeset/, package folders, or the publish workflow itself
Two-phase process:
Phase 1: Changeset → Version PR
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
Phase 2: Publish to npm
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.
3. .github/workflows/docs.yml - Documentation Deployment
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.
Local Development (Recommended)
For day-to-day development, use the monorepo’s npm link tooling so you can iterate on tshtml / tshtml-loader without publishing.
Link the monorepo packages
From the repository root:
npm run link
This uses the root scripts:
npm run link: linkstshtml, then linkstshtml-loader(and links it against the localtshtml)npm run unlink: removes the global links
Use the linked packages in a consuming project
In your Angular (or other) project:
npm link tshtml tshtml-loader
When done:
npm unlink tshtml tshtml-loader
Optional: “as-published” tarball testing
If you specifically want to test the exact tarballs that would be published (a closer approximation to npm install), you can still use:
npm run pack
This builds both packages and writes dist/*.tgz at the repo root.
Step 1: Create a Changeset (During Development)
When you make changes that should be published, create a changeset:
cd tshtml
npx changeset add
This 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 Y
Commit this file as part of your PR. Do NOT commit the generated package.json/CHANGELOG files - the workflow creates those automatically.
Step 2: Submit PR with Changeset
When your PR is merged to main:
- CI workflow runs (tests, coverage)
- Once merged, publish workflow detects the changeset file
Step 3: Changeset Action Creates Version PR
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.
Step 4: Merge Version PR to Trigger Publishing
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)
Version Numbering Strategy
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.
Version Types
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 |
Changeset Configuration
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
What Happens During Publishing
Build Phase
npm run build # Builds both packages
npm run test # Verifies tests pass
Each 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.
Publish Phase
changeset publish
When 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.
Authentication
Recommended: npm trusted publishing (OIDC)
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.
Step 1: Configure trusted publishers on npmjs.com
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.
Step 2: Ensure the workflow has OIDC permission
The publish workflow must include:
permissions:
id-token: write
This repo’s publish workflow already requests this permission. It also pins npm to a version that supports trusted publishing.
Fallback: token-based publishing (not preferred)
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.
Troubleshooting
Problem: Tests fail in CI workflow
Solution:
- Fix the code locally
- Ensure
npm testpasses locally - Push to your PR branch - CI will re-run
Problem: Changeset action doesn't create Version PR
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-branch
Problem: Version PR shows wrong version numbers
Cause: Manual package.json edits before changesets ran
Solution: Don't manually edit package versions. Let changesets manage them.
Problem: "Invalid tag format" or npm publish errors
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
Problem: Packages published with different versions
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 Reference
| 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 |
Related Documentation
- Changesets Documentation
- GitHub Actions Documentation
- npm Publishing Guide
- npm Provenance
- npm Trusted Publishing (OIDC)
Quick Reference
Common Commands
# 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 version
Workflow Status Checks
Check 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.
Dependency Management with Dependabot
The project uses Dependabot for automated dependency updates. Configuration is in .github/dependabot.yml.
What Dependabot Does
Automatically creates PRs when:
- npm packages have updates available (weekly)
- GitHub Actions need updates (weekly)
Review Process
- 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
Configuration
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.