ワンクリックで
update-release
Automates the release process for the ts-oauth2-server project.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Automates the release process for the ts-oauth2-server project.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
| name | update-release |
| description | Automates the release process for the ts-oauth2-server project. |
| license | Complete terms in LICENSE |
Automates the release process for the ts-oauth2-server project by:
package.json and jsr.jsonpnpm typecheck && pnpm build && pnpm test) before release, aborting if any step fails.github/workflows/publish.yml)Use this skill when the user requests:
package.json and jsr.json files presentCHANGELOG.md file existsSupported version bump types (following semver):
Prerelease format (REQUIRED):
-rc.#. This project's prereleases are alwaysX.Y.Z-rc.N, starting atrc.0and incrementing the trailing number (rc.0→rc.1→ …). Never use bare-0or-next.Nsuffixes. The new prerelease number must be strictly greater than the previous one for the sameX.Y.Z.
import { readFileSync } from 'fs';
function getCurrentVersion(): string {
const pkg = JSON.parse(readFileSync('package.json', 'utf-8'));
return pkg.version;
}
// Prereleases use the REQUIRED `-rc.#` convention (e.g. 5.0.0-rc.0, 5.0.0-rc.1).
function bumpVersion(current: string, bumpType: string): string {
const parts = current.split('-');
const [major, minor, patch] = parts[0].split('.').map(Number);
const prerelease = parts[1]; // e.g. "rc.0"
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-rc.0`;
case 'preminor':
return `${major}.${minor + 1}.0-rc.0`;
case 'prepatch':
return `${major}.${minor}.${patch + 1}-rc.0`;
case 'prerelease':
if (prerelease) {
// Increment the trailing rc number: "rc.0" -> "rc.1". Reject non-rc suffixes.
const match = prerelease.match(/^rc\.(\d+)$/);
if (!match) {
throw new Error(`Prerelease must use the -rc.# format, got: ${prerelease}`);
}
return `${parts[0]}-rc.${parseInt(match[1], 10) + 1}`;
}
return `${major}.${minor}.${patch + 1}-rc.0`;
default:
throw new Error(`Invalid bump type: ${bumpType}`);
}
}
# 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
Parse commits and categorize them based on conventional commit format:
feat: or feature: → 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 sectionFollow 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
Update both package.json and jsr.json with the new version, and prepend the new changelog entry to CHANGELOG.md under the ## [Unreleased] section.
Always run the full pre-flight locally before pausing for confirmation. The publish workflow runs pnpm build && pnpm test in CI, but failures there fire only after the release event — too late to undo a consumed version. Catch them locally first.
Run all three, in order, and abort the release if any fails:
pnpm typecheck # tsc -p tsconfig.build.json --noEmit
pnpm build # tsdown
pnpm test # vitest run
If any step fails, do NOT proceed to commit/tag/release — surface the failure and stop. pnpm build writes to the gitignored dist/, so it does not dirty the release commit; confirm git status --short shows only CHANGELOG.md, package.json, and jsr.json before committing.
After the file edits land and the pre-flight is green, stop and ask the user for confirmation before any git mutation. Do NOT proceed automatically — the user may want to review the diff or hand-edit changelog wording first.
Once confirmed, run these in order:
# 1. Commit the version bump + changelog
git add CHANGELOG.md package.json jsr.json
git commit -m "chore: release vX.Y.Z"
# 2. Tag and push (tag push alone does NOT trigger publish)
git tag vX.Y.Z
git push && git push --tags
# 3. Create GitHub release — THIS is what triggers .github/workflows/publish.yml
# (workflow listens on `release: [released, prereleased]`, not on tag push)
gh release create vX.Y.Z --title "vX.Y.Z" --notes "<changelog body for this version>"
For the --notes body, pass the section content from CHANGELOG.md for the new version (everything between the ## [X.Y.Z] header and the next ## header), without the version header itself. Use a HEREDOC for multiline notes.
For prereleases (always -rc.#, e.g. 5.0.0-rc.0), add --prerelease to gh release create so the workflow's prerelease branch publishes to npm under the next dist-tag.
After gh release create returns the release URL, share it with the user and note that npm + JSR publishing is now running in CI.
// 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
// 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
// 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
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: ask user to confirm, then commit + tag + push + `gh release create`.');
console.log('The GitHub release event triggers .github/workflows/publish.yml (npm + JSR).');
}
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;
}
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
Updated package.json, jsr.json, CHANGELOG.md
Pre-flight: typecheck ✓ build ✓ test ✓
Files prepared for v4.1.2.
Confirm to proceed with: commit + tag + push + GitHub release
(GitHub release triggers npm + JSR publish via .github/workflows/publish.yml)
After user confirmation, run the git+gh commands and report the release URL.
This skill respects the project's:
gh release create in sequence — the GitHub release is what triggers .github/workflows/publish.yml to publish to npm and JSR (a tag push alone does NOT trigger publish)