diff --git a/.github/RELEASE.md b/.github/RELEASE.md new file mode 100644 index 0000000..daeaad2 --- /dev/null +++ b/.github/RELEASE.md @@ -0,0 +1,121 @@ +# Release Configuration + +This project provides multiple release workflows: fully automated GitHub Actions, local Rake tasks, and interactive scripts. + +## ๐Ÿš€ Automated GitHub Actions Release (Recommended) + +The easiest way to create releases using GitHub's web interface. + +### Quick Start + +1. **Navigate to** [Actions tab](https://github.com/markhallen/slack-github-threads/actions) +2. **Click** "Create Release PR" workflow +3. **Click** "Run workflow" button +4. **Choose options**: + - **Release Type**: `auto` (recommended), `major`, `minor`, or `patch` + - **Dry Run**: Check this to preview without creating actual release +5. **Click** "Run workflow" +6. **Review and merge** the automatically created PR +7. **Release publishes automatically** when PR is merged + +### What the GitHub Action Does + +1. **Analyzes commits** since last release +2. **Suggests appropriate version bump** (if `auto` selected) +3. **Runs full test suite** to ensure quality +4. **Generates changelog** from conventional commits +5. **Creates release branch** with version bump +6. **Opens PR** with detailed release information +7. **Auto-releases** when PR is merged + +### Workflow Options + +| Option | Description | When to Use | +| --------- | ------------------------------------------------- | ----------------------------- | +| `auto` | System analyzes commits and suggests release type | Most releases | +| `major` | Force major version bump (breaking changes) | API changes, major refactors | +| `minor` | Force minor version bump (new features) | New functionality | +| `patch` | Force patch version bump (bug fixes) | Bug fixes, small improvements | +| `dry_run` | Preview mode - no actual release created | Testing, planning | + +## ๐Ÿง  Local Rake Tasks + +For developers who prefer command-line workflows. + +### Key Features + +1. **Automatic Changelog Generation**: Parses git commit messages since the last tag +2. **Conventional Commit Support**: Categorizes commits based on conventional commit patterns +3. **Integrated Testing**: Runs full CI checks before creating releases +4. **Git Tag Management**: Automatically creates and manages version tags + +## Creating a Release + +### Method 1: Rake Task (Recommended) + +```bash +# Preview unreleased changes +rake release:preview + +# Create release with automatic changelog generation +rake release:create[1.1.0] + +# Push changes and tag +git push origin main && git push origin v1.1.0 +``` + +### Method 2: Release Script + +```bash +# One command that handles everything including the push +./scripts/release.sh 1.1.0 +``` + +## Commit Message Conventions + +The automatic changelog generation works best with conventional commit messages: + +| Commit Pattern | Changelog Section | Example | +| ---------------------------- | ----------------- | ---------------------------------- | +| `feat:`, `add`, `implement` | **Added** | `feat: add webhook support` | +| `fix:`, `bug`, `resolve` | **Fixed** | `fix: resolve timeout issue` | +| `chore:`, `update`, `change` | **Changed** | `chore: update dependencies` | +| `docs:`, `documentation` | **Changed** | `docs: improve API documentation` | +| `remove`, `delete` | **Removed** | `remove: delete deprecated method` | +| `security`, `sec:` | **Security** | `security: fix XSS vulnerability` | + +## Manual Changelog Updates + +If you prefer manual changelog management: + +1. Edit `CHANGELOG.md` directly with your changes +2. Use `rake release:create[1.1.0]` - it will preserve manual entries +3. The Rake task will add the version and date automatically + +## Release Checklist + +Before creating a release, ensure: + +- [ ] All tests pass locally +- [ ] CHANGELOG.md is updated with the new version +- [ ] Version follows semantic versioning (MAJOR.MINOR.PATCH) +- [ ] Documentation is up to date +- [ ] No sensitive information is included in the release + +## Manual Release (if needed) + +If you need to create a release manually: + +1. Go to the [Releases page](https://github.com/markhallen/slack-github-threads/releases) +2. Click "Create a new release" +3. Choose your tag or create a new one +4. Fill in the release title and description +5. Upload any additional assets if needed + +## Version Tag Format + +Always use the format `v..` for version tags: + +- `v1.0.0` - Major release +- `v1.1.0` - Minor release +- `v1.0.1` - Patch release diff --git a/.github/release.conf b/.github/release.conf new file mode 100644 index 0000000..49ecf7e --- /dev/null +++ b/.github/release.conf @@ -0,0 +1,66 @@ +# Release Configuration for slack-github-threads + +# Default settings for automated releases +RELEASE_BRANCH=main +PACKAGE_NAME=slack-github-threads +CHANGELOG_FILE=CHANGELOG.md + +# Files to exclude from release archives +EXCLUDE_PATTERNS=( + '.git*' + 'test/' + 'log/*.log' + '.env*' + 'tmp/' + '*.tmp' + '.DS_Store' +) + +# Required checks before release +PRE_RELEASE_CHECKS=( + 'syntax' + 'rubocop' + 'test' +) + +# Conventional commit patterns for changelog generation +# These patterns are used by the Rake tasks to categorize commits +COMMIT_PATTERNS=( + # Added section + "feat(\(.+\))?:|add|implement -> Added" + + # Fixed section + "fix(\(.+\))?:|bug|resolve -> Fixed" + + # Changed section + "chore(\(.+\))?:|update|change|modify -> Changed" + "docs(\(.+\))?:|documentation -> Changed" + "style(\(.+\))?:|format -> Changed" + "refactor(\(.+\))?: -> Changed" + "perf(\(.+\))?:|performance -> Changed" + "test(\(.+\))?: -> Changed" + + # Removed section + "remove|delete -> Removed" + + # Security section + "security|sec: -> Security" + + # Deprecated section + "deprecate -> Deprecated" +) + +# Post-release notifications (optional) +# Set these environment variables if you want notifications +# SLACK_WEBHOOK_URL="" +# DISCORD_WEBHOOK_URL="" + +# GitHub release settings +GITHUB_RELEASE_DRAFT=false +GITHUB_RELEASE_PRERELEASE=false +GITHUB_GENERATE_RELEASE_NOTES=true + +# Rake task shortcuts +# rake release:preview - Preview unreleased changes +# rake release:changelog[VERSION] - Generate changelog for version +# rake release:create[VERSION] - Full release process diff --git a/.github/workflows/create-release-pr.yml b/.github/workflows/create-release-pr.yml new file mode 100644 index 0000000..3d14e90 --- /dev/null +++ b/.github/workflows/create-release-pr.yml @@ -0,0 +1,236 @@ +name: Create Release PR + +on: + workflow_dispatch: + inputs: + release_type: + description: 'Type of release' + required: true + default: 'auto' + type: choice + options: + - auto + - major + - minor + - patch + dry_run: + description: 'Dry run (preview only)' + required: false + default: false + type: boolean + +permissions: + contents: write + pull-requests: write + +jobs: + create-release-pr: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.2' + bundler-cache: true + + - name: Configure Git + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + + - name: Determine release type + id: release_type + run: | + if [ "${{ github.event.inputs.release_type }}" = "auto" ]; then + # Run preview to get suggested release type + PREVIEW_OUTPUT=$(bundle exec rake release:preview) + echo "$PREVIEW_OUTPUT" + + # Extract suggested release type from output + SUGGESTED_TYPE=$(echo "$PREVIEW_OUTPUT" | grep "Suggested release type:" | awk '{print $NF}') + + if [ -z "$SUGGESTED_TYPE" ]; then + echo "Could not determine release type automatically, defaulting to patch" + RELEASE_TYPE="patch" + else + RELEASE_TYPE="$SUGGESTED_TYPE" + fi + else + RELEASE_TYPE="${{ github.event.inputs.release_type }}" + fi + + echo "RELEASE_TYPE=$RELEASE_TYPE" >> $GITHUB_OUTPUT + echo "Selected release type: $RELEASE_TYPE" + + - name: Get current version + id: current_version + run: | + CURRENT=$(ruby scripts/version.rb current) + echo "CURRENT_VERSION=$CURRENT" >> $GITHUB_OUTPUT + + - name: Calculate next version + id: next_version + run: | + RELEASE_TYPE=${{ steps.release_type.outputs.RELEASE_TYPE }} + NEXT_VERSION=$(ruby scripts/version.rb next "$RELEASE_TYPE") + echo "NEXT_VERSION=$NEXT_VERSION" >> $GITHUB_OUTPUT + echo "Next version will be: $NEXT_VERSION" + + - name: Preview release (dry run) + if: ${{ github.event.inputs.dry_run == 'true' }} + run: | + echo "=== DRY RUN MODE ===" + echo "Current version: ${{ steps.current_version.outputs.CURRENT_VERSION }}" + echo "Release type: ${{ steps.release_type.outputs.RELEASE_TYPE }}" + echo "Next version: ${{ steps.next_version.outputs.NEXT_VERSION }}" + echo + echo "=== COMMIT PREVIEW ===" + bundle exec rake release:preview + echo + echo "=== CHANGELOG PREVIEW ===" + bundle exec rake release:changelog[${{ steps.next_version.outputs.NEXT_VERSION }}] || true + + - name: Create release branch + if: ${{ github.event.inputs.dry_run != 'true' }} + run: | + BRANCH_NAME="release/v${{ steps.next_version.outputs.NEXT_VERSION }}" + git checkout -b "$BRANCH_NAME" + echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV + + - name: Run tests before release + if: ${{ github.event.inputs.dry_run != 'true' }} + run: bundle exec rake ci + + - name: Create release + if: ${{ github.event.inputs.dry_run != 'true' }} + run: | + bundle exec rake release:create[${{ steps.release_type.outputs.RELEASE_TYPE }}] + + - name: Push release branch + if: ${{ github.event.inputs.dry_run != 'true' }} + run: | + git push origin "$BRANCH_NAME" + + - name: Create Pull Request + if: ${{ github.event.inputs.dry_run != 'true' }} + id: create_pr + uses: actions/github-script@v7 + with: + script: | + const { data: pr } = await github.rest.pulls.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: `Release v${{ steps.next_version.outputs.NEXT_VERSION }}`, + head: process.env.BRANCH_NAME, + base: 'main', + body: `## Release v${{ steps.next_version.outputs.NEXT_VERSION }} + + This is an automated release PR created by GitHub Actions. + + ### Release Type: ${{ steps.release_type.outputs.RELEASE_TYPE }} + + ### Changes + + This PR includes: + - ๐Ÿ“ Updated CHANGELOG.md with new version + - ๐Ÿท๏ธ Version tag v${{ steps.next_version.outputs.NEXT_VERSION }} + - โœ… All tests passing + + ### What happens when merged? + + When this PR is merged to main: + 1. ๐Ÿš€ GitHub Actions will automatically create a GitHub release + 2. ๐Ÿ“ฆ Release artifacts will be built and attached + 3. ๐ŸŒ Release will be published + + ### Review Checklist + + - [ ] Review the changelog entries + - [ ] Verify the version bump is appropriate + - [ ] Confirm all tests are passing + - [ ] Check that no sensitive information is included + + --- + + *This PR was created automatically by the [Create Release PR workflow](.github/workflows/create-release-pr.yml)*`, + draft: false + }); + + // Add labels + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pr.number, + labels: ['release', '${{ steps.release_type.outputs.RELEASE_TYPE }}'] + }); + + console.log(`Created PR #${pr.number}: ${pr.html_url}`); + + // Set outputs + core.setOutput('pr_number', pr.number); + core.setOutput('pr_url', pr.html_url); + + return { + pr_number: pr.number, + pr_url: pr.html_url + }; + + - name: Comment on PR with details + if: ${{ github.event.inputs.dry_run != 'true' }} + uses: actions/github-script@v7 + with: + script: | + const prNumber = ${{ steps.create_pr.outputs.pr_number }}; + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body: `### ๐Ÿš€ Release Summary + + | Item | Value | + |------|-------| + | **Current Version** | ${{ steps.current_version.outputs.CURRENT_VERSION }} | + | **New Version** | ${{ steps.next_version.outputs.NEXT_VERSION }} | + | **Release Type** | ${{ steps.release_type.outputs.RELEASE_TYPE }} | + | **Branch** | \`${process.env.BRANCH_NAME}\` | + + ### ๐Ÿ“‹ Next Steps + + 1. **Review** the changes in this PR + 2. **Merge** this PR to trigger the release + 3. **Monitor** the release workflow in the Actions tab + + The release will be automatically created when this PR is merged! ๐ŸŽ‰` + }); + + - name: Output summary + if: ${{ github.event.inputs.dry_run != 'true' }} + run: | + echo "## Release PR Created! ๐ŸŽ‰" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Version**: ${{ steps.next_version.outputs.NEXT_VERSION }}" >> $GITHUB_STEP_SUMMARY + echo "- **Type**: ${{ steps.release_type.outputs.RELEASE_TYPE }}" >> $GITHUB_STEP_SUMMARY + echo "- **Branch**: $BRANCH_NAME" >> $GITHUB_STEP_SUMMARY + echo "- **PR**: ${{ steps.create_pr.outputs.pr_url }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### What's Next?" >> $GITHUB_STEP_SUMMARY + echo "1. Review and merge the PR" >> $GITHUB_STEP_SUMMARY + echo "2. The release will be automatically created" >> $GITHUB_STEP_SUMMARY + + - name: Output dry run summary + if: ${{ github.event.inputs.dry_run == 'true' }} + run: | + echo "## Dry Run Complete! ๐Ÿ‘€" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Would create version**: ${{ steps.next_version.outputs.NEXT_VERSION }}" >> $GITHUB_STEP_SUMMARY + echo "- **Release type**: ${{ steps.release_type.outputs.RELEASE_TYPE }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "To create the actual release, run this workflow again without dry run mode." >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..9862e2e --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,110 @@ +name: Release + +on: + push: + tags: + - 'v*' + pull_request: + types: [closed] + branches: [main] + +env: + VERSION_REGEX: 'v[0-9]+\.[0-9]+\.[0-9]+' + +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + # Only run if it's a merged PR with release label, or a version tag push + if: | + (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')) || + (github.event_name == 'pull_request' && + github.event.pull_request.merged == true && + contains(github.event.pull_request.labels.*.name, 'release')) + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.2' + bundler-cache: true + + - name: Run tests + run: | + bundle exec rake syntax + bundle exec rake rubocop + bundle exec rake test + + - name: Extract version from tag or PR + id: version + run: | + if [ "${{ github.event_name }}" = "push" ]; then + # Extract from tag + VERSION=${GITHUB_REF#refs/tags/v} + else + # Extract from PR title (should be "Release vX.Y.Z") + VERSION=$(echo "${{ github.event.pull_request.title }}" | grep -oE "$VERSION_REGEX" | sed 's/^v//') + if [ -z "$VERSION" ]; then + echo "Could not extract version from PR title: ${{ github.event.pull_request.title }}" + exit 1 + fi + fi + echo "VERSION=$VERSION" >> $GITHUB_OUTPUT + echo "Extracted version: $VERSION" + + - name: Create tag if from PR + if: github.event_name == 'pull_request' + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + git tag -a "v${{ steps.version.outputs.VERSION }}" -m "Release version ${{ steps.version.outputs.VERSION }}" + git push origin "v${{ steps.version.outputs.VERSION }}" + + - name: Extract changelog for version + id: changelog + run: | + # Extract changelog section for this version + VERSION="${{ steps.version.outputs.VERSION }}" + CHANGELOG_SECTION=$(awk "/^## \[$VERSION\]/{flag=1; next} /^## \[/{flag=0} flag" CHANGELOG.md) + + # If no specific version section found, use unreleased section + if [ -z "$CHANGELOG_SECTION" ]; then + CHANGELOG_SECTION=$(awk "/^## \[Unreleased\]/{flag=1; next} /^## \[/{flag=0} flag" CHANGELOG.md) + fi + + # Save to file to preserve multiline content + echo "$CHANGELOG_SECTION" > changelog_body.txt + + # Also set as output for use in release + { + echo 'BODY<> $GITHUB_OUTPUT + + - name: Build and package application + run: | + # Create a simple archive of the application + tar -czf slack-github-threads-v${{ steps.version.outputs.VERSION }}.tar.gz \ + --exclude='.git*' \ + --exclude='test/' \ + --exclude='log/*.log' \ + --exclude='.env*' \ + --exclude='tmp/' \ + . + + - name: Create GitHub Release + uses: softprops/action-gh-release@v1 + with: + name: Release v${{ steps.version.outputs.VERSION }} + body: ${{ steps.changelog.outputs.BODY }} + files: slack-github-threads-v${{ steps.version.outputs.VERSION }}.tar.gz + draft: false + prerelease: false diff --git a/README.md b/README.md index 52642f1..e9e316b 100644 --- a/README.md +++ b/README.md @@ -209,14 +209,20 @@ docker run -p 3000:3000 --env-file .env slack-github-threads ### Running Tests ```bash +# Show available rake tasks and quick start guide +bundle exec rake help + # Run all CI checks (recommended for development) bundle exec rake ci -# Individual commands -bundle exec rake rubocop # Run RuboCop linter -bundle exec rake test # Run all tests +# Code quality checks +bundle exec rake lint # Run linting (syntax + rubocop) +bundle exec rake rubocop # Run RuboCop linter only bundle exec rake syntax # Check syntax only +# Testing +bundle exec rake test # Run all tests + # Run specific test groups bundle exec rake test_services bundle exec rake test_app @@ -283,6 +289,67 @@ The application follows Sinatra best practices with clear separation of concerns 4. Test your changes 5. Submit a pull request +## Releases + +This project supports multiple release workflows: automated GitHub Actions, smart Rake tasks, and interactive scripts. + +### ๐Ÿš€ GitHub Actions Release (Recommended) + +Create releases directly from GitHub's web interface: + +1. **Go to Actions tab** โ†’ **"Create Release PR"** workflow +2. **Click "Run workflow"** and choose: + - `auto` - Let the system analyze commits and suggest release type + - `major/minor/patch` - Specify release type manually + - `dry_run` - Preview what would be released +3. **Review and merge** the created PR +4. **Release is automatically published** when PR merges + +### ๐Ÿง  Smart Local Release Process + +```bash +# Preview changes and get version suggestion +rake release:preview + +# Create releases by type (automatic version bumping) +rake release:major # Breaking changes (1.0.0 โ†’ 2.0.0) +rake release:minor # New features (1.0.0 โ†’ 1.1.0) +rake release:patch # Bug fixes (1.0.0 โ†’ 1.0.1) + +# Push to trigger automated GitHub release +git push origin main && git push origin v +``` + +### ๐ŸŽฏ Interactive Release Script + +```bash +# Interactive script with smart suggestions +./scripts/release.sh + +# Or specify release type directly +./scripts/release.sh minor +``` + +### Key Features + +- **๐Ÿค– Fully Automated**: GitHub Actions handles everything including PR creation +- **๐Ÿง  Smart Version Detection**: Analyzes commits to suggest appropriate version bump +- **๐Ÿ“ Automatic Changelog**: Generates changelog from conventional commit messages +- **๐Ÿš€ One-Click Releases**: Complete release process with testing and validation +- **๐Ÿ“‹ Preview Mode**: See what will be released before committing +- **๐Ÿ‘ฅ Team Friendly**: PR-based workflow for team review + +### Commit Message Conventions + +For optimal automatic changelog generation: + +- `feat: add new feature` โ†’ **Added** section โ†’ **minor** version bump +- `fix: resolve bug` โ†’ **Fixed** section โ†’ **patch** version bump +- `feat!: breaking change` โ†’ **Added** section โ†’ **major** version bump +- `chore: update dependencies` โ†’ **Changed** section โ†’ **patch** version bump + +See [docs/CONVENTIONAL_COMMITS.md](docs/CONVENTIONAL_COMMITS.md) for detailed commit message guidelines. + ## Security - Never commit tokens or secrets to the repository diff --git a/Rakefile b/Rakefile index 89a0e46..b5a42ee 100644 --- a/Rakefile +++ b/Rakefile @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'rake/testtask' +require_relative 'lib/version_helper' begin require 'rubocop/rake_task' @@ -9,8 +10,45 @@ rescue LoadError # RuboCop not available end -# Default task -task default: :test +# Default task - show help +task default: :help + +# Help task - show commonly used tasks +desc 'Show commonly used rake tasks' +task :help do + puts "\n๐Ÿš€ slack-github-threads - Available Rake Tasks\n\n" + + puts '๐Ÿ“‹ Development:' + puts ' rake test # Run all tests' + puts ' rake ci # Run all CI checks (syntax + rubocop + tests)' + puts ' rake server # Start the development server' + puts ' rake install # Install dependencies' + puts '' + + puts '๐Ÿ”ง Code Quality:' + puts ' rake lint # Run linting checks (syntax + rubocop)' + puts ' rake syntax # Check Ruby syntax only' + puts ' rake rubocop # Run RuboCop linter only' + puts '' + + puts '๐Ÿ“ฆ Release Management:' + puts ' rake release:preview # Preview next release and changelog' + puts ' rake release:patch # Create patch release (bug fixes)' + puts ' rake release:minor # Create minor release (new features)' + puts ' rake release:major # Create major release (breaking changes)' + puts '' + + puts '๐Ÿ“š More Information:' + puts ' rake -T # Show all available tasks with descriptions' + puts ' rake -T release # Show only release-related tasks' + puts '' + + puts '๐Ÿ’ก Quick Start:' + puts " 1. Run 'rake test' to ensure everything works" + puts " 2. Run 'rake release:preview' to see what would be released" + puts " 3. Run 'rake release:patch' (or minor/major) to create a release" + puts '' +end # Test task Rake::TestTask.new(:test) do |t| @@ -53,10 +91,10 @@ task :syntax do puts 'All files have valid syntax!' end -# Run all CI checks (same as GitHub Actions) -desc 'Run all CI checks (syntax + rubocop + tests) - same as GitHub Actions' -task :ci do - puts '๐Ÿ” Running CI checks...' +# Run linting checks (syntax + rubocop) +desc 'Run linting checks (syntax + rubocop)' +task :lint do + puts '๐Ÿ” Running linting checks...' puts "\n๐Ÿ“‹ Step 1: Checking syntax..." Rake::Task[:syntax].invoke @@ -68,8 +106,186 @@ task :ci do puts 'โš ๏ธ RuboCop not available, skipping...' end - puts "\n๐Ÿงช Step 3: Running tests..." + puts "\nโœ… All linting checks passed!" +end + +# Run all CI checks (same as GitHub Actions) +desc 'Run all CI checks (syntax + rubocop + tests) - same as GitHub Actions' +task :ci do + puts '๐Ÿ” Running CI checks...' + + puts "\n๏ฟฝ Step 1: Running linting checks..." + Rake::Task[:lint].invoke + + puts "\n๐Ÿงช Step 2: Running tests..." Rake::Task[:test].invoke puts "\nโœ… All CI checks passed!" end + +# Release management tasks +namespace :release do + desc 'Generate changelog from git commits since last tag' + task :changelog, [:version] do |_t, args| + version = args[:version] || ENV.fetch('VERSION', nil) + + if version.nil? + puts 'โŒ Version required. Usage: rake release:changelog[1.1.0] or VERSION=1.1.0 rake release:changelog' + exit(1) + end + + puts "๐Ÿ“ Generating changelog for version #{version}..." + generate_changelog_for_version(version) + end + + desc 'Create a new release with automatic version bumping' + task :create, [:bump_type] do |_t, args| + bump_type = args[:bump_type] || ENV['BUMP_TYPE'] || 'patch' + + unless %w[major minor patch].include?(bump_type) + puts 'โŒ Invalid bump type. Use: major, minor, or patch' + exit(1) + end + + puts "๐Ÿš€ Creating #{bump_type} release..." + + # Check if working directory is clean + unless `git status --porcelain`.strip.empty? + puts 'โŒ Working directory is not clean. Please commit or stash your changes.' + system('git status --short') + exit(1) + end + + # Determine next version based on bump type and commit analysis + version = determine_next_version(bump_type) + puts "๐Ÿ“Š Next version will be: #{version}" + + # Run CI checks + puts "\n๐Ÿ” Running CI checks..." + Rake::Task[:ci].invoke + + # Generate changelog + puts "\n๐Ÿ“ Generating changelog..." + generate_changelog_for_version(version) + + # Create tag + tag = "v#{version}" + puts "\n๐Ÿท๏ธ Creating tag #{tag}..." + system('git add CHANGELOG.md') if File.exist?('CHANGELOG.md') + system("git commit -m 'Update changelog for #{version}'") unless `git status --porcelain CHANGELOG.md`.strip.empty? + system("git tag -a #{tag} -m 'Release version #{version}'") + + puts "\nโœ… Release #{version} created locally!" + puts "๐Ÿ“ค Push with: git push origin main && git push origin #{tag}" + puts '๐ŸŒ Monitor at: https://github.com/markhallen/slack-github-threads/actions' + end + + desc 'Create a major release (breaking changes)' + task :major do + Rake::Task['release:create'].invoke('major') + end + + desc 'Create a minor release (new features)' + task :minor do + Rake::Task['release:create'].invoke('minor') + end + + desc 'Create a patch release (bug fixes)' + task :patch do + Rake::Task['release:create'].invoke('patch') + end + + desc 'Preview changelog for current unreleased commits' + task :preview do + puts '๐Ÿ“‹ Previewing changelog for unreleased commits...' + + # Get version information using VersionHelper + info = VersionHelper.version_info + current_version = info[:current] + suggested_type = info[:suggested_type] + + puts "\n๐Ÿ’ก Suggested release type: #{suggested_type}" + puts "๐Ÿ“Š Current version: #{current_version}" + puts "๐ŸŽฏ Next patch version would be: #{info[:next_patch]}" + puts "๐ŸŽฏ Next minor version would be: #{info[:next_minor]}" + puts "๐ŸŽฏ Next major version would be: #{info[:next_major]}" + + # Show preview of what the changelog would look like + puts "\n๐Ÿ“ Changelog preview for #{suggested_type} release:" + puts VersionHelper.generate_changelog_for_version( + VersionHelper.determine_next_version(suggested_type) + ) + end + + desc 'Show current version' + task :version do + version = VersionHelper.current_version + puts version ? "Current version: #{version}" : 'No version tags found' + end +end + +# Helper methods for release management (delegating to VersionHelper) +def determine_next_version(bump_type) + VersionHelper.determine_next_version(bump_type) +end + +def current_version + VersionHelper.current_version +end + +def bump_version(version, bump_type) + VersionHelper.bump_version(version, bump_type) +end + +def generate_changelog_for_version(version) + content = VersionHelper.generate_changelog_for_version(version) + puts content + + # Update CHANGELOG.md if it exists + if File.exist?('CHANGELOG.md') + update_changelog_file(version, content) + else + create_changelog_file(version, content) + end +end + +def update_changelog_file(version, content) + changelog_file = 'CHANGELOG.md' + existing_content = File.read(changelog_file) + + # Insert new section after "## [Unreleased]" or at the beginning + if existing_content.include?('## [Unreleased]') + # Find the end of the Unreleased section + lines = existing_content.split("\n") + unreleased_end = lines.find_index { |line| line.match?(/^## \[.*\]/) && !line.include?('Unreleased') } + unreleased_end ||= lines.length + + # Insert new section + lines[unreleased_end, 0] = content.split("\n") + updated_content = lines.join("\n") + else + # No existing structure, prepend content + updated_content = "#{content}\n#{existing_content}" + end + + File.write(changelog_file, updated_content) + puts "โœ… Updated #{changelog_file} with version #{version}" +end + +def create_changelog_file(version, content) + changelog_file = 'CHANGELOG.md' + header = build_changelog_header + + File.write(changelog_file, header + content) + puts "โœ… Created #{changelog_file} with version #{version}" +end + +def build_changelog_header + [ + "# Changelog\n", + "All notable changes to this project will be documented in this file.\n", + 'The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),', + "\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).", + "\n\n## [Unreleased]\n\n", + ].join("\n") +end diff --git a/app.rb b/app.rb index 46ff664..fe801ea 100644 --- a/app.rb +++ b/app.rb @@ -34,7 +34,7 @@ # Add startup logging (only in debug mode) if settings.debug_mode - puts 'Starting gh-commenter app...' + puts 'Starting slack-github-threads app...' puts "RACK_ENV: #{ENV.fetch('RACK_ENV', nil)}" puts "PORT: #{ENV.fetch('PORT', nil)}" puts "Environment variables loaded: #{ENV.keys.grep(/GITHUB|SLACK/).join(', ')}" @@ -42,7 +42,7 @@ end # Log startup - settings.logger.info "Starting gh-commenter app (#{settings.environment})" + settings.logger.info "Starting slack-github-threads app (#{settings.environment})" end # Helpers diff --git a/docs/CONVENTIONAL_COMMITS.md b/docs/CONVENTIONAL_COMMITS.md new file mode 100644 index 0000000..dedf902 --- /dev/null +++ b/docs/CONVENTIONAL_COMMITS.md @@ -0,0 +1,85 @@ +# Conventional Commits Guide + +This project uses conventional commits for automatic changelog generation. + +## Commit Message Format + +``` +[optional scope]: + +[optional body] + +[optional footer(s)] +``` + +## Types + +- **feat**: A new feature (MINOR version bump) +- **fix**: A bug fix (PATCH version bump) +- **docs**: Documentation only changes +- **style**: Changes that do not affect the meaning of the code +- **refactor**: A code change that neither fixes a bug nor adds a feature +- **perf**: A code change that improves performance +- **test**: Adding missing tests or correcting existing tests +- **chore**: Changes to the build process or auxiliary tools + +## Examples + +```bash +# New feature +git commit -m "feat: add webhook endpoint for GitHub events" + +# Bug fix +git commit -m "fix: resolve timeout issue in Slack API calls" + +# Documentation +git commit -m "docs: update installation instructions" + +# Chore +git commit -m "chore: update Ruby to 3.3" + +# Breaking change (MAJOR version bump) +git commit -m "feat!: change API response format + +BREAKING CHANGE: The /api/threads endpoint now returns an array instead of an object" +``` + +## Changelog Mapping + +| Commit Type | Changelog Section | +| ---------------------------------------------------- | ------------------- | +| `feat` | Added | +| `fix` | Fixed | +| `docs`, `style`, `refactor`, `perf`, `test`, `chore` | Changed | +| Breaking changes | Changed (with note) | + +## Release Workflow + +### ๐Ÿš€ GitHub Actions (Recommended) + +1. Make commits following conventional format +2. Go to **Actions** โ†’ **"Create Release PR"** โ†’ **"Run workflow"** +3. Choose release type (or use `auto` for smart suggestions) +4. Review and merge the created PR +5. Release is automatically published! + +### ๐Ÿง  Local Development + +1. Make commits following conventional format +2. Preview changes: `rake release:preview` +3. Create release by type: + - `rake release:major` (or `./scripts/release.sh major`) + - `rake release:minor` (or `./scripts/release.sh minor`) + - `rake release:patch` (or `./scripts/release.sh patch`) +4. Push: `git push origin main && git push origin v` + +### Version Bumping Rules + +| Commit Type | Version Bump | Example | +| ------------------------------ | ------------ | ------------- | +| `feat!:` or `BREAKING CHANGE:` | **MAJOR** | 1.0.0 โ†’ 2.0.0 | +| `feat:` | **MINOR** | 1.0.0 โ†’ 1.1.0 | +| `fix:` | **PATCH** | 1.0.0 โ†’ 1.0.1 | +| `chore:`, `docs:`, etc. | **PATCH** | 1.0.0 โ†’ 1.0.1 | + +The system analyzes all unreleased commits and suggests the appropriate version bump automatically! diff --git a/docs/RELEASE_SYSTEM.md b/docs/RELEASE_SYSTEM.md new file mode 100644 index 0000000..da05ae1 --- /dev/null +++ b/docs/RELEASE_SYSTEM.md @@ -0,0 +1,155 @@ +# Release System Summary + +## ๐ŸŽฏ **Complete Release System with GitHub Actions** + +A fully automated release system supporting multiple workflows: GitHub Actions (recommended), local Rake tasks, and interactive scripts. + +### **๐Ÿš€ GitHub Actions Workflow (NEW & Recommended)** + +#### **How to Use** + +1. **Go to Actions tab** โ†’ **"Create Release PR"** workflow +2. **Click "Run workflow"** โ†’ Choose options: + - `auto` - Smart commit analysis with version suggestions + - `major/minor/patch` - Manual release type selection + - `dry_run` - Preview mode without creating actual release +3. **Review and merge** the automatically created PR +4. **Release publishes automatically** when PR merges + +#### **What It Does** + +``` +GitHub Actions Workflow: +โ”œโ”€โ”€ Analyzes commits since last release +โ”œโ”€โ”€ Suggests appropriate version bump (if auto) +โ”œโ”€โ”€ Runs full CI test suite +โ”œโ”€โ”€ Generates changelog from conventional commits +โ”œโ”€โ”€ Creates release branch (release/vX.Y.Z) +โ”œโ”€โ”€ Opens detailed PR with release info +โ”œโ”€โ”€ Auto-publishes release when PR merges +โ””โ”€โ”€ Sends notifications and updates +``` + +### **๐Ÿง  Available Commands (All Methods)** + +#### **GitHub Actions (Web UI)** + +- **Actions โ†’ "Create Release PR"** - Complete automated workflow +- **Choose release type** - `auto`, `major`, `minor`, `patch` +- **Dry run option** - Preview without creating release + +#### **Local Rake Tasks** + +```bash +rake release:major # Breaking changes (1.0.0 โ†’ 2.0.0) +rake release:minor # New features (1.0.0 โ†’ 1.1.0) +rake release:patch # Bug fixes (1.0.0 โ†’ 1.0.1) +rake release:preview # Preview unreleased changes + suggestions +rake release:version # Show current version +``` + +#### **Interactive Script** + +```bash +./scripts/release.sh # Interactive with suggestions +./scripts/release.sh minor # Direct release type +``` + +### **๐Ÿ”„ Complete Workflow Examples** + +#### **Recommended: GitHub Actions** + +``` +1. Commit changes with conventional messages +2. Go to Actions โ†’ "Create Release PR" โ†’ "Run workflow" +3. Select "auto" (or specific type) +4. Review the created PR +5. Merge PR โ†’ Release published automatically! ๐ŸŽ‰ +``` + +#### **Local Development** + +```bash +# Quick preview and release +rake release:preview # See what's changed +rake release:minor # Create release +git push origin main && git push origin v1.1.0 + +# Or interactive +./scripts/release.sh # Guided workflow +``` + +### **๐Ÿง  Intelligent Features** + +1. **Fully Automated Workflow**: GitHub Actions handles everything +2. **Smart Commit Analysis**: Suggests version bumps based on commits +3. **PR-Based Reviews**: Team can review releases before publishing +4. **Automatic Version Bumping**: No manual version number management +5. **Comprehensive Testing**: Full CI suite runs before each release +6. **Multi-Channel Support**: Web UI, command line, and scripts + +### **๐Ÿ“‹ Version Bump Logic** + +- **MAJOR**: `feat!:`, `BREAKING CHANGE:`, or any commit with `!:` +- **MINOR**: `feat:`, `add`, `implement` patterns +- **PATCH**: `fix:`, `chore:`, `docs:`, etc. + +### **๐Ÿ“ File Structure** + +#### **GitHub Actions** + +- `.github/workflows/create-release-pr.yml` - **NEW**: Creates release PRs +- `.github/workflows/release.yml` - **UPDATED**: Publishes releases from PRs or tags + +#### **Local Tools** + +- `Rakefile` - Smart release tasks with version detection +- `scripts/release.sh` - Interactive release script +- `docs/CONVENTIONAL_COMMITS.md` - Commit message guidelines + +### **โœ… Benefits of GitHub Actions Integration** + +1. **๐ŸŒ Web-Based**: No local development environment required +2. **๐Ÿ‘ฅ Team Friendly**: PR-based workflow for collaboration +3. **๐Ÿ”„ Fully Automated**: From commit analysis to release publishing +4. **๐Ÿ›ก๏ธ Safe**: Built-in testing and review process +5. **๐Ÿ“ Documented**: Detailed PR descriptions with release info +6. **๐ŸŽฏ Flexible**: Supports auto-detection and manual override +7. **๐Ÿ“Š Trackable**: Full audit trail in GitHub + +### **๐ŸŽฌ Real Usage Examples** + +#### **Example 1: Smart Auto Release** + +``` +Actions โ†’ "Create Release PR" โ†’ Run workflow +โ”œโ”€โ”€ Release type: "auto" +โ”œโ”€โ”€ System analyzes: 3 feat commits, 2 fix commits +โ”œโ”€โ”€ Suggests: "minor" release +โ”œโ”€โ”€ Creates: PR "Release v1.2.0" +โ”œโ”€โ”€ You: Review and merge PR +โ””โ”€โ”€ Result: v1.2.0 published automatically! โœจ +``` + +#### **Example 2: Manual Override** + +``` +Actions โ†’ "Create Release PR" โ†’ Run workflow +โ”œโ”€โ”€ Release type: "major" (manual choice) +โ”œโ”€โ”€ Creates: PR "Release v2.0.0" +โ”œโ”€โ”€ You: Review breaking changes +โ”œโ”€โ”€ You: Merge PR +โ””โ”€โ”€ Result: v2.0.0 published with breaking changes! ๐Ÿš€ +``` + +#### **Example 3: Preview Mode** + +``` +Actions โ†’ "Create Release PR" โ†’ Run workflow +โ”œโ”€โ”€ Release type: "auto" +โ”œโ”€โ”€ Dry run: โœ… (checked) +โ”œโ”€โ”€ Result: Shows what would be released +โ””โ”€โ”€ No actual PR created (preview only) ๐Ÿ‘€ +``` + +This system provides the best of all worlds: **automated convenience** for teams, **local control** for developers, and **smart intelligence** for version management! ๐ŸŽ‰ diff --git a/docs/VERSION_HELPER_REFACTORING.md b/docs/VERSION_HELPER_REFACTORING.md new file mode 100644 index 0000000..e7ead63 --- /dev/null +++ b/docs/VERSION_HELPER_REFACTORING.md @@ -0,0 +1,165 @@ +# Code Refactoring: Extracted Version Helper + +## Problem + +The GitHub Actions workflow in `.github/workflows/create-release-pr.yml` contained inline Ruby code that duplicated version bumping logic from the Rakefile: + +```ruby +# Duplicated inline Ruby in GitHub Actions +NEXT_VERSION=$(ruby -e " + require_relative 'Rakefile' + current = '$CURRENT_VERSION' == 'none' ? nil : '$CURRENT_VERSION' + puts bump_version(current, '$RELEASE_TYPE') +") +``` + +This created several issues: + +- **Code duplication** between Rakefile and GitHub Actions +- **Maintenance burden** when updating version logic +- **Fragile inline code** that's hard to test and debug +- **Poor separation of concerns** + +## Solution + +Created a dedicated `VersionHelper` module and CLI script to centralize all version-related logic: + +### 1. New Files Created + +#### `lib/version_helper.rb` + +- **Centralized version logic** for semantic versioning operations +- **Conventional commit analysis** for automatic version bump detection +- **Changelog generation** with proper categorization +- **Comprehensive error handling** and validation + +#### `scripts/version.rb` + +- **CLI interface** for version operations +- **GitHub Actions friendly** with simple command interface +- **Executable script** that can be called from any environment + +### 2. Key Refactoring Changes + +#### Before (Duplicated): + +```ruby +# In Rakefile +def bump_version(version, bump_type) + # version logic here +end + +# In GitHub Actions (inline Ruby) +NEXT_VERSION=$(ruby -e "complex inline code") +``` + +#### After (DRY): + +```ruby +# In lib/version_helper.rb +module VersionHelper + def self.bump_version(current_version, release_type) + # centralized logic + end +end + +# In Rakefile +def bump_version(version, bump_type) + VersionHelper.bump_version(version, bump_type) +end + +# In GitHub Actions +NEXT_VERSION=$(ruby scripts/version.rb next "$RELEASE_TYPE") +``` + +### 3. Updated Components + +#### Rakefile + +- **Simplified helper methods** that delegate to VersionHelper +- **Removed duplicate logic** for commit analysis and version bumping +- **Enhanced preview task** using centralized version info + +#### GitHub Actions Workflow + +- **Replaced inline Ruby** with clean CLI script calls +- **Improved maintainability** with external script dependency +- **Better error handling** and debugging capabilities + +### 4. CLI Interface Examples + +```bash +# Get current version +ruby scripts/version.rb current + +# Get next version (auto-detect from commits) +ruby scripts/version.rb next auto + +# Get specific version bump +ruby scripts/version.rb next patch + +# Analyze commits for suggested release type +ruby scripts/version.rb analyze + +# Get comprehensive version information +ruby scripts/version.rb info +``` + +## Benefits + +### โœ… **Eliminated Code Duplication** + +- Single source of truth for version logic +- Consistent behavior across all tools + +### โœ… **Improved Maintainability** + +- Changes only need to be made in one place +- Easier to test and debug version logic + +### โœ… **Better Separation of Concerns** + +- Version logic isolated in dedicated module +- CLI script provides clean interface + +### โœ… **Enhanced Testability** + +- Helper module can be unit tested independently +- CLI script provides clear interfaces + +### โœ… **GitHub Actions Optimization** + +- Cleaner workflow YAML without inline Ruby +- Better error messages and debugging + +## Testing + +The refactoring maintains full backward compatibility: + +```bash +# All existing Rake tasks still work +bundle exec rake release:preview +bundle exec rake release:create[patch] + +# New CLI interface works +ruby scripts/version.rb info +# Current version: none +# Suggested release type: minor +# Next patch: 1.0.0 +# Next minor: 1.0.0 +# Next major: 1.0.0 +``` + +## Files Modified + +### New Files + +- `lib/version_helper.rb` - Centralized version management module +- `scripts/version.rb` - CLI interface for version operations + +### Modified Files + +- `Rakefile` - Updated to use VersionHelper instead of duplicated logic +- `.github/workflows/create-release-pr.yml` - Replaced inline Ruby with CLI script calls + +This refactoring follows the **DRY (Don't Repeat Yourself)** principle and provides a more maintainable, testable, and robust version management system. diff --git a/lib/version_helper.rb b/lib/version_helper.rb new file mode 100644 index 0000000..621a1d1 --- /dev/null +++ b/lib/version_helper.rb @@ -0,0 +1,204 @@ +# frozen_string_literal: true + +require 'English' +require 'date' + +# Helper module for version management and release operations +module VersionHelper + # Version pattern for semantic versioning + VERSION_PATTERN = /^v?(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9\-]+ + (?:\.[a-zA-Z0-9\-]+)*))?(?:\+([a-zA-Z0-9\-]+ + (?:\.[a-zA-Z0-9\-]+)*))?$/x + + # Bump version based on release type + def self.bump_version(current_version, release_type) + return '1.0.0' if current_version.nil? || current_version == 'none' + + # Remove 'v' prefix if present + version_string = current_version.sub(/^v/, '') + + match = version_string.match(VERSION_PATTERN) + raise "Invalid version format: #{current_version}" unless match + + major = match[1].to_i + minor = match[2].to_i + patch = match[3].to_i + + case release_type.to_s.downcase + when 'major' + "#{major + 1}.0.0" + when 'minor' + "#{major}.#{minor + 1}.0" + when 'patch' + "#{major}.#{minor}.#{patch + 1}" + else + raise "Invalid release type: #{release_type}" + end + end + + # Get the current version from git tags + def self.current_version + # Get the latest tag that looks like a version + latest_tag = `git describe --tags --abbrev=0 --match="v*" 2>/dev/null`.strip + + return nil if latest_tag.empty? || $CHILD_STATUS.exitstatus != 0 + + latest_tag + end + + # Analyze commits since last version to determine suggested release type + def self.analyze_commits_for_version_bump(since_ref = nil) + since_ref ||= current_version + return 'minor' if since_ref.nil? + + commits = get_commits_since(since_ref) + return 'patch' if commits.empty? + + determine_version_bump_type(commits) + end + + # Get commits since a reference + def self.get_commits_since(since_ref) + `git log #{since_ref}..HEAD --oneline --no-merges 2>/dev/null`.strip.split("\n") + end + + # Determine version bump type based on commits + def self.determine_version_bump_type(commits) + has_breaking = false + has_feature = false + + commits.each do |commit| + commit_msg = commit.split(' ', 2)[1] || '' + + if breaking_change?(commit_msg) + has_breaking = true + elsif feature_commit?(commit_msg) + has_feature = true + end + end + + return 'major' if has_breaking + return 'minor' if has_feature + + 'patch' + end + + # Check if commit message indicates a breaking change + def self.breaking_change?(commit_msg) + commit_msg.include?('BREAKING CHANGE') || commit_msg.match(/^[^:]+!:/) + end + + # Check if commit message indicates a feature + def self.feature_commit?(commit_msg) + commit_msg.start_with?('feat') + end + + # Generate changelog entries for a version + def self.generate_changelog_for_version(version, since_ref = nil) + since_ref ||= current_version + + commits = get_commits_for_changelog(since_ref) + categorized_commits = categorize_commits(commits) + + build_changelog_content(version, categorized_commits) + end + + # Get commits for changelog generation + def self.get_commits_for_changelog(since_ref) + if since_ref.nil? + `git log --oneline --no-merges`.strip.split("\n") + else + `git log #{since_ref}..HEAD --oneline --no-merges`.strip.split("\n") + end + end + + # Categorize commits by type + def self.categorize_commits(commits) + categories = { + features: [], + fixes: [], + breaking_changes: [], + other_changes: [], + } + + commits.reverse.each do |commit| + _sha, message = commit.split(' ', 2) + next unless message + + categorize_single_commit(message, categories) + end + + categories + end + + # Categorize a single commit message + def self.categorize_single_commit(message, categories) + case message + when /^feat(\(.+\))?!?:/ + if message.include?('!') || message.include?('BREAKING CHANGE') + categories[:breaking_changes] << format_commit_message(message, /^feat(\(.+\))?!?: /) + else + categories[:features] << format_commit_message(message, /^feat(\(.+\))?: /) + end + when /^fix(\(.+\))?:/ + categories[:fixes] << format_commit_message(message, /^fix(\(.+\))?: /) + when /BREAKING CHANGE/ + categories[:breaking_changes] << "- #{message}" + else + categories[:other_changes] << "- #{message}" + end + end + + # Format commit message by removing prefix + def self.format_commit_message(message, prefix_pattern) + "- #{message.sub(prefix_pattern, '')}" + end + + # Build changelog content from categorized commits + def self.build_changelog_content(version, categories) + content = [] + content << "## [#{version}] - #{Date.today.strftime('%Y-%m-%d')}" + content << '' + + add_changelog_section(content, '### โš ๏ธ BREAKING CHANGES', categories[:breaking_changes]) + add_changelog_section(content, '### โœจ Features', categories[:features]) + add_changelog_section(content, '### ๐Ÿ› Bug Fixes', categories[:fixes]) + add_changelog_section(content, '### ๐Ÿ”ง Other Changes', categories[:other_changes]) + + content.join("\n") + end + + # Add a section to changelog content if items exist + def self.add_changelog_section(content, header, items) + return if items.empty? + + content << header + content << '' + content.concat(items) + content << '' + end + + # Determine the next version based on commits + def self.determine_next_version(release_type = 'auto') + current = current_version + + if release_type == 'auto' + suggested_type = analyze_commits_for_version_bump(current) + bump_version(current, suggested_type) + else + bump_version(current, release_type) + end + end + + # Get version information as a hash + def self.version_info + current = current_version + { + current: current || 'none', + suggested_type: analyze_commits_for_version_bump(current), + next_patch: bump_version(current, 'patch'), + next_minor: bump_version(current, 'minor'), + next_major: bump_version(current, 'major'), + } + end +end diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 0000000..f6b9460 --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,119 @@ +#!/bin/bash + +# slack-github-threads Release Script +# Usage: ./scripts/release.sh [major|minor|patch] +# Example: ./scripts/release.sh minor + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Function to print colored output +print_status() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +print_warning() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +print_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Determine bump type +BUMP_TYPE=${1:-"auto"} + +# If no argument provided, show preview and ask user +if [ "$BUMP_TYPE" = "auto" ]; then + print_status "Analyzing commits to suggest release type..." + + if command -v bundle &> /dev/null; then + bundle exec rake release:preview + else + rake release:preview + fi + + echo + echo "Release types:" + echo " major - Breaking changes (1.0.0 โ†’ 2.0.0)" + echo " minor - New features (1.0.0 โ†’ 1.1.0)" + echo " patch - Bug fixes (1.0.0 โ†’ 1.0.1)" + echo + read -p "Which type of release? [major/minor/patch]: " -r + BUMP_TYPE=$REPLY +fi + +# Validate bump type +if [[ ! "$BUMP_TYPE" =~ ^(major|minor|patch)$ ]]; then + print_error "Invalid release type. Use: major, minor, or patch" + echo "Usage: $0 [major|minor|patch]" + exit 1 +fi + +print_status "Creating $BUMP_TYPE release..." + +# Check if we're on the main branch +CURRENT_BRANCH=$(git branch --show-current) +if [ "$CURRENT_BRANCH" != "main" ]; then + print_warning "You are not on the main branch (current: $CURRENT_BRANCH)" + read -p "Continue anyway? (y/N): " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + print_error "Aborted" + exit 1 + fi +fi + +# Check if working directory is clean +if [ -n "$(git status --porcelain)" ]; then + print_error "Working directory is not clean. Please commit or stash your changes." + git status --short + exit 1 +fi + +# Use Rake task to create the release +print_status "Using Rake to create $BUMP_TYPE release (includes tests, changelog, and tagging)..." +if command -v bundle &> /dev/null; then + bundle exec rake release:create[$BUMP_TYPE] +else + rake release:create[$BUMP_TYPE] +fi + +# Extract the version that was just created +VERSION=$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//') +if [ -z "$VERSION" ]; then + print_error "No tags found in the repository. Release creation may have failed." + exit 1 +fi +TAG="v$VERSION" + +print_status "Release $VERSION created successfully!" +print_status "Now push the changes and tag to GitHub..." + +echo +read -p "Push to GitHub now? (y/N): " -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]]; then + print_status "Pushing changes and tag to GitHub..." + git push origin "$CURRENT_BRANCH" + git push origin "$TAG" + + print_status "๐ŸŽ‰ Release $VERSION pushed to GitHub!" + print_status "GitHub Actions will automatically create the release." + print_status "Monitor progress at: https://github.com/markhallen/slack-github-threads/actions" +else + print_status "Manual push required:" + echo " git push origin $CURRENT_BRANCH" + echo " git push origin $TAG" +fi + +echo +print_status "Next steps:" +echo " 1. Monitor the GitHub Actions workflow" +echo " 2. Verify the release was created at: https://github.com/markhallen/slack-github-threads/releases" +echo " 3. Update any deployment configurations if needed" diff --git a/scripts/version.rb b/scripts/version.rb new file mode 100755 index 0000000..6fc8405 --- /dev/null +++ b/scripts/version.rb @@ -0,0 +1,68 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# CLI script for version operations +# Usage: +# ruby scripts/version.rb current # Get current version +# ruby scripts/version.rb next [type] # Get next version (auto/major/minor/patch) +# ruby scripts/version.rb bump [type] # Same as next (alias) +# ruby scripts/version.rb analyze # Analyze commits for suggested release type +# ruby scripts/version.rb info # Get all version information + +require_relative '../lib/version_helper' + +def main + command = ARGV[0] + + case command + when 'current' + handle_current_command + when 'next', 'bump' + handle_next_command + when 'analyze' + handle_analyze_command + when 'info' + handle_info_command + else + show_usage + end +rescue StandardError => e + warn "Error: #{e.message}" + exit 1 +end + +def handle_current_command + puts VersionHelper.current_version || 'none' +end + +def handle_next_command + release_type = ARGV[1] || 'auto' + puts VersionHelper.determine_next_version(release_type) +end + +def handle_analyze_command + puts VersionHelper.analyze_commits_for_version_bump +end + +def handle_info_command + info = VersionHelper.version_info + puts "Current version: #{info[:current]}" + puts "Suggested release type: #{info[:suggested_type]}" + puts "Next patch: #{info[:next_patch]}" + puts "Next minor: #{info[:next_minor]}" + puts "Next major: #{info[:next_major]}" +end + +def show_usage + puts "Usage: #{$PROGRAM_NAME} [args]" + puts '' + puts 'Commands:' + puts ' current Get current version' + puts ' next [type] Get next version (auto/major/minor/patch)' + puts ' bump [type] Same as next (alias)' + puts ' analyze Analyze commits for suggested release type' + puts ' info Get all version information' + exit 1 +end + +main if __FILE__ == $PROGRAM_NAME