Claude-skill-registry git-release-workflow
Execute git commit, tag, and push operations with configurable patterns for any project type
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/git-release-workflow" ~/.claude/skills/majiayu000-claude-skill-registry-git-release-workflow && rm -rf "$T"
skills/data/git-release-workflow/SKILL.mdGit Release Workflow
Purpose
Executes the git operations for a release: running pre-release hooks, staging modified files, creating a commit with proper formatting and attribution, creating an annotated git tag with configurable patterns, and running post-release hooks. Works with any project type.
Input Context
Requires:
- Project Configuration: Output from
skilldetect-project-type - Version: New version string (e.g., "1.2.0")
- Commit Message: Pre-formatted commit message (from changelog-update skill)
- Files to Stage: List of modified files to include in commit
Workflow
1. Load Configuration
Use configuration from
detect-project-type:
- Pattern for git tag (e.g., "v{version}", "{package}-v{version}")tag_pattern
- Message template for annotated tagtag_message
- Template for commit messagecommit_message_template
- Script to run before git operations (optional)pre_release_hook
- Script to run after successful push (optional)post_release_hook
2. Run Pre-Release Hook
If
preReleaseHook is configured, execute it before any git operations:
if [ -n "$pre_release_hook" ]; then echo "Running pre-release hook: $pre_release_hook" if [ -x "$pre_release_hook" ]; then # Run hook and capture output if ! hook_output=$($pre_release_hook 2>&1); then # Hook failed - abort release echo "✗ Pre-release hook failed:" echo "$hook_output" exit 1 else echo "✓ Pre-release hook succeeded" echo "$hook_output" fi else echo "⚠️ Pre-release hook not executable: $pre_release_hook" exit 1 fi fi
Common pre-release hook use cases:
- Run tests:
,npm test
,cargo testgo test ./... - Build project:
,npm run buildcargo build --release - Run linters:
,npm run lintcargo clippy - Generate documentation:
npm run docs - Validate package:
,npm pack --dry-runtwine check dist/*
3. Stage Modified Files
Stage all files that were modified during the release process:
git add {file1} {file2} {file3} ...
Files typically include:
- Version configuration files (plugin.json, marketplace.json, variants.json)
- Changelog files (CHANGELOG.md)
- Documentation files (README.md)
Verify staging succeeded:
git status --short
2. Create Commit
Create commit with the provided message, ensuring proper formatting and attribution:
git commit -m "$(cat <<'EOF' {commit-message} Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> EOF )"
Commit message format:
Release {scope} v{version} {changelog-body} Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Important: Use heredoc (
<<'EOF') to preserve formatting and handle multi-line messages correctly.
Capture commit hash:
git rev-parse HEAD
5. Create Annotated Git Tag
Create an annotated tag (not lightweight) using configurable pattern from configuration:
Build tag name from pattern:
# Get pattern from config (e.g., "v{version}", "{package}-v{version}") tag_pattern="v{version}" # from config # Replace placeholders tag_name="${tag_pattern//\{version\}/$new_version}" # For monorepo/plugins, also replace {package} if [[ "$tag_pattern" == *"{package}"* ]]; then tag_name="${tag_name//\{package\}/$package_name}" fi # Examples: # Pattern: "v{version}" → Tag: "v1.2.0" # Pattern: "release-{version}" → Tag: "release-1.2.0" # Pattern: "{package}-v{version}" → Tag: "my-lib-v1.2.0"
Build tag message from template:
# Get template from config (default: "Release v{version}") tag_message_template="Release v{version}" # from config # Replace placeholders tag_message="${tag_message_template//\{version\}/$new_version}" tag_message="${tag_message//\{package\}/$package_name}" # Example: "Release v1.2.0"
Create tag:
git tag -a "$tag_name" -m "$tag_message"
Verify tag created:
if git tag -l "$tag_name" | grep -q "$tag_name"; then echo "✓ Created tag: $tag_name" else echo "✗ Failed to create tag: $tag_name" exit 1 fi
4. Prepare Push Information
Do NOT automatically push. Instead, prepare information for the command to display:
# Get remote URL git remote get-url origin # Get current branch git branch --show-current # Show what will be pushed git log origin/{branch}..HEAD --oneline
Return push command for user to execute:
git push origin {branch} --follow-tags
Or if using
--force-with-lease after rebase:
git push origin {branch} --follow-tags --force-with-lease
6. Run Post-Release Hook (After Push)
Note: This section is executed by the
/release command AFTER successful push, not within this skill.
If
postReleaseHook is configured, execute it after git push succeeds:
if [ -n "$post_release_hook" ]; then echo "Running post-release hook: $post_release_hook" if [ -x "$post_release_hook" ]; then # Run hook and capture output if ! hook_output=$($post_release_hook 2>&1); then # Hook failed - warn but don't abort (release already pushed) echo "⚠️ Post-release hook failed:" echo "$hook_output" # Continue anyway - release is already public else echo "✓ Post-release hook succeeded" echo "$hook_output" fi else echo "⚠️ Post-release hook not executable: $post_release_hook" fi fi
Common post-release hook use cases:
- Publish package:
,npm publish
,cargo publishtwine upload dist/* - Deploy to CDN/hosting:
,netlify deployvercel --prod - Send notifications:
,./scripts/notify-slack.sh./scripts/post-to-discord.sh - Update documentation site:
./scripts/deploy-docs.sh - Trigger CI/CD:
curl -X POST $CI_WEBHOOK_URL - Create GitHub release:
gh release create $TAG_NAME
7. Generate Summary
Collect information for post-release summary:
- Commit hash (short: first 7 characters)
- Tag name
- Files committed (count)
- Current branch
- Remote URL (if configured)
Output Format
Return:
{ "commit_hash": "a1b2c3d", "commit_hash_full": "a1b2c3d4e5f6g7h8i9j0", "tag_name": "daily-carry-v1.2.0", "files_committed": [ "plugins/daily-carry/.claude-plugin/plugin.json", "plugins/daily-carry/CHANGELOG.md", ".claude-plugin/marketplace.json" ], "files_count": 3, "branch": "master", "remote_url": "https://github.com/jayteealao/agent-skills.git", "push_command": "git push origin master --follow-tags", "success": true }
Examples
Example 1: Plugin Release
Input:
- Scope:
plugin:daily-carry - Version:
1.2.0 - Commit message:
Release plugin:daily-carry v1.2.0 Added: - New deployment command Fixed: - Git push error handling Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> - Files:
["plugins/daily-carry/.claude-plugin/plugin.json", "plugins/daily-carry/CHANGELOG.md", ".claude-plugin/marketplace.json"]
Operations:
# Stage files git add plugins/daily-carry/.claude-plugin/plugin.json git add plugins/daily-carry/CHANGELOG.md git add .claude-plugin/marketplace.json # Create commit git commit -m "$(cat <<'EOF' Release plugin:daily-carry v1.2.0 Added: - New deployment command Fixed: - Git push error handling Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> EOF )" # Create tag git tag -a "daily-carry-v1.2.0" -m "Release plugin:daily-carry v1.2.0"
Output:
{ "commit_hash": "f7e8d9c", "commit_hash_full": "f7e8d9c6b5a4e3d2c1b0a9f8e7d6c5b4", "tag_name": "daily-carry-v1.2.0", "files_committed": [ "plugins/daily-carry/.claude-plugin/plugin.json", "plugins/daily-carry/CHANGELOG.md", ".claude-plugin/marketplace.json" ], "files_count": 3, "branch": "master", "remote_url": "https://github.com/jayteealao/agent-skills.git", "push_command": "git push origin master --follow-tags", "success": true }
Example 2: Marketplace Release
Input:
- Scope:
marketplace - Version:
1.1.0 - Files:
[".claude-plugin/marketplace.json", "CHANGELOG.md", "README.md"]
Tag created:
marketplace-v1.1.0
Output:
{ "commit_hash": "b4c5d6e", "tag_name": "marketplace-v1.1.0", "files_count": 3, "branch": "master", "push_command": "git push origin master --follow-tags", "success": true }
Example 3: Variants Release
Input:
- Scope:
variants - Version:
2.0.0 - Files:
["variants/variants.json", "variants/CHANGELOG.md"]
Tag created:
variants-v2.0.0
Error Handling
Git Add Failure
Error: File doesn't exist or permission denied
Response:
{ "success": false, "error": "Failed to stage files", "details": "git add failed: {error-message}", "suggestion": "Verify files exist and are writable" }
Git Commit Failure
Error: Nothing to commit, commit hook failed, etc.
Response:
{ "success": false, "error": "Failed to create commit", "details": "{git-error-message}", "suggestion": "Check git status and pre-commit hooks" }
Rollback: If commit fails, unstage files:
git reset HEAD
Git Tag Failure
Error: Tag already exists, invalid tag name, etc.
Response:
{ "success": false, "error": "Failed to create tag", "details": "{git-error-message}", "commit_hash": "f7e8d9c", "suggestion": "Tag may already exist. Use 'git tag -d {tag}' to delete or choose different version" }
Rollback: Offer to undo commit:
git reset --soft HEAD~1
No Remote Configured
Warning: (non-blocking)
Response:
{ "success": true, "commit_hash": "f7e8d9c", "tag_name": "daily-carry-v1.2.0", "remote_url": null, "push_command": null, "warning": "No git remote configured - cannot push" }
Integration Notes
This skill is invoked by the
/release command in Phase 6. The command will:
- Execute git operations via this skill
- Display commit hash and tag name
- Prompt user to push with provided command
- If user confirms, execute push:
git push origin {branch} --follow-tags - Proceed to Phase 7 for verification
Git Best Practices
- Always use annotated tags (
flag) for releases (contains metadata)-a - Use heredoc for multi-line commit messages to preserve formatting
- Include Co-Authored-By for attribution when Claude assists
- Use
when pushing to include annotated tags--follow-tags - Never force push unless explicitly requested and using
--force-with-lease - Verify operations after each git command (check status, verify tag exists)
Linear History Maintenance
To maintain linear git history:
- Commit directly to master/main (no merge commits)
- If on feature branch, rebase onto master first
- Use fast-forward merges only
- Avoid
git merge --no-ff