Automates the release process for the ts-oauth2-server project.
Installation
Details
Usage
After installing, this skill will be available to your AI coding assistant.
Verify installation:
skills listSkill Instructions
name: artifacts-builder description: Automates the release process for the ts-oauth2-server project. license: Complete terms in LICENSE
Release Creator Skill
Purpose
Automates the release process for the ts-oauth2-server project by:
- Bumping version numbers in both
package.jsonandjsr.json - Generating changelog entries based on git commits since the last tagged version
- Updating the CHANGELOG.md file with proper formatting
- Maintaining Keep a Changelog format and Semantic Versioning compliance
When to Use This Skill
Use this skill when the user requests:
- Creating a new release
- Bumping the version (patch, minor, major, premajor, preminor, prepatch, prerelease)
- Updating the changelog
- Preparing for a release
- Publishing a new version
Prerequisites
- Git repository with commit history
- Existing tags for version tracking
package.jsonandjsr.jsonfiles presentCHANGELOG.mdfile exists
Core Functionality
Version Bump Types
Supported version bump types (following semver):
- patch: Bug fixes (1.0.0 → 1.0.1)
- minor: New features (1.0.0 → 1.1.0)
- major: Breaking changes (1.0.0 → 2.0.0)
- prepatch: Pre-release patch (1.0.0 → 1.0.1-0)
- preminor: Pre-release minor (1.0.0 → 1.1.0-0)
- premajor: Pre-release major (1.0.0 → 2.0.0-0)
- prerelease: Increment pre-release (1.0.0-0 → 1.0.0-1)
Implementation Steps
1. Get Current Version
import { readFileSync } from 'fs';
function getCurrentVersion(): string {
const pkg = JSON.parse(readFileSync('package.json', 'utf-8'));
return pkg.version;
}
2. Calculate New Version
function bumpVersion(current: string, bumpType: string): string {
const parts = current.split('-');
const [major, minor, patch] = parts[0].split('.').map(Number);
const prerelease = parts[1];
switch (bumpType) {
case 'major':
return `${major + 1}.0.0`;
case 'minor':
return `${major}.${minor + 1}.0`;
case 'patch':
return `${major}.${minor}.${patch + 1}`;
case 'premajor':
return `${major + 1}.0.0-0`;
case 'preminor':
return `${major}.${minor + 1}.0-0`;
case 'prepatch':
return `${major}.${minor}.${patch + 1}-0`;
case 'prerelease':
if (prerelease) {
const num = parseInt(prerelease) || 0;
return `${parts[0]}-${num + 1}`;
}
return `${major}.${minor}.${patch + 1}-0`;
default:
throw new Error(`Invalid bump type: ${bumpType}`);
}
}
3. Get Git Commits Since Last Tag
# Get the last tag
last_tag=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
# Get commits since last tag
if [ -z "$last_tag" ]; then
# No previous tag, get all commits
commits=$(git log --pretty=format:"%s" --no-merges)
else
# Get commits since last tag
commits=$(git log ${last_tag}..HEAD --pretty=format:"%s" --no-merges)
fi
4. Categorize Commits
Parse commits and categorize them based on conventional commit format:
feat:orfeature:→ Added sectionfix:→ Fixed sectionBREAKING CHANGE:or!:→ Changed section (breaking)docs:→ Skip (or note in documentation)chore:,refactor:,test:→ Skip or group under "Changed"security:→ Security section
5. Generate Changelog Entry
Follow Keep a Changelog format:
## [Version] - YYYY-MM-DD
### Added
- New feature A
- New feature B
### Changed
- **BREAKING**: Changed behavior X
- Updated Y
### Fixed
- Fixed bug in Z
- Resolved issue with W
### Security
- Security fix for vulnerability V
6. Update Files
Update both package.json and jsr.json with the new version, and prepend the new changelog entry to CHANGELOG.md under the ## [Unreleased] section.
Usage Examples
Example 1: Patch Release
// User request: "Create a patch release"
const currentVersion = "4.1.1";
const newVersion = "4.1.2";
// 1. Get commits since v4.1.1
// 2. Categorize commits
// 3. Generate changelog entry
// 4. Update package.json, jsr.json, CHANGELOG.md
Example 2: Minor Release with Features
// User request: "Bump minor version and update changelog"
const currentVersion = "4.1.1";
const newVersion = "4.2.0";
// Changelog generated from commits:
// - feat: Add new grant type support
// - feat: Enhance token validation
// - fix: Resolve refresh token issue
Example 3: Major Release with Breaking Changes
// User request: "Create major release for breaking changes"
const currentVersion = "4.1.1";
const newVersion = "5.0.0";
// Identify BREAKING CHANGE commits
// Place them under ### Changed section
Implementation Script
Here's a complete implementation approach:
interface ChangelogEntry {
added: string[];
changed: string[];
deprecated: string[];
removed: string[];
fixed: string[];
security: string[];
}
async function createRelease(bumpType: string): Promise<void> {
// 1. Read current version
const currentVersion = getCurrentVersion();
console.log(`Current version: ${currentVersion}`);
// 2. Calculate new version
const newVersion = bumpVersion(currentVersion, bumpType);
console.log(`New version: ${newVersion}`);
// 3. Get last tag and commits
const lastTag = await getLastTag();
const commits = await getCommitsSinceTag(lastTag);
console.log(`Found ${commits.length} commits since ${lastTag || 'beginning'}`);
// 4. Parse and categorize commits
const changelog = categorizeCommits(commits);
// 5. Generate changelog entry
const changelogEntry = generateChangelogEntry(newVersion, changelog);
console.log('Generated changelog entry');
// 6. Update files
await updatePackageJson(newVersion);
await updateJsrJson(newVersion);
await updateChangelog(changelogEntry);
console.log(`✓ Successfully prepared release ${newVersion}`);
console.log('Next steps:');
console.log(' 1. Review the changes');
console.log(' 2. Commit: git add -A && git commit -m "chore: release v' + newVersion + '"');
console.log(' 3. Tag: git tag v' + newVersion);
console.log(' 4. Push: git push && git push --tags');
}
function categorizeCommits(commits: string[]): ChangelogEntry {
const entry: ChangelogEntry = {
added: [],
changed: [],
deprecated: [],
removed: [],
fixed: [],
security: [],
};
for (const commit of commits) {
const lower = commit.toLowerCase();
if (lower.includes('breaking change') || lower.includes('!:')) {
entry.changed.push(commit.replace(/^[^:]+:\s*/, ''));
} else if (lower.startsWith('feat:') || lower.startsWith('feature:')) {
entry.added.push(commit.replace(/^[^:]+:\s*/, ''));
} else if (lower.startsWith('fix:')) {
entry.fixed.push(commit.replace(/^[^:]+:\s*/, ''));
} else if (lower.startsWith('security:')) {
entry.security.push(commit.replace(/^[^:]+:\s*/, ''));
} else if (lower.startsWith('deprecate:')) {
entry.deprecated.push(commit.replace(/^[^:]+:\s*/, ''));
} else if (lower.startsWith('remove:')) {
entry.removed.push(commit.replace(/^[^:]+:\s*/, ''));
}
// Skip chore, docs, test, refactor, style, etc.
}
return entry;
}
function generateChangelogEntry(version: string, changelog: ChangelogEntry): string {
const date = new Date().toISOString().split('T')[0];
let entry = `## [${version}] - ${date}\n\n`;
if (changelog.added.length > 0) {
entry += '### Added\n';
for (const item of changelog.added) {
entry += `- ${item}\n`;
}
entry += '\n';
}
if (changelog.changed.length > 0) {
entry += '### Changed\n';
for (const item of changelog.changed) {
entry += `- ${item}\n`;
}
entry += '\n';
}
if (changelog.deprecated.length > 0) {
entry += '### Deprecated\n';
for (const item of changelog.deprecated) {
entry += `- ${item}\n`;
}
entry += '\n';
}
if (changelog.removed.length > 0) {
entry += '### Removed\n';
for (const item of changelog.removed) {
entry += `- ${item}\n`;
}
entry += '\n';
}
if (changelog.fixed.length > 0) {
entry += '### Fixed\n';
for (const item of changelog.fixed) {
entry += `- ${item}\n`;
}
entry += '\n';
}
if (changelog.security.length > 0) {
entry += '### Security\n';
for (const item of changelog.security) {
entry += `- ${item}\n`;
}
entry += '\n';
}
return entry;
}
Best Practices
- Always verify versions match: Ensure package.json and jsr.json versions are in sync before and after bumping
- Review commits carefully: Not all commits should appear in changelog
- Use conventional commits: Encourage the team to use conventional commit format
- Breaking changes: Always highlight breaking changes prominently
- Date format: Use ISO date format (YYYY-MM-DD) for consistency
- Manual review: Always review generated changelog before committing
Error Handling
- No git repository: Warn user and exit gracefully
- Uncommitted changes: Warn user to commit or stash changes first
- Invalid bump type: Show available options and exit
- Version mismatch: If package.json and jsr.json versions don't match, warn user
- No commits: If no commits since last tag, warn user and ask if they want to proceed
Output Format
The skill should provide clear feedback:
📦 Current version: 4.1.1
🔼 Bumping version: patch
📝 New version: 4.1.2
🔍 Found 5 commits since v4.1.1
📋 Categorized commits:
- Added: 2
- Fixed: 3
✍️ Generated changelog entry
✅ Updated package.json
✅ Updated jsr.json
✅ Updated CHANGELOG.md
✓ Successfully prepared release v4.1.2
Next steps:
1. Review the changes
2. Commit: git add -A && git commit -m "chore: release v4.1.2"
3. Tag: git tag v4.1.2
4. Push: git push && git push --tags
Integration with Project
This skill respects the project's:
- Semantic versioning (as stated in CHANGELOG.md)
- Keep a Changelog format (as shown in existing CHANGELOG.md)
- Git workflow and tagging conventions
- Dual package manager support (npm and JSR)
Notes
- The skill does NOT automatically commit, tag, or push changes
- It prepares all files for review before manual commit
- This allows the developer to review and adjust if needed
- The skill is idempotent - can be run multiple times safely
- ALWAYS Ensure package.json and jsr.json versions are in sync before and after bumping
More by jasonraimondi
View allLoads plan, reviews critically, executes each batch, and reports for review between batches. Use when implementing a plan from a plan file.
Creates a comprehensive easy to follow plan for building out a design. Use when creating an implementation plan from a design.