From 8b102154a6fc351e41e1cb97f91d05a6bbad9cd3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 30 Aug 2025 22:10:01 +0000 Subject: [PATCH 1/5] Initial plan From 0f667f44d203983a959cc90a200b4686687ab2e5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 30 Aug 2025 22:15:37 +0000 Subject: [PATCH 2/5] feat: add gitflow-esque release workflow with keepachangelog support Co-authored-by: Ryangr0 <3602480+Ryangr0@users.noreply.github.com> --- .../semantic-release/action.yml | 10 +- .github/workflows/gitflow-application.yml | 49 +++ .github/workflows/gitflow-release.yml | 292 ++++++++++++++++++ .releaserc.gitflow.json | 95 ++++++ docs/gitflow-workflow.md | 194 ++++++++++++ 5 files changed, 639 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/gitflow-application.yml create mode 100644 .github/workflows/gitflow-release.yml create mode 100644 .releaserc.gitflow.json create mode 100644 docs/gitflow-workflow.md diff --git a/.github/composite-actions/semantic-release/action.yml b/.github/composite-actions/semantic-release/action.yml index 017271d..94e26c8 100644 --- a/.github/composite-actions/semantic-release/action.yml +++ b/.github/composite-actions/semantic-release/action.yml @@ -37,7 +37,15 @@ runs: shell: bash run: | if [[ -n "${{ inputs.release-type }}" ]]; then - echo "release_config=--extends=./.releaserc.${{ inputs.release-type }}.json" >> $GITHUB_OUTPUT + # Check if custom config exists in the repo + if [[ -f "./.releaserc.${{ inputs.release-type }}.json" ]]; then + echo "release_config=--extends=./.releaserc.${{ inputs.release-type }}.json" >> $GITHUB_OUTPUT + # Fall back to webgrip/workflows configs + elif [[ -f "$(dirname "$0")/../../.releaserc.${{ inputs.release-type }}.json" ]]; then + echo "release_config=--extends=$(dirname "$0")/../../.releaserc.${{ inputs.release-type }}.json" >> $GITHUB_OUTPUT + else + echo "release_config=" >> $GITHUB_OUTPUT + fi else echo "release_config=" >> $GITHUB_OUTPUT fi diff --git a/.github/workflows/gitflow-application.yml b/.github/workflows/gitflow-application.yml new file mode 100644 index 0000000..b538300 --- /dev/null +++ b/.github/workflows/gitflow-application.yml @@ -0,0 +1,49 @@ +name: '[Application] Gitflow CI/CD' + +# This workflow provides a complete gitflow implementation for applications +# It should replace the traditional on_source_change.yml workflow + +on: + push: + branches: + - 'main' + - 'development' + - 'develop' + - 'feature/**' + - 'bugfix/**' + - 'hotfix/**' + paths: + - 'ops/**' + - 'src/**' + - '.releaserc.json' + - '.github/workflows/**' + pull_request: + branches: + - 'main' + - 'development' + - 'develop' + paths: + - 'ops/**' + - 'src/**' + - '.releaserc.json' + - '.github/workflows/**' + +concurrency: + group: gitflow-${{ github.ref }} + cancel-in-progress: true + +jobs: + gitflow: + name: 'Gitflow CI/CD' + uses: webgrip/workflows/.github/workflows/gitflow-release.yml@main + secrets: + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }} + DIGITAL_OCEAN_API_KEY: ${{ secrets.DIGITAL_OCEAN_API_KEY }} + SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }} + with: + source-paths: 'src/**' + ops-paths: 'ops/**' + static-analysis-enabled: true + tests-enabled: true + deploy-enabled: true diff --git a/.github/workflows/gitflow-release.yml b/.github/workflows/gitflow-release.yml new file mode 100644 index 0000000..dcc6dc2 --- /dev/null +++ b/.github/workflows/gitflow-release.yml @@ -0,0 +1,292 @@ +name: 'Gitflow Release Workflow' + +on: + workflow_call: + inputs: + source-paths: + description: 'Paths to monitor for source changes' + required: false + type: string + default: 'src/**' + ops-paths: + description: 'Paths to monitor for ops changes' + required: false + type: string + default: 'ops/**' + static-analysis-enabled: + description: 'Enable static analysis' + required: false + type: boolean + default: true + tests-enabled: + description: 'Enable tests' + required: false + type: boolean + default: true + deploy-enabled: + description: 'Enable deployment' + required: false + type: boolean + default: true + secrets: + DOCKER_USERNAME: + required: false + DOCKER_TOKEN: + required: false + DIGITAL_OCEAN_API_KEY: + required: false + SOPS_AGE_KEY: + required: false + +jobs: + # Determine what type of branch we're working with + branch-analysis: + name: 'Analyze Branch Type' + runs-on: arc-runner-set + outputs: + branch-type: ${{ steps.branch-type.outputs.type }} + is-main: ${{ steps.branch-type.outputs.is-main }} + is-development: ${{ steps.branch-type.outputs.is-development }} + is-feature: ${{ steps.branch-type.outputs.is-feature }} + should-run-tests: ${{ steps.branch-type.outputs.should-run-tests }} + should-create-release-pr: ${{ steps.branch-type.outputs.should-create-release-pr }} + should-run-semantic-release: ${{ steps.branch-type.outputs.should-run-semantic-release }} + steps: + - name: Determine branch type and actions + id: branch-type + run: | + BRANCH_NAME="${{ github.ref_name }}" + echo "Branch: $BRANCH_NAME" + + # Default values + IS_MAIN=false + IS_DEVELOPMENT=false + IS_FEATURE=false + SHOULD_RUN_TESTS=true + SHOULD_CREATE_RELEASE_PR=false + SHOULD_RUN_SEMANTIC_RELEASE=false + + # Determine branch type + if [[ "$BRANCH_NAME" == "main" ]]; then + BRANCH_TYPE="main" + IS_MAIN=true + SHOULD_RUN_SEMANTIC_RELEASE=true + elif [[ "$BRANCH_NAME" == "development" || "$BRANCH_NAME" == "develop" ]]; then + BRANCH_TYPE="development" + IS_DEVELOPMENT=true + SHOULD_CREATE_RELEASE_PR=true + elif [[ "$BRANCH_NAME" == feature/* ]] || [[ "$BRANCH_NAME" == bugfix/* ]] || [[ "$BRANCH_NAME" == hotfix/* ]]; then + BRANCH_TYPE="feature" + IS_FEATURE=true + else + BRANCH_TYPE="other" + fi + + # Output results + echo "type=$BRANCH_TYPE" >> $GITHUB_OUTPUT + echo "is-main=$IS_MAIN" >> $GITHUB_OUTPUT + echo "is-development=$IS_DEVELOPMENT" >> $GITHUB_OUTPUT + echo "is-feature=$IS_FEATURE" >> $GITHUB_OUTPUT + echo "should-run-tests=$SHOULD_RUN_TESTS" >> $GITHUB_OUTPUT + echo "should-create-release-pr=$SHOULD_CREATE_RELEASE_PR" >> $GITHUB_OUTPUT + echo "should-run-semantic-release=$SHOULD_RUN_SEMANTIC_RELEASE" >> $GITHUB_OUTPUT + + echo "Branch Type: $BRANCH_TYPE" + echo "Should run tests: $SHOULD_RUN_TESTS" + echo "Should create release PR: $SHOULD_CREATE_RELEASE_PR" + echo "Should run semantic release: $SHOULD_RUN_SEMANTIC_RELEASE" + + # Run static analysis on all branches + static-analysis: + name: 'Static Analysis' + needs: [branch-analysis] + if: inputs.static-analysis-enabled == true + uses: webgrip/workflows/.github/workflows/static-analysis.yml@main + + # Run tests on all branches + tests: + name: 'Tests' + needs: [branch-analysis, static-analysis] + if: | + always() && + inputs.tests-enabled == true && + needs.branch-analysis.outputs.should-run-tests == 'true' && + (needs.static-analysis.result == 'success' || needs.static-analysis.result == 'skipped') + uses: webgrip/workflows/.github/workflows/tests.yml@main + + # Create release PR from development to main + create-release-pr: + name: 'Create Release PR' + needs: [branch-analysis, static-analysis, tests] + if: | + always() && + needs.branch-analysis.outputs.should-create-release-pr == 'true' && + needs.tests.result == 'success' + runs-on: arc-runner-set + permissions: + contents: read + pull-requests: write + steps: + - name: Checkout development + uses: actions/checkout@v4 + with: + ref: development + fetch-depth: 0 + + - name: Check if release PR already exists + id: check-pr + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Check if there's already an open PR from development to main + EXISTING_PR=$(gh pr list --base main --head development --state open --json number --jq '.[0].number') + if [[ "$EXISTING_PR" != "null" && -n "$EXISTING_PR" ]]; then + echo "existing-pr=$EXISTING_PR" >> $GITHUB_OUTPUT + echo "PR already exists: #$EXISTING_PR" + else + echo "existing-pr=" >> $GITHUB_OUTPUT + echo "No existing PR found" + fi + + - name: Generate changelog for release + id: changelog + if: steps.check-pr.outputs.existing-pr == '' + run: | + # Get commits between main and development + COMMITS=$(git log main..development --pretty=format:"- %s" --no-merges) + + if [[ -z "$COMMITS" ]]; then + echo "No new commits to release" + echo "has-changes=false" >> $GITHUB_OUTPUT + exit 0 + fi + + echo "has-changes=true" >> $GITHUB_OUTPUT + + # Generate changelog content + CHANGELOG="## Changes in this release + + The following changes will be included in the next release: + + $COMMITS + + ## Release Notes + + This release includes the latest changes from the development branch. + Please review the changes above before merging. + + --- + *This PR was automatically created by the Gitflow Release Workflow.*" + + # Save changelog to file for use in PR + echo "$CHANGELOG" > /tmp/release-notes.md + echo "Generated changelog with $(echo "$COMMITS" | wc -l) commits" + + - name: Create release PR + if: steps.check-pr.outputs.existing-pr == '' && steps.changelog.outputs.has-changes == 'true' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Create PR from development to main + gh pr create \ + --title "🚀 Release: development → main" \ + --body-file /tmp/release-notes.md \ + --base main \ + --head development \ + --label "release" \ + --label "automated" + + echo "Created release PR from development to main" + + - name: Update existing release PR + if: steps.check-pr.outputs.existing-pr != '' && steps.changelog.outputs.has-changes == 'true' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Update the existing PR with new changelog + gh pr edit ${{ steps.check-pr.outputs.existing-pr }} \ + --body-file /tmp/release-notes.md + + echo "Updated existing release PR #${{ steps.check-pr.outputs.existing-pr }}" + + # Run semantic release on main branch only + semantic-release: + name: 'Semantic Release' + needs: [branch-analysis, static-analysis, tests] + if: | + always() && + needs.branch-analysis.outputs.should-run-semantic-release == 'true' && + needs.tests.result == 'success' + uses: webgrip/workflows/.github/workflows/semantic-release.yml@main + + # Determine changed secrets (only on main) + determine-changed-secrets: + name: "Determine Changed Secrets" + needs: [branch-analysis, semantic-release] + if: | + always() && + needs.branch-analysis.outputs.is-main == 'true' && + inputs.deploy-enabled == true && + (needs.semantic-release.result == 'success' || needs.semantic-release.result == 'skipped') + uses: webgrip/workflows/.github/workflows/determine-changed-directories.yml@main + with: + inside-dir: 'ops/secrets' + max-level: 1 + + # Deploy changed secrets (only on main) + deploy-changed-secrets: + name: "Deploy Changed Secrets" + needs: [branch-analysis, determine-changed-secrets] + if: | + always() && + needs.branch-analysis.outputs.is-main == 'true' && + needs.determine-changed-secrets.outputs.matrix != '[]' && + inputs.deploy-enabled == true + uses: webgrip/workflows/.github/workflows/helm-charts-deploy.yml@main + secrets: + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }} + DIGITAL_OCEAN_API_KEY: ${{ secrets.DIGITAL_OCEAN_API_KEY }} + SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }} + with: + paths: ${{ needs.determine-changed-secrets.outputs.matrix }} + + # Build Docker image (only on main after successful release) + build: + name: 'Build' + needs: [branch-analysis, semantic-release] + if: | + always() && + needs.branch-analysis.outputs.is-main == 'true' && + needs.semantic-release.result == 'success' && + needs.semantic-release.outputs.version != '' && + inputs.deploy-enabled == true + uses: webgrip/workflows/.github/workflows/docker-build-and-push.yml@main + secrets: + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }} + with: + docker-context: '.' + docker-file: './ops/docker/application/Dockerfile' + docker-tags: | + ${{github.repository_owner}}/${{ github.event.repository.name }}:latest + ${{github.repository_owner}}/${{ github.event.repository.name }}:${{ needs.semantic-release.outputs.version }} + + # Deploy application (only on main after successful build) + deploy-application: + name: 'Deploy Application' + needs: [branch-analysis, semantic-release, build, deploy-changed-secrets] + if: | + always() && + needs.branch-analysis.outputs.is-main == 'true' && + needs.build.result == 'success' && + inputs.deploy-enabled == true + uses: webgrip/workflows/.github/workflows/helm-chart-deploy.yml@main + secrets: + DIGITAL_OCEAN_API_KEY: ${{ secrets.DIGITAL_OCEAN_API_KEY }} + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }} + with: + environment: 'staging' + path: './ops/helm/${{ github.event.repository.name }}' + tag: ${{ needs.semantic-release.outputs.version }} diff --git a/.releaserc.gitflow.json b/.releaserc.gitflow.json new file mode 100644 index 0000000..6aa6a0d --- /dev/null +++ b/.releaserc.gitflow.json @@ -0,0 +1,95 @@ +{ + "branches": [ + "main", + { + "name": "development", + "prerelease": "dev" + } + ], + "plugins": [ + "@semantic-release/commit-analyzer", + "@semantic-release/release-notes-generator", + [ + "@semantic-release/changelog", + { + "changelogFile": "CHANGELOG.md", + "changelogTitle": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." + } + ], + [ + "@semantic-release/exec", + { + "prepareCmd": "echo 'Preparing release ${nextRelease.version}'" + } + ], + [ + "@semantic-release/git", + { + "assets": [ + "CHANGELOG.md", + "package.json", + "package-lock.json", + "composer.json", + "composer.lock" + ], + "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" + } + ], + "@semantic-release/github" + ], + "preset": "conventionalcommits", + "releaseRules": [ + { + "type": "feat", + "release": "minor" + }, + { + "type": "fix", + "release": "patch" + }, + { + "type": "perf", + "release": "patch" + }, + { + "type": "revert", + "release": "patch" + }, + { + "type": "docs", + "release": false + }, + { + "type": "style", + "release": false + }, + { + "type": "chore", + "release": false + }, + { + "type": "refactor", + "release": "patch" + }, + { + "type": "test", + "release": false + }, + { + "type": "build", + "release": false + }, + { + "type": "ci", + "release": false + }, + { + "scope": "no-release", + "release": false + }, + { + "type": "breaking", + "release": "major" + } + ] +} \ No newline at end of file diff --git a/docs/gitflow-workflow.md b/docs/gitflow-workflow.md new file mode 100644 index 0000000..6e9bef1 --- /dev/null +++ b/docs/gitflow-workflow.md @@ -0,0 +1,194 @@ +# Gitflow Release Workflow + +This workflow provides a complete gitflow implementation for applications, replacing the traditional `on_source_change.yml` workflow with a more comprehensive branching strategy. + +## Features + +- **Multi-branch support**: Handles `main`, `development`, `feature/*`, `bugfix/*`, and `hotfix/*` branches +- **Automatic testing**: Runs static analysis and tests on all branches +- **Release automation**: Creates release PRs from development to main +- **Semantic versioning**: Uses semantic-release only on main branch after tests pass +- **Changelog generation**: Follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format +- **Conditional deployment**: Only deploys on main after successful release + +## Branch Strategy + +### Main Branch (`main`) +- **Triggers**: Push to main (typically from merged release PRs) +- **Actions**: + - Static analysis and tests + - Semantic release (creates version tags and changelog) + - Docker build and push + - Deployment to staging environment + +### Development Branch (`development` or `develop`) +- **Triggers**: Push to development (from merged feature branches) +- **Actions**: + - Static analysis and tests + - Automatic creation of release PR to main (if tests pass) + +### Feature Branches (`feature/*`, `bugfix/*`, `hotfix/*`) +- **Triggers**: Push to feature branches +- **Actions**: + - Static analysis and tests only + - No releases or deployments + +## Usage + +### 1. Replace your existing workflow + +Replace your existing `on_source_change.yml` or similar workflow with: + +```yaml +name: '[Application] Gitflow CI/CD' + +on: + push: + branches: + - 'main' + - 'development' + - 'develop' + - 'feature/**' + - 'bugfix/**' + - 'hotfix/**' + paths: + - 'ops/**' + - 'src/**' + - '.releaserc.json' + - '.github/workflows/**' + pull_request: + branches: + - 'main' + - 'development' + - 'develop' + paths: + - 'ops/**' + - 'src/**' + - '.releaserc.json' + - '.github/workflows/**' + +concurrency: + group: gitflow-${{ github.ref }} + cancel-in-progress: true + +jobs: + gitflow: + name: 'Gitflow CI/CD' + uses: webgrip/workflows/.github/workflows/gitflow-release.yml@main + secrets: + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }} + DIGITAL_OCEAN_API_KEY: ${{ secrets.DIGITAL_OCEAN_API_KEY }} + SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }} + with: + source-paths: 'src/**' + ops-paths: 'ops/**' + static-analysis-enabled: true + tests-enabled: true + deploy-enabled: true +``` + +### 2. Configure semantic-release for gitflow + +Create or update your `.releaserc.json` to use the gitflow configuration: + +```json +{ + "extends": "webgrip/workflows/.releaserc.gitflow.json" +} +``` + +Or copy the gitflow configuration from `webgrip/workflows/.releaserc.gitflow.json` and customize as needed. + +### 3. Set up your branches + +1. **Create a `development` branch** from main: + ```bash + git checkout main + git checkout -b development + git push -u origin development + ``` + +2. **Set branch protection rules** in GitHub: + - Protect `main`: Require PR reviews, require status checks + - Protect `development`: Require status checks (optional PR reviews) + +## Workflow Process + +### Development Flow + +1. **Create feature branch** from development: + ```bash + git checkout development + git checkout -b feature/my-new-feature + ``` + +2. **Develop and push** changes: + - Static analysis and tests run automatically + - Fix any issues before proceeding + +3. **Create PR** to development: + - Tests run on PR + - Merge when ready + +4. **Automatic release PR creation**: + - When development is updated, a release PR is automatically created to main + - PR includes changelog with all changes since last release + +5. **Review and merge** release PR: + - Review the generated changelog + - Merge to trigger semantic release and deployment + +### Configuration Options + +The workflow accepts several input parameters: + +- `source-paths`: Paths to monitor for source changes (default: `'src/**'`) +- `ops-paths`: Paths to monitor for ops changes (default: `'ops/**'`) +- `static-analysis-enabled`: Enable static analysis (default: `true`) +- `tests-enabled`: Enable tests (default: `true`) +- `deploy-enabled`: Enable deployment (default: `true`) + +### Required Secrets + +- `DOCKER_USERNAME`: Docker Hub username for image publishing +- `DOCKER_TOKEN`: Docker Hub token for authentication +- `DIGITAL_OCEAN_API_KEY`: DigitalOcean API key for deployments +- `SOPS_AGE_KEY`: SOPS age key for secret decryption + +## Migration from Existing Workflow + +If you're migrating from the existing `on_source_change.yml` workflow: + +1. **Backup** your existing workflow +2. **Replace** with the gitflow workflow above +3. **Create** development branch if it doesn't exist +4. **Update** branch protection rules +5. **Test** the workflow by pushing to development + +## Changelog Format + +The workflow uses [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format with automatic generation based on conventional commits: + +- `feat:` → **Added** section (minor version bump) +- `fix:` → **Fixed** section (patch version bump) +- `BREAKING CHANGE:` → **Changed** section (major version bump) +- `perf:` → **Fixed** section (patch version bump) +- `refactor:` → **Changed** section (patch version bump) + +## Troubleshooting + +### Release PR not created +- Ensure the development branch has new commits compared to main +- Check that tests are passing on development +- Verify GitHub token has proper permissions + +### Semantic release not running +- Ensure you're pushing to main branch +- Check that tests are passing +- Verify conventional commit format + +### Deployment not triggered +- Ensure semantic release created a new version +- Check that all required secrets are configured +- Verify Docker and Helm configurations are correct \ No newline at end of file From 60b1a76f68c1fdbb681a042a904807bf160095b9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 30 Aug 2025 22:18:37 +0000 Subject: [PATCH 3/5] fix: improve error handling and add comprehensive validation and migration docs Co-authored-by: Ryangr0 <3602480+Ryangr0@users.noreply.github.com> --- .../semantic-release/action.yml | 24 ++- .github/workflows/gitflow-release.yml | 7 +- docs/migration-guide.md | 183 ++++++++++++++++++ 3 files changed, 209 insertions(+), 5 deletions(-) create mode 100644 docs/migration-guide.md diff --git a/.github/composite-actions/semantic-release/action.yml b/.github/composite-actions/semantic-release/action.yml index 94e26c8..63abc2d 100644 --- a/.github/composite-actions/semantic-release/action.yml +++ b/.github/composite-actions/semantic-release/action.yml @@ -40,14 +40,30 @@ runs: # Check if custom config exists in the repo if [[ -f "./.releaserc.${{ inputs.release-type }}.json" ]]; then echo "release_config=--extends=./.releaserc.${{ inputs.release-type }}.json" >> $GITHUB_OUTPUT - # Fall back to webgrip/workflows configs - elif [[ -f "$(dirname "$0")/../../.releaserc.${{ inputs.release-type }}.json" ]]; then - echo "release_config=--extends=$(dirname "$0")/../../.releaserc.${{ inputs.release-type }}.json" >> $GITHUB_OUTPUT + echo "Using repo-specific config: ./.releaserc.${{ inputs.release-type }}.json" else - echo "release_config=" >> $GITHUB_OUTPUT + # For gitflow, try to download the config from webgrip/workflows + if [[ "${{ inputs.release-type }}" == "gitflow" ]]; then + echo "Downloading gitflow config from webgrip/workflows..." + curl -s -o ".releaserc.gitflow.json" \ + "https://raw.githubusercontent.com/webgrip/workflows/main/.releaserc.gitflow.json" || \ + echo "Failed to download gitflow config, using default" + + if [[ -f ".releaserc.gitflow.json" ]]; then + echo "release_config=--extends=./.releaserc.gitflow.json" >> $GITHUB_OUTPUT + echo "Using downloaded gitflow config" + else + echo "release_config=" >> $GITHUB_OUTPUT + echo "Using default semantic-release configuration" + fi + else + echo "release_config=" >> $GITHUB_OUTPUT + echo "Using default semantic-release configuration" + fi fi else echo "release_config=" >> $GITHUB_OUTPUT + echo "Using default semantic-release configuration" fi - name: Run semantic-release diff --git a/.github/workflows/gitflow-release.yml b/.github/workflows/gitflow-release.yml index dcc6dc2..6d8343b 100644 --- a/.github/workflows/gitflow-release.yml +++ b/.github/workflows/gitflow-release.yml @@ -152,8 +152,11 @@ jobs: id: changelog if: steps.check-pr.outputs.existing-pr == '' run: | + # Ensure we have the latest main branch + git fetch origin main:main || git fetch origin main + # Get commits between main and development - COMMITS=$(git log main..development --pretty=format:"- %s" --no-merges) + COMMITS=$(git log main..development --pretty=format:"- %s" --no-merges 2>/dev/null || echo "") if [[ -z "$COMMITS" ]]; then echo "No new commits to release" @@ -218,6 +221,8 @@ jobs: needs.branch-analysis.outputs.should-run-semantic-release == 'true' && needs.tests.result == 'success' uses: webgrip/workflows/.github/workflows/semantic-release.yml@main + with: + release-type: 'gitflow' # Determine changed secrets (only on main) determine-changed-secrets: diff --git a/docs/migration-guide.md b/docs/migration-guide.md new file mode 100644 index 0000000..0aabd58 --- /dev/null +++ b/docs/migration-guide.md @@ -0,0 +1,183 @@ +# Migration Guide: From on_source_change.yml to Gitflow + +This guide helps you migrate from the existing `on_source_change.yml` workflow to the new gitflow-esque release workflow. + +## Quick Migration Steps + +### 1. Backup your current workflow +```bash +# In your application repository +cp .github/workflows/on_source_change.yml .github/workflows/on_source_change.yml.backup +``` + +### 2. Replace with gitflow workflow +Replace the contents of your workflow file with: + +```yaml +name: '[Application] Gitflow CI/CD' + +on: + push: + branches: + - 'main' + - 'development' + - 'develop' + - 'feature/**' + - 'bugfix/**' + - 'hotfix/**' + paths: + - 'ops/**' + - 'src/**' + - '.releaserc.json' + - '.github/workflows/**' + pull_request: + branches: + - 'main' + - 'development' + - 'develop' + paths: + - 'ops/**' + - 'src/**' + - '.releaserc.json' + - '.github/workflows/**' + +concurrency: + group: gitflow-${{ github.ref }} + cancel-in-progress: true + +jobs: + gitflow: + name: 'Gitflow CI/CD' + uses: webgrip/workflows/.github/workflows/gitflow-release.yml@main + secrets: + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }} + DIGITAL_OCEAN_API_KEY: ${{ secrets.DIGITAL_OCEAN_API_KEY }} + SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }} + with: + source-paths: 'src/**' + ops-paths: 'ops/**' + static-analysis-enabled: true + tests-enabled: true + deploy-enabled: true +``` + +### 3. Update semantic-release configuration +Create or update `.releaserc.json` in your repository root: + +```json +{ + "extends": "webgrip/workflows/.releaserc.gitflow.json" +} +``` + +Or if you want to customize the configuration, copy the content from `webgrip/workflows/.releaserc.gitflow.json` and modify as needed. + +### 4. Create development branch +```bash +# Create development branch from main +git checkout main +git pull origin main +git checkout -b development +git push -u origin development +``` + +### 5. Update branch protection rules +In your GitHub repository settings: + +#### Main branch protection: +- ✅ Require pull request reviews before merging +- ✅ Require status checks to pass before merging +- ✅ Require branches to be up to date before merging +- ✅ Include administrators +- ✅ Restrict pushes that create files larger than 100MB + +#### Development branch protection: +- ✅ Require status checks to pass before merging +- ⚪ Require pull request reviews (optional) +- ✅ Require branches to be up to date before merging + +### 6. Test the workflow +```bash +# Create a test feature branch +git checkout development +git checkout -b feature/test-gitflow +echo "# Test" > test-gitflow.md +git add test-gitflow.md +git commit -m "feat: add test file for gitflow" +git push -u origin feature/test-gitflow + +# Create PR to development +# - Watch the static analysis and tests run +# - Merge the PR +# - Watch for automatic release PR creation from development to main +``` + +## Key Differences from old workflow + +### Before (on_source_change.yml): +- Only triggered on main branch +- Immediate semantic release on every push +- Immediate build and deploy + +### After (gitflow): +- Triggers on multiple branches (main, development, feature/*) +- Static analysis and tests run on all branches +- Semantic release only on main after tests pass +- Automatic release PR creation from development to main +- Build and deploy only after successful release + +## Configuration Options + +### Disable specific features: +```yaml +with: + static-analysis-enabled: false # Skip static analysis + tests-enabled: false # Skip tests + deploy-enabled: false # Skip deployment +``` + +### Custom paths: +```yaml +with: + source-paths: 'app/**,lib/**' # Monitor multiple source directories + ops-paths: 'deploy/**' # Custom ops directory +``` + +## Troubleshooting + +### "No changes to release" in release PR +- Ensure development branch has commits not in main +- Check that conventional commit format is used + +### Static analysis failures +- Fix code quality issues before merging to development +- Configure static analysis tools in your repository + +### Release PR not created +- Verify development branch exists and has new commits +- Check GitHub token permissions in Actions settings +- Ensure tests are passing on development + +### Semantic release not running +- Verify you're pushing to main branch +- Check that tests passed +- Ensure conventional commit format in commits + +## Rollback Plan + +If you need to rollback to the old workflow: +```bash +# Restore backup +cp .github/workflows/on_source_change.yml.backup .github/workflows/on_source_change.yml +git add .github/workflows/on_source_change.yml +git commit -m "rollback: restore original workflow" +git push +``` + +## Support + +For issues with the gitflow workflow: +1. Check the [gitflow documentation](./gitflow-workflow.md) +2. Validate your configuration with the validation script +3. Create an issue in the webgrip/workflows repository \ No newline at end of file From b946e33ecbb8a4f2990f020a24fea96cc31f3894 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 30 Aug 2025 22:46:11 +0000 Subject: [PATCH 4/5] feat: add example on_source_change.yml and on_release.yml for applications Co-authored-by: Ryangr0 <3602480+Ryangr0@users.noreply.github.com> --- examples/README.md | 134 ++++++++++++++++++++++++++++++++++ examples/on_release.yml | 36 +++++++++ examples/on_source_change.yml | 50 +++++++++++++ 3 files changed, 220 insertions(+) create mode 100644 examples/README.md create mode 100644 examples/on_release.yml create mode 100644 examples/on_source_change.yml diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..55aea75 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,134 @@ +# Application Workflow Examples + +This directory contains example workflow files that applications can copy into their repositories to use the gitflow-esque release workflow. + +## Files + +### `on_source_change.yml` +This workflow handles development activities and should be triggered by: +- Pushes to development/feature branches +- Pull requests to main/development branches + +**Actions performed:** +- Static analysis on all changes +- Automated testing +- Automatic creation of release PRs from development to main +- No deployment (development activities only) + +### `on_release.yml` +This workflow handles release activities and should be triggered by: +- Pushes to the main branch (typically from merged release PRs) + +**Actions performed:** +- Static analysis and testing (as final validation) +- Semantic versioning and changelog generation +- Docker image building and publishing +- Application deployment to staging environment + +## Usage + +### Option 1: Separate Source Change and Release Workflows + +Copy both files to your application repository's `.github/workflows/` directory: + +```bash +# In your application repository +cp examples/on_source_change.yml .github/workflows/ +cp examples/on_release.yml .github/workflows/ +``` + +This approach separates development activities from release activities, providing clear separation of concerns. + +### Option 2: Single Combined Workflow + +If you prefer a single workflow file, you can use the combined approach from `gitflow-application.yml`: + +```bash +# In your application repository +cp .github/workflows/gitflow-application.yml .github/workflows/ +``` + +## Configuration + +Both workflow examples can be customized by modifying the `with` parameters: + +```yaml +with: + source-paths: 'src/**' # Paths to monitor for source changes + ops-paths: 'ops/**' # Paths to monitor for ops changes + static-analysis-enabled: true # Enable/disable static analysis + tests-enabled: true # Enable/disable automated testing + deploy-enabled: true # Enable/disable deployment (release workflow only) +``` + +## Required Secrets + +Ensure your repository has the following secrets configured: + +- `DOCKER_USERNAME`: Docker Hub username for image publishing +- `DOCKER_TOKEN`: Docker Hub token for authentication +- `DIGITAL_OCEAN_API_KEY`: DigitalOcean API key for deployments +- `SOPS_AGE_KEY`: SOPS age key for secret decryption + +## Semantic Release Configuration + +Create or update `.releaserc.json` in your repository root: + +```json +{ + "extends": "webgrip/workflows/.releaserc.gitflow.json" +} +``` + +## Branch Setup + +1. **Create development branch** (if it doesn't exist): + ```bash + git checkout main + git checkout -b development + git push -u origin development + ``` + +2. **Set up branch protection rules** in GitHub: + - **Main branch**: Require PR reviews, require status checks + - **Development branch**: Require status checks (optional PR reviews) + +## Workflow Process + +1. **Create feature branch** from development: + ```bash + git checkout development + git checkout -b feature/my-feature + ``` + +2. **Develop and push** changes: + - `on_source_change.yml` runs static analysis and tests + - Fix any issues before proceeding + +3. **Create PR** to development and merge when ready + +4. **Automatic release PR**: + - When development is updated, a release PR is created to main + - Review the generated changelog and merge when ready + +5. **Release and deployment**: + - `on_release.yml` runs semantic release, build, and deployment + +## Migration from Existing Workflows + +If you're migrating from an existing `on_source_change.yml` workflow: + +1. **Backup your current workflow**: + ```bash + cp .github/workflows/on_source_change.yml .github/workflows/on_source_change.yml.backup + ``` + +2. **Replace with the new workflows**: + ```bash + cp examples/on_source_change.yml .github/workflows/ + cp examples/on_release.yml .github/workflows/ + ``` + +3. **Follow the branch setup and configuration steps above** + +For detailed migration instructions, see [migration-guide.md](../docs/migration-guide.md). \ No newline at end of file diff --git a/examples/on_release.yml b/examples/on_release.yml new file mode 100644 index 0000000..7da36cc --- /dev/null +++ b/examples/on_release.yml @@ -0,0 +1,36 @@ +name: 'Release CI/CD' + +# This workflow handles release activities: +# - Semantic versioning and changelog generation +# - Docker image building and publishing +# - Application deployment to staging + +on: + push: + branches: + - 'main' + paths: + - 'ops/**' + - 'src/**' + - '.releaserc.json' + - '.github/workflows/**' + +concurrency: + group: release-${{ github.ref }} + cancel-in-progress: true + +jobs: + release: + name: 'Release Pipeline' + uses: webgrip/workflows/.github/workflows/gitflow-release.yml@main + secrets: + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }} + DIGITAL_OCEAN_API_KEY: ${{ secrets.DIGITAL_OCEAN_API_KEY }} + SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }} + with: + source-paths: 'src/**' + ops-paths: 'ops/**' + static-analysis-enabled: true + tests-enabled: true + deploy-enabled: true # Enable deployment on releases \ No newline at end of file diff --git a/examples/on_source_change.yml b/examples/on_source_change.yml new file mode 100644 index 0000000..5a824ab --- /dev/null +++ b/examples/on_source_change.yml @@ -0,0 +1,50 @@ +name: 'Source Change CI/CD' + +# This workflow handles development activities: +# - Static analysis and testing on all source changes +# - Automatic release PR creation from development to main +# - Validation of pull requests + +on: + push: + branches: + - 'development' + - 'develop' + - 'feature/**' + - 'bugfix/**' + - 'hotfix/**' + paths: + - 'ops/**' + - 'src/**' + - '.releaserc.json' + - '.github/workflows/**' + pull_request: + branches: + - 'main' + - 'development' + - 'develop' + paths: + - 'ops/**' + - 'src/**' + - '.releaserc.json' + - '.github/workflows/**' + +concurrency: + group: source-change-${{ github.ref }} + cancel-in-progress: true + +jobs: + source-change: + name: 'Source Change Pipeline' + uses: webgrip/workflows/.github/workflows/gitflow-release.yml@main + secrets: + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }} + DIGITAL_OCEAN_API_KEY: ${{ secrets.DIGITAL_OCEAN_API_KEY }} + SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }} + with: + source-paths: 'src/**' + ops-paths: 'ops/**' + static-analysis-enabled: true + tests-enabled: true + deploy-enabled: false # No deployment on source changes \ No newline at end of file From 2e7c2c30b4a4004c931fc32b8d80aef99e547c90 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 30 Aug 2025 23:19:07 +0000 Subject: [PATCH 5/5] refactor: separate development and release workflows with direct component calls Co-authored-by: Ryangr0 <3602480+Ryangr0@users.noreply.github.com> --- examples/README.md | 52 ++++++++------ examples/on_release.yml | 89 ++++++++++++++++++++--- examples/on_source_change.yml | 130 ++++++++++++++++++++++++++++++---- 3 files changed, 226 insertions(+), 45 deletions(-) diff --git a/examples/README.md b/examples/README.md index 55aea75..6c8ac09 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,33 +1,41 @@ # Application Workflow Examples -This directory contains example workflow files that applications can copy into their repositories to use the gitflow-esque release workflow. +This directory contains example workflow files that applications can copy into their repositories to implement a clean gitflow-style development process with separated concerns. ## Files -### `on_source_change.yml` +### `on_source_change.yml` - Development CI/CD This workflow handles development activities and should be triggered by: -- Pushes to development/feature branches +- Pushes to development/feature/bugfix/hotfix branches - Pull requests to main/development branches **Actions performed:** -- Static analysis on all changes -- Automated testing +- Static analysis using `webgrip/workflows/.github/workflows/static-analysis.yml` +- Automated testing using `webgrip/workflows/.github/workflows/tests.yml` - Automatic creation of release PRs from development to main - No deployment (development activities only) -### `on_release.yml` +### `on_release.yml` - Release CI/CD This workflow handles release activities and should be triggered by: - Pushes to the main branch (typically from merged release PRs) **Actions performed:** -- Static analysis and testing (as final validation) -- Semantic versioning and changelog generation -- Docker image building and publishing -- Application deployment to staging environment +- Static analysis and testing (final validation) +- Semantic versioning and changelog generation using `webgrip/workflows/.github/workflows/semantic-release.yml` +- Docker image building and publishing using `webgrip/workflows/.github/workflows/docker-build-and-push.yml` +- Secrets deployment using `webgrip/workflows/.github/workflows/helm-charts-deploy.yml` +- Application deployment using `webgrip/workflows/.github/workflows/helm-chart-deploy.yml` + +## Key Benefits + +- **Separation of Concerns**: Development and release workflows are completely separate +- **Direct Workflow Calls**: Each workflow calls specific components instead of monolithic workflows +- **Clear Dependencies**: Easy to understand job dependencies and flow +- **Focused Execution**: Development workflows focus on validation, release workflows focus on deployment ## Usage -### Option 1: Separate Source Change and Release Workflows +### Recommended: Separate Development and Release Workflows Copy both files to your application repository's `.github/workflows/` directory: @@ -37,9 +45,13 @@ cp examples/on_source_change.yml .github/workflows/ cp examples/on_release.yml .github/workflows/ ``` -This approach separates development activities from release activities, providing clear separation of concerns. +This approach provides: +- **Clear separation** between development validation and release deployment +- **Focused workflows** that do exactly what they need to +- **Better visibility** into which stage is failing +- **Independent scaling** of development vs release processes -### Option 2: Single Combined Workflow +### Alternative: Single Combined Workflow If you prefer a single workflow file, you can use the combined approach from `gitflow-application.yml`: @@ -50,16 +62,12 @@ cp .github/workflows/gitflow-application.yml .github/workflows/ ## Configuration -Both workflow examples can be customized by modifying the `with` parameters: +The example workflows are ready to use as-is, but can be customized by modifying the workflow parameters directly in the files. Common customizations include: -```yaml -with: - source-paths: 'src/**' # Paths to monitor for source changes - ops-paths: 'ops/**' # Paths to monitor for ops changes - static-analysis-enabled: true # Enable/disable static analysis - tests-enabled: true # Enable/disable automated testing - deploy-enabled: true # Enable/disable deployment (release workflow only) -``` +- **Path filters**: Modify the `paths` sections to watch different directories +- **Branch names**: Adjust branch names in the `branches` sections +- **Environments**: Change deployment targets in `on_release.yml` +- **Docker settings**: Update Docker context, file paths, and tag strategies ## Required Secrets diff --git a/examples/on_release.yml b/examples/on_release.yml index 7da36cc..448ca0e 100644 --- a/examples/on_release.yml +++ b/examples/on_release.yml @@ -1,6 +1,7 @@ name: 'Release CI/CD' # This workflow handles release activities: +# - Static analysis and testing before release # - Semantic versioning and changelog generation # - Docker image building and publishing # - Application deployment to staging @@ -20,17 +21,89 @@ concurrency: cancel-in-progress: true jobs: - release: - name: 'Release Pipeline' - uses: webgrip/workflows/.github/workflows/gitflow-release.yml@main + # Run static analysis before release + static-analysis: + name: 'Static Analysis' + uses: webgrip/workflows/.github/workflows/static-analysis.yml@main + + # Run tests before release + tests: + name: 'Tests' + needs: [static-analysis] + if: | + always() && + (needs.static-analysis.result == 'success' || + needs.static-analysis.result == 'skipped') + uses: webgrip/workflows/.github/workflows/tests.yml@main + + # Create semantic release with version and changelog + semantic-release: + name: 'Semantic Release' + needs: [static-analysis, tests] + if: always() && needs.tests.result == 'success' + uses: webgrip/workflows/.github/workflows/semantic-release.yml@main + with: + release-type: 'gitflow' + + # Determine changed secrets for deployment + determine-changed-secrets: + name: "Determine Changed Secrets" + needs: [semantic-release] + if: | + always() && + (needs.semantic-release.result == 'success' || + needs.semantic-release.result == 'skipped') + uses: webgrip/workflows/.github/workflows/determine-changed-directories.yml@main + with: + inside-dir: 'ops/secrets' + max-level: 1 + + # Deploy changed secrets + deploy-changed-secrets: + name: "Deploy Changed Secrets" + needs: [determine-changed-secrets] + if: always() && needs.determine-changed-secrets.outputs.matrix != '[]' + uses: webgrip/workflows/.github/workflows/helm-charts-deploy.yml@main secrets: DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }} DIGITAL_OCEAN_API_KEY: ${{ secrets.DIGITAL_OCEAN_API_KEY }} SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }} with: - source-paths: 'src/**' - ops-paths: 'ops/**' - static-analysis-enabled: true - tests-enabled: true - deploy-enabled: true # Enable deployment on releases \ No newline at end of file + paths: ${{ needs.determine-changed-secrets.outputs.matrix }} + + # Build and push Docker image (only if semantic release created a version) + build: + name: 'Build and Push Docker Image' + needs: [semantic-release] + if: | + always() && + needs.semantic-release.result == 'success' && + needs.semantic-release.outputs.version != '' + uses: webgrip/workflows/.github/workflows/docker-build-and-push.yml@main + secrets: + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }} + with: + docker-context: '.' + docker-file: './ops/docker/application/Dockerfile' + docker-tags: | + ${{github.repository_owner}}/${{ github.event.repository.name }}:latest + ${{github.repository_owner}}/${{ github.event.repository.name }}:${{ needs.semantic-release.outputs.version }} + + # Deploy application to staging (only after successful build) + deploy-application: + name: 'Deploy Application' + needs: [semantic-release, build, deploy-changed-secrets] + if: | + always() && + needs.build.result == 'success' + uses: webgrip/workflows/.github/workflows/helm-chart-deploy.yml@main + secrets: + DIGITAL_OCEAN_API_KEY: ${{ secrets.DIGITAL_OCEAN_API_KEY }} + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }} + with: + environment: 'staging' + path: './ops/helm/${{ github.event.repository.name }}' + tag: ${{ needs.semantic-release.outputs.version }} diff --git a/examples/on_source_change.yml b/examples/on_source_change.yml index 5a824ab..0453f46 100644 --- a/examples/on_source_change.yml +++ b/examples/on_source_change.yml @@ -1,4 +1,4 @@ -name: 'Source Change CI/CD' +name: 'Development CI/CD' # This workflow handles development activities: # - Static analysis and testing on all source changes @@ -34,17 +34,117 @@ concurrency: cancel-in-progress: true jobs: - source-change: - name: 'Source Change Pipeline' - uses: webgrip/workflows/.github/workflows/gitflow-release.yml@main - secrets: - DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} - DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }} - DIGITAL_OCEAN_API_KEY: ${{ secrets.DIGITAL_OCEAN_API_KEY }} - SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }} - with: - source-paths: 'src/**' - ops-paths: 'ops/**' - static-analysis-enabled: true - tests-enabled: true - deploy-enabled: false # No deployment on source changes \ No newline at end of file + # Run static analysis on all source changes + static-analysis: + name: 'Static Analysis' + uses: webgrip/workflows/.github/workflows/static-analysis.yml@main + + # Run tests on all source changes + tests: + name: 'Tests' + needs: [static-analysis] + if: always() && (needs.static-analysis.result == 'success' || needs.static-analysis.result == 'skipped') + uses: webgrip/workflows/.github/workflows/tests.yml@main + + # Create release PR from development to main (only on development branch pushes) + create-release-pr: + name: 'Create Release PR' + needs: [static-analysis, tests] + if: | + always() && + github.event_name == 'push' && + (github.ref_name == 'development' || github.ref_name == 'develop') && + needs.tests.result == 'success' + runs-on: arc-runner-set + permissions: + contents: read + pull-requests: write + steps: + - name: Checkout development + uses: actions/checkout@v4 + with: + ref: ${{ github.ref_name }} + fetch-depth: 0 + + - name: Check if release PR already exists + id: check-pr + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Check if there's already an open PR from development to main + EXISTING_PR=$(gh pr list --base main --head ${{ github.ref_name }} --state open --json number --jq '.[0].number') + if [[ "$EXISTING_PR" != "null" && -n "$EXISTING_PR" ]]; then + echo "existing-pr=$EXISTING_PR" >> $GITHUB_OUTPUT + echo "PR already exists: #$EXISTING_PR" + else + echo "existing-pr=" >> $GITHUB_OUTPUT + echo "No existing PR found" + fi + + - name: Generate changelog for release + id: changelog + if: steps.check-pr.outputs.existing-pr == '' + run: | + # Ensure we have the latest main branch + git fetch origin main:main || git fetch origin main + + # Get commits between main and development + COMMITS=$(git log main..${{ github.ref_name }} --pretty=format:"- %s" --no-merges 2>/dev/null || echo "") + + if [[ -z "$COMMITS" ]]; then + echo "No new commits to release" + echo "has-changes=false" >> $GITHUB_OUTPUT + exit 0 + fi + + echo "has-changes=true" >> $GITHUB_OUTPUT + + # Generate changelog content + CHANGELOG="## Changes in this release + + The following changes will be included in the next release: + + $COMMITS + + ## Release Notes + + This release includes the latest changes from the ${{ github.ref_name }} branch. + Please review the changes above before merging. + + --- + *This PR was automatically created by the Development CI/CD workflow.*" + + # Save changelog to file for use in PR + echo "$CHANGELOG" > /tmp/release-notes.md + echo "Generated changelog with $(echo "$COMMITS" | wc -l) commits" + + - name: Create release PR + if: | + steps.check-pr.outputs.existing-pr == '' && + steps.changelog.outputs.has-changes == 'true' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Create PR from development to main + gh pr create \ + --title "🚀 Release: ${{ github.ref_name }} → main" \ + --body-file /tmp/release-notes.md \ + --base main \ + --head ${{ github.ref_name }} \ + --label "release" \ + --label "automated" + + echo "Created release PR from ${{ github.ref_name }} to main" + + - name: Update existing release PR + if: | + steps.check-pr.outputs.existing-pr != '' && + steps.changelog.outputs.has-changes == 'true' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Update the existing PR with new changelog + gh pr edit ${{ steps.check-pr.outputs.existing-pr }} \ + --body-file /tmp/release-notes.md + + echo "Updated existing release PR #${{ steps.check-pr.outputs.existing-pr }}" \ No newline at end of file