| name | release-package |
| description | Create and publish a new version of docent to npm registry |
| group | docent |
| author | @tnez |
| version | 1.0.0 |
| keywords | ["release","npm","publishing","version"] |
Runbook: Release Package
Purpose: Create and publish a new version of docent to npm registry
Owner: Maintainers
Last Updated: 2025-10-17
Frequency: As needed for releases
Overview
This runbook covers the complete release process from version bumping through automated npm publishing. The workflow is:
- Prepare locally - Version bump, changelog, validation
- Push tagged commit - Triggers automated publishing
- Monitor release - Watch GitHub Actions complete the publish
Expected duration: 20-30 minutes (plus monitoring time)
Prerequisites
Required Tools
git command line tool
gh CLI (GitHub CLI) - installed and authenticated
npm - Node.js package manager
- Node.js >= 18.0.0
Required Access
- Write access to the repository
- Ability to create tags and push to main branch
- npm publish access (configured in GitHub secrets)
Pre-Flight Checklist
Before starting, ensure:
Part 1: Preparation (Local)
Step 1: Pre-Release Health Check
Purpose: Validate code quality and documentation before release
Commands:
npm test
npm run build
npm run lint:md
git grep -n "console\.log\|debugger\|TODO.*remove\|FIXME.*before.*release\|XXX" src/ || echo "✓ No obvious debug code"
git grep -n "\.only\|\.skip" test/ && echo "⚠ Remove .only/.skip from tests" || echo "✓ No test-only markers"
git status --short
npm run docent audit . 2>/dev/null || echo "Note: docent audit available via MCP"
Validation:
- All tests pass
- Build succeeds without errors
- No obvious debug code in src/
- No
.only or .skip in tests
- Working directory is clean
- Markdown linting passes (or known issues documented)
If step fails:
- Fix test failures before proceeding
- Remove debug code and test markers
- Commit any fixes:
git add . && git commit -m "chore: pre-release cleanup"
- Document markdown lint issues if non-blocking (see runbook: fix-markdown-lint)
Step 2: Determine Version Number
Purpose: Decide what version number to use based on changes
Semantic Versioning Rules:
- Major (X.0.0): Breaking changes, incompatible API changes
- Minor (0.X.0): New features, backwards-compatible
- Patch (0.0.X): Bug fixes, backwards-compatible
Commands:
npm pkg get version
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
if [ -n "$LAST_TAG" ]; then
echo "Commits since $LAST_TAG:"
git log $LAST_TAG..HEAD --oneline --no-decorate
echo -e "\nAPI changes:"
git diff $LAST_TAG -- src/mcp/ --stat
else
echo "No previous tags found - this is the first release"
git log --oneline --no-decorate
fi
git grep "BREAKING:" CHANGELOG.md && echo "⚠ Breaking changes found in CHANGELOG"
Validation:
- Current version is identified
- Changes are categorized (breaking, feature, fix)
- New version number determined following semver
- Breaking changes are identified
Decision Point:
Choose version bump type:
patch - Bug fixes only (0.5.0 → 0.5.1)
minor - New features, no breaking changes (0.5.0 → 0.6.0)
major - Breaking changes (0.5.0 → 1.0.0)
If step fails:
- No tags exist yet: start with 0.1.0 or 1.0.0
- Can't determine changes: review commit history manually
Step 3: Update Version Number
Purpose: Bump version in package.json
Commands:
npm version patch --no-git-tag-version
npm version minor --no-git-tag-version
npm version major --no-git-tag-version
npm version 0.6.0 --no-git-tag-version
NEW_VERSION=$(npm pkg get version | tr -d '"')
echo "New version: $NEW_VERSION"
Validation:
package.json shows new version number
package-lock.json also updated with new version (2 places)
- Files are modified but not committed yet
- Verify:
npm pkg get version
- Verify:
git status shows both package.json and package-lock.json modified
If step fails:
- Working directory must be clean (commit or stash changes)
- Manually edit package.json if npm version fails
Step 4: Update CHANGELOG.md
Purpose: Document changes in this release for users
Commands:
$EDITOR CHANGELOG.md
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
[ -n "$LAST_TAG" ] && git log $LAST_TAG..HEAD --oneline
Required CHANGELOG Structure:
## [X.Y.Z] - YYYY-MM-DD
### Breaking Changes (if any)
- **BREAKING**: Clear description of breaking change
- Migration guide: how to update code
### Added
- New features and capabilities
- User-facing improvements
### Changed
- Changes to existing functionality
- Behavior modifications
### Fixed
- Bug fixes and corrections
### Technical
- Internal improvements, refactoring
- Dependency updates
- Development tooling changes
Validation:
- New version section added at top of CHANGELOG
- Date set to today (YYYY-MM-DD format)
- Changes categorized appropriately
- Each change describes user-facing value (not just commit messages)
- Breaking changes clearly marked with BREAKING: prefix
- Breaking changes include migration guidance
- Follows Keep a Changelog format
If step fails:
- Review recent commits:
git log --oneline --since="2 weeks ago"
- Check closed issues:
gh issue list --state closed --limit 20
- Check merged PRs:
gh pr list --state merged --limit 20
Step 5: Documentation Review
Purpose: Ensure documentation accurately reflects the release
Commands:
git grep -n "0\.[0-9]\.[0-9]" README.md docs/*.md | grep -v CHANGELOG.md | grep -v "version.*latest"
Validation:
- README accurately describes current features
- No hardcoded old version numbers (except in CHANGELOG)
- Examples match current API
- No references to removed features
- Installation instructions are current
If step fails:
- Update documentation as needed
- Commit doc updates before proceeding
- Consider if docs warrant their own changelog entry
Step 6: Final Build and Test
Purpose: Ensure everything builds and tests pass with new version
Commands:
rm -rf lib/
npm install
npm run build
npm test
npm pack --dry-run | head -20
npm pack --dry-run | grep "Tarball Contents"
Validation:
- Build completes without errors
- All tests pass (14 tests expected for docent)
lib/ directory contains compiled JavaScript
- Package includes correct files:
/bin, /lib, /templates, CHANGELOG.md
- Package does NOT include:
src/, test/, docs/, .git/
- Size is reasonable (< 1MB for docent)
If step fails:
- Fix build errors before proceeding
- Fix test failures - do not release broken code
- Update
files array in package.json if package contents wrong
- Commit fixes and restart from Step 1
Step 7: Review Changes
Purpose: Final review of all changes before committing
Commands:
git status
git diff
git diff package.json
git diff CHANGELOG.md
git status --short
Validation:
- Only expected files are modified (package.json, CHANGELOG.md, possibly docs)
- Version number is correct in package.json
- CHANGELOG.md is complete and accurate
- No unintended changes
- No debug code or temporary files
If step fails:
- Use
git checkout -- <file> to discard unwanted changes
- Make corrections and re-review
Step 8: Commit and Tag Release
Purpose: Create release commit and git tag
Commands:
VERSION=$(npm pkg get version | tr -d '"')
echo "Preparing release v$VERSION"
git add package.json package-lock.json CHANGELOG.md
git commit -m "chore: release v${VERSION}"
git tag -a "v${VERSION}" -m "Release v${VERSION}"
git log -1 --stat
git show "v${VERSION}" --stat
Validation:
- Commit message follows format:
chore: release vX.Y.Z
- Commit includes: package.json, package-lock.json, CHANGELOG.md (and any docs)
- Tag is created with format
vX.Y.Z (lowercase v)
- Tag points to release commit
- Commit and tag are local only (not pushed yet)
- Verify files in commit:
git show --stat
If step fails:
- Fix commit message:
git commit --amend -m "correct message"
- Delete and recreate tag:
git tag -d vX.Y.Z && git tag -a vX.Y.Z -m "..."
- Unstage unwanted files:
git reset HEAD <file>
Step 9: Final Pre-Push Validation
Purpose: Last check before pushing (triggers automated publish)
Commands:
git status
npm run build && npm test && npm run lint:md || echo "⚠ Local checks failed"
git log -1 --oneline
VERSION=$(npm pkg get version | tr -d '"')
git tag -l "v${VERSION}"
git branch --show-current
gh run list --branch main --limit 5 --json conclusion,name,startedAt --jq '.[] | "\(.startedAt | split("T")[0]) \(.name): \(.conclusion)"'
Validation:
- Working directory is clean
- Local build and tests pass
- Commit message is correct
- Tag exists and matches version
- On main branch
- Latest CI run on main is successful
If step fails:
- Fix any issues found
- Do NOT push until everything is validated
- May need to amend commit or delete/recreate tag
Part 2: Publish (Automated via GitHub Actions)
Step 10: Push Release
Purpose: Push commit and tag to trigger automated publishing
⚠️ POINT OF NO RETURN: Once you push the tag, GitHub Actions will automatically publish to npm. Verify everything in Steps 1-9 before proceeding.
Commands:
git push origin main --follow-tags
git status
git ls-remote --tags origin | grep "$(npm pkg get version | tr -d '"')"
Validation:
- Commit is pushed to origin/main
- Tag is pushed to origin
- No errors during push
- Remote shows the tag
If step fails:
- Check network connectivity
- Verify push permissions
- If behind remote:
git pull --rebase origin main then retry
- If tag fails:
git push origin vX.Y.Z
Step 11: Monitor GitHub Actions Publish
Purpose: Watch the automated publish workflow complete
Commands:
VERSION=$(npm pkg get version | tr -d '"')
gh run watch --repo tnez/docent
gh run list --workflow=publish.yml --limit 5
gh run view --log
What the Publish Workflow Does:
- ✅ Checks out code at the tagged commit
- ✅ Sets up Node.js and installs dependencies
- ✅ Runs build:
npm run build
- ✅ Runs tests:
npm test
- ✅ Publishes to npm:
npm publish --access public
- ✅ Creates GitHub Release with CHANGELOG notes
- ✅ Uploads release assets
Expected Timeline:
- Workflow starts: ~30 seconds after push
- Build/test phase: 2-3 minutes
- npm publish: 30-60 seconds
- GitHub release: 15-30 seconds
- Total: ~4-5 minutes
Monitoring Checklist:
If workflow fails:
See Troubleshooting section below for common issues and recovery procedures.
Step 12: Verify Publication
Purpose: Confirm package is available and installable
Commands:
sleep 30
VERSION=$(npm pkg get version | tr -d '"')
npm view @tnezdev/docent@${VERSION} version
npm view @tnezdev/docent
mkdir -p /tmp/test-npm-install
cd /tmp/test-npm-install
npm install @tnezdev/docent
./node_modules/.bin/docent --version 2>/dev/null || echo "Binary check"
ls -la node_modules/@tnezdev/docent/
cd -
rm -rf /tmp/test-npm-install
Validation:
- Package appears on npm registry with correct version
- Published date is today
- Package installs successfully
- Binary is executable
- Templates directory exists
- lib/ directory contains compiled code
If step fails:
- Wait longer for npm propagation (up to 5 minutes)
- Check npm registry status: https://status.npmjs.org/
- Review GitHub Actions logs for publish errors
- See Troubleshooting section
Step 13: Verify GitHub Release
Purpose: Confirm GitHub release was created correctly
Commands:
VERSION=$(npm pkg get version | tr -d '"')
gh release view "v${VERSION}" --web
gh release view "v${VERSION}"
gh release view "v${VERSION}" --json body --jq '.body' | head -20
Validation:
- GitHub release exists for tag
- Release notes match CHANGELOG section
- Release is marked as "Latest release" (if applicable)
- Release assets include source tarball
- No "Pre-release" flag (unless intentional)
If step fails:
- Check GitHub Actions logs for release creation errors
- Release can be created manually if workflow failed:
VERSION=$(npm pkg get version | tr -d '"')
awk "/## \[${VERSION}\]/,/## \[/" CHANGELOG.md | head -n -2 | tail -n +3 > /tmp/release-notes.md
gh release create "v${VERSION}" --title "Release v${VERSION}" --notes-file /tmp/release-notes.md
rm /tmp/release-notes.md
Step 14: Post-Release Verification
Purpose: Final checks and cleanup
Commands:
gh run list --branch main --limit 3
gh issue list --label "bug" --limit 5
git status
git pull origin main
Validation:
- All CI checks passing on main
- No critical bugs reported
- Local repo is in sync with remote
- Working directory is clean
Post-Release Tasks:
Step 15: Announce Release (Optional)
Purpose: Notify users and contributors of new release
Commands:
VERSION=$(npm pkg get version | tr -d '"')
echo "GitHub Release: https://github.com/tnez/docent/releases/tag/v${VERSION}"
echo "npm Package: https://www.npmjs.com/package/@tnezdev/docent/v/${VERSION}"
Announcement Channels (as applicable):
- GitHub Discussions (if enabled)
- Project Discord/Slack
- Twitter/Social media
- Dev.to or blog post (for major releases)
- Email to contributors list
Announcement Template:
🎉 docent v${VERSION} is now available!
**Highlights:**
- [Key feature 1]
- [Key feature 2]
- [Important fix]
**Install/Update:**
`npm install @tnezdev/docent@latest`
**Full changelog:** https://github.com/tnez/docent/releases/tag/v${VERSION}
Troubleshooting
Common Workflow Failures
Issue: Publish Workflow Test Failures
Symptoms:
- GitHub Actions workflow fails at test step
- Tests were passing locally
Resolution:
- Check workflow logs:
gh run view --log
- Look for environment differences (Node version, OS)
- If reproducible locally:
- Fix tests
- Commit fix
- Create new patch release
- If not reproducible:
- Re-run workflow:
gh run rerun
- Check GitHub Actions status
Issue: npm Publish Permission Denied
Symptoms:
- Workflow fails at publish step with 403 or permission error
- Error: "You do not have permission to publish"
Resolution:
- Verify npm token in GitHub secrets:
- Settings → Secrets → Actions
- Check
NPM_TOKEN exists and is not expired
- Regenerate npm token:
- Log in to npmjs.com
- Generate new automation token
- Update GitHub secret
- Re-run failed workflow:
gh run rerun --failed
Issue: Version Already Published
Symptoms:
- Workflow fails with "cannot publish over existing version"
Resolution:
This means the version was already published (possibly from previous attempt).
Option 1: Skip npm publish (if version is correct)
npm view @tnezdev/docent version
VERSION=$(npm pkg get version | tr -d '"')
gh release create "v${VERSION}" --title "Release v${VERSION}" --notes "See CHANGELOG.md"
Option 2: Bump version again (if version was wrong)
git push origin :refs/tags/vX.Y.Z
git tag -d vX.Y.Z
npm version patch --no-git-tag-version
git add package.json
git commit --amend --no-edit
git tag -a "vX.Y.Z" -m "Release vX.Y.Z"
git push origin main --follow-tags --force-with-lease
Issue: GitHub Release Creation Fails
Symptoms:
- npm publish succeeds but GitHub release fails
- Workflow completes with partial success
Resolution:
Create GitHub release manually:
VERSION=$(npm pkg get version | tr -d '"')
awk "/## \[${VERSION}\]/,/## \[/" CHANGELOG.md | head -n -2 | tail -n +3 > /tmp/release-notes.md
gh release create "v${VERSION}" \
--title "Release v${VERSION}" \
--notes-file /tmp/release-notes.md
rm /tmp/release-notes.md
Issue: Breaking Change in Patch Release
Symptoms:
- Realized after publishing that a breaking change was in a patch release
- Semantic versioning was violated
Resolution:
Immediate action:
npm deprecate @tnezdev/docent@X.Y.Z "Breaking changes - use X+1.0.0 instead"
npm version major --no-git-tag-version
git add package.json package-lock.json CHANGELOG.md
git commit -m "chore: release vX+1.0.0 (correcting semver violation)"
Emergency Rollback
If critical bug discovered immediately after publish:
npm deprecate @tnezdev/docent@X.Y.Z "Critical bug - use X.Y.Z+1"
git fetch --tags
git checkout "vX.Y.Z"
git checkout -b hotfix/vX.Y.Z+1
npm run build && npm test
git checkout main
git merge hotfix/vX.Y.Z+1
npm version patch --no-git-tag-version
git add package.json package-lock.json CHANGELOG.md
git commit -m "chore: hotfix vX.Y.Z+1"
git tag -a "vX.Y.Z+1" -m "Hotfix vX.Y.Z+1"
git push origin main --follow-tags
gh run watch
Note: Cannot unpublish from npm after 72 hours. Deprecation + new version is the only option.
When to Escalate
Escalate if:
- Critical security vulnerability discovered in published version
- npm registry having extended outage
- GitHub Actions repeatedly failing for infrastructure reasons
- Permissions issues cannot be resolved
- Breaking changes accidentally published
Escalation Contact:
- npm support (for registry issues)
- GitHub support (for Actions issues)
- Security team (for vulnerabilities)
- Package owner/maintainer
Validation Checklist
After completing all steps:
Quick Reference
Full Release Command Sequence
git checkout main && git pull origin main
git status
npm test && npm run build
npm version patch --no-git-tag-version
$EDITOR CHANGELOG.md
npm run build && npm test
npm pack --dry-run
VERSION=$(npm pkg get version | tr -d '"')
git add package.json package-lock.json CHANGELOG.md
git commit -m "chore: release v${VERSION}"
git tag -a "v${VERSION}" -m "Release v${VERSION}"
git push origin main --follow-tags
gh run watch
Monitoring Commands
gh run watch
gh run list --workflow=publish.yml --limit 5
gh run view --log
npm view @tnezdev/docent version
npm install @tnezdev/docent
Notes
Important:
- Pushing a version tag triggers automatic npm publishing
- Cannot unpublish from npm after 72 hours
- Always verify locally before pushing
- Breaking changes require major version bump
- GitHub Actions needs valid NPM_TOKEN secret
Gotchas:
--follow-tags only pushes annotated tags (created with -a)
- npm propagation can take 30-60 seconds
- GitHub Actions has ~30 second startup delay
- CHANGELOG format must match for release notes extraction
- Tag format must be
vX.Y.Z (lowercase v prefix)
Related Procedures:
Revision History
| Date | Author | Changes |
|---|
| 2025-10-17 | @tnez | Combined prepare-release and publish-package into single GitOps workflow |