TinyKit Pro Docs

Automated Versioning & Release Management

TinyKit Pro uses automated semantic versioning with conventional commits to streamline release management and maintain accurate changelogs.

TinyKit Pro uses automated semantic versioning with conventional commits to streamline release management and maintain accurate changelogs.

Overview

The versioning system automatically:

  • Analyzes commit messages to determine version bumps
  • Updates CHANGELOG.md with categorized changes
  • Bumps version in package.json
  • Creates GitHub releases with release notes
  • Tags releases in git

Conventional Commits

All commits must follow the Conventional Commits specification.

Format

<type>(<scope>): <description>

[optional body]

[optional footer(s)]

Commit Types & Version Bumps

TypeVersion ImpactDescriptionExamples
featMinor (0.X.0)New featurefeat(auth): add passwordless login
fixPatch (0.0.X)Bug fixfix(billing): correct proration
perfPatch (0.0.X)Performance improvementperf(queries): optimize user lookup
refactorPatch (0.0.X)Code restructuringrefactor(utils): simplify helpers
revertPatch (0.0.X)Revert previous commitrevert: feat(auth): add oauth
docsNoneDocumentation changesdocs: update API reference
styleNoneCode formatting (no logic change)style: format with prettier
testNoneTest additions/updatestest: add billing tests
buildNoneBuild system changesbuild: update dependencies
ciNoneCI configurationci: add codecov integration
choreNoneMaintenance taskschore: update gitignore

Breaking Changes (Major X.0.0)

Breaking changes trigger a major version bump:

# Using ! suffix
feat!: remove deprecated user.email field

# Using BREAKING CHANGE footer
feat(api): redesign authentication endpoints

BREAKING CHANGE: Session tokens are now JWT-based instead of opaque strings

Scope Examples

Scopes help categorize changes:

feat(auth): add two-factor authentication
fix(billing): handle failed payment webhooks
docs(api): update stripe integration guide
perf(database): add indexes for faster queries
refactor(email): extract template rendering logic

Local Validation

Commitlint validates commit messages via Husky's commit-msg hook:

# ✅ Valid commits
git commit -m "feat(users): add profile avatars"
git commit -m "fix: resolve navigation bug"
git commit -m "docs: update README"

# ❌ Invalid commits (will be rejected)
git commit -m "Added new feature"          # No type
git commit -m "fix bug"                     # Missing colon
git commit -m "FEAT: new thing"             # Uppercase type

Testing Locally

# Test commit message validation
echo "bad message" | npx commitlint        # Should fail
echo "feat: good message" | npx commitlint # Should pass

# Dry-run semantic-release
bun release:dry-run

Release Workflow

Automatic Releases (Main Branch)

When changes are merged to main:

  1. Quality Checks - ESLint and TypeScript validation
  2. Testing - Backend and frontend tests run in parallel
  3. Release - If all checks pass:
    • Semantic-release analyzes commits since last tag
    • Determines version bump based on commit types
    • Updates CHANGELOG.md with categorized changes
    • Bumps version in package.json
    • Commits changes with [skip ci] to prevent loops
    • Creates git tag (e.g., v1.2.0)
    • Creates GitHub release with auto-generated notes
    • Pushes tag and release commit back to repository

No Release Scenarios

Releases are skipped when:

  • Branch is not main
  • Only non-versioned commits (docs, style, test, etc.)
  • Commit messages include [skip ci]
  • Quality checks or tests fail

GitHub Workflow Configuration

Release Workflow (.github/workflows/release.yml)

name: Release
on:
  push:
    branches: [main]

jobs:
  check-skip: # Skip if [skip ci] in commit
  quality: # Lint & typecheck
  test-backend: # Backend unit tests
  test-frontend: # Frontend unit tests
  release: # Semantic-release (if all pass)

Features:

  • Concurrency control prevents parallel releases
  • fetch-depth: 0 ensures full git history for analysis
  • Proper GitHub permissions for creating releases
  • Security-safe handling of commit messages via environment variables

Configuration Files

.releaserc.json

Semantic-release configuration:

{
  "branches": ["main"],
  "plugins": [
    "@semantic-release/commit-analyzer", // Analyze commits
    "@semantic-release/release-notes-generator", // Generate notes
    "@semantic-release/changelog", // Update CHANGELOG.md
    "@semantic-release/npm", // Bump package.json (no publish)
    "@semantic-release/github", // Create GitHub release
    "@semantic-release/git" // Commit changes back
  ]
}

commitlint.config.js

Commit message validation rules:

module.exports = {
  extends: ["@commitlint/config-conventional"],
  rules: {
    "type-enum": [
      2,
      "always",
      [
        "feat",
        "fix",
        "docs",
        "style",
        "refactor",
        "perf",
        "test",
        "build",
        "ci",
        "chore",
        "revert",
      ],
    ],
    "type-case": [2, "always", "lower-case"],
    "subject-empty": [2, "never"],
    "header-max-length": [2, "always", 100],
  },
};

CHANGELOG.md Structure

Automatically generated changelog entries:

# Changelog

## [1.2.0] - 2025-01-22

### Features

- **auth**: Add two-factor authentication (#123)
- **billing**: Support annual subscriptions (#124)

### Bug Fixes

- **email**: Fix template rendering issue (#125)

### Performance

- **database**: Add indexes for user queries (#126)

## [1.1.0] - 2025-01-15

...

Best Practices

Writing Good Commit Messages

Good Examples:

feat(auth): add OAuth support for Microsoft
fix(billing): prevent duplicate charges on retry
perf(api): cache frequently accessed queries
docs(readme): update installation instructions

Bad Examples:

Added stuff                    # Too vague, no type
fix bug                        # Missing colon, no description
WIP                           # Not descriptive
Update files                   # No type, too generic

Commit Message Tips

  1. Use imperative mood: "add" not "added" or "adds"
  2. Be specific: Include what and why
  3. Reference issues: Add #123 for issue/PR references
  4. Keep it concise: Under 100 characters for header
  5. Add body for complex changes: Explain the "why"

Squash vs. Merge

Recommended: Squash and merge PRs

  • Keeps main branch history clean
  • Each PR becomes one commit
  • Easier to revert features
  • Commit message determines version bump

If using merge commits:

  • All commits in PR affect versioning
  • Ensure each commit follows conventions
  • Consider rebasing before merge

Versioning Strategy

Version Numbers

Following Semantic Versioning:

  • MAJOR (X.0.0) - Breaking changes
  • MINOR (0.X.0) - New features (backward compatible)
  • PATCH (0.0.X) - Bug fixes (backward compatible)

Pre-1.0.0 Development

During initial development (0.x.x):

  • Breaking changes increment MINOR version
  • Features and fixes increment PATCH version
  • Release 1.0.0 when production-ready

Baseline Tagging

For existing projects without tags:

# Create baseline tag before first automated release
git tag v1.1.0 -m "Baseline for automated releases"
git push origin v1.1.0

This prevents semantic-release from analyzing the entire commit history.

Troubleshooting

Release Not Created

Issue: Merged to main but no release created

Solutions:

  1. Check if commits trigger releases (feat, fix, etc.)
  2. Verify quality checks passed in GitHub Actions
  3. Check if [skip ci] in commit message
  4. Review release workflow logs

Version Bump Incorrect

Issue: Wrong version number assigned

Solutions:

  1. Review commit messages for correct types
  2. Check for breaking change markers (! or BREAKING CHANGE:)
  3. Verify all commits since last tag
  4. Use bun release:dry-run to preview

Commitlint Rejecting Valid Messages

Issue: Local commits rejected despite correct format

Solutions:

  1. Check commitlint.config.js for custom rules
  2. Verify Husky hooks are installed (bun prepare)
  3. Test with: echo "feat: test" | npx commitlint

CHANGELOG.md Conflicts

Issue: Merge conflicts in CHANGELOG.md

Solutions:

  1. Don't manually edit CHANGELOG.md on main
  2. Let semantic-release manage it automatically
  3. If conflicts occur, accept incoming changes

Manual Overrides

Skip Automated Release

Add [skip ci] to commit message:

git commit -m "chore: update dependencies [skip ci]"

Force Specific Version

Not recommended, but possible via git tags:

# Not ideal - breaks automation
git tag v2.0.0 -m "Force major version"
git push origin v2.0.0

Migration from Manual Versioning

If migrating from manual versioning:

  1. Create baseline tag with current version
  2. Update all developers on new commit format
  3. Enable commitlint in all development environments
  4. Review first automated release with dry-run
  5. Monitor releases for first few weeks

On this page

Ship your startup faster. In minutes.

Get TinyKit Pro