diff --git a/.github/actions/ci/bicep-standard-ci/action.yml b/.github/actions/ci/bicep-standard-ci/action.yml new file mode 100644 index 00000000..62352157 --- /dev/null +++ b/.github/actions/ci/bicep-standard-ci/action.yml @@ -0,0 +1,71 @@ +name: Bicep Standard CI +description: | + This action builds Bicep templates and uploads the artifacts. + +inputs: + branch_name: + description: 'The name of the branch being built' + required: true + new_version: + description: 'The version being built' + required: true + should_publish: + description: 'Whether the build should be published' + required: true + +runs: + using: composite + + steps: + - name: Update Packages + shell: bash + run: | + echo "✅ Updating packages..." + # Simulate package update + sudo apt-get update + echo "✅ Packages updated successfully" + + - name: Build Accelerator Bicep + shell: bash + run: | + echo "✅ Building Bicep templates..." + mkdir -p ./artifacts + + # Check if Azure CLI is available + if command -v az &> /dev/null; then + az bicep build --file ./infra/main.bicep --outdir ./artifacts + echo "✅ Bicep build completed" + else + echo "⚠️ Azure CLI not available, creating placeholder artifacts" + echo "Bicep build would be executed here" > ./artifacts/placeholder.txt + fi + + - name: Upload Artifacts + uses: actions/upload-artifact@v4 + with: + name: artifacts-${{ steps.calculate_next_version.outputs.new_version }} + path: ./artifacts + compression-level: 6 + overwrite: true + if-no-files-found: warn + retention-days: 7 + + - name: Artifact Summary + shell: bash + run: | + branch_name="${{ inputs.branch_name }}" + version="${{ inputs.new_version }}" + should_publish="${{ inputs.should_publish }}" + + echo "📦 Artifacts Summary:" + echo " - Version: $version" + echo " - Branch: $branch_name" + echo " - Tag created: ✅" + echo " - Artifacts uploaded: ✅" + echo " - GitHub release will be published: $should_publish" + + if [[ "$should_publish" == "false" ]]; then + echo "" + echo "ℹ️ This is a development build from a non-main branch." + echo " Tag and artifacts are created for tracking, but no GitHub release is published." + fi \ No newline at end of file diff --git a/.github/actions/ci/generate-release/action.yml b/.github/actions/ci/generate-release/action.yml new file mode 100644 index 00000000..15a6c184 --- /dev/null +++ b/.github/actions/ci/generate-release/action.yml @@ -0,0 +1,339 @@ +name: Generate Release +description: Generate a release based on the build outputs + +outputs: + new_version: + description: 'The new version generated for the release' + value: ${{ steps.calculate_next_version.outputs.new_version }} + release_type: + description: 'The type of release (major, minor, patch, etc.)' + value: ${{ steps.determine_release_type.outputs.release_type }} + previous_tag: + description: 'The last tag before this release' + value: ${{ steps.get_tag.outputs.tag }} + should_release: + description: 'Whether a release should be created' + value: ${{ steps.determine_release_type.outputs.should_release }} + should_publish: + description: 'Whether the release should be published on GitHub' + value: ${{ steps.determine_release_type.outputs.should_publish }} + branch_name: + description: 'The name of the branch being built' + value: ${{ steps.branch_info.outputs.branch_name }} + +runs: + using: composite + steps: + - name: Setup Git identity + shell: bash + run: | + git config user.name "github-actions" + git config user.email "github-actions@github.com" + + - name: Debug trigger information + shell: bash + run: | + echo "🐛 Debug Information:" + echo "Event name: ${{ github.event_name }}" + echo "Ref: ${{ github.ref }}" + echo "Head ref: ${{ github.head_ref }}" + echo "Base ref: ${{ github.base_ref }}" + echo "Repository: ${{ github.repository }}" + echo "Actor: ${{ github.actor }}" + echo "SHA: ${{ github.sha }}" + + - name: Get branch information + shell: bash + id: branch_info + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + branch_name="${{ github.ref_name }}" + elif [ "${{ github.event_name }}" = "pull_request" ]; then + branch_name="${{ github.head_ref }}" + else + branch_name="${{ github.ref_name }}" + fi + + echo "branch_name=$branch_name" >> $GITHUB_OUTPUT + echo "✅ Current branch: $branch_name" + + - name: Get latest tag + shell: bash + id: get_tag + run: | + # Fetch all tags + git fetch --tags --force + # Get the latest tag matching v.. optionally with a suffix + tag=$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*'* | sort -V | tail -n 1) + if [ -z "$tag" ]; then + tag="v0.0.0" + fi + + # If tag matches v..-, extract only v.. + base_tag=$(echo "$tag" | sed -E 's/^(v[0-9]+\.[0-9]+\.[0-9]+).*$/\1/') + + echo "tag=$tag" >> $GITHUB_OUTPUT + echo "base_tag=$base_tag" >> $GITHUB_OUTPUT + echo "Previous tag: $base_tag" + echo "✅ Latest tag: $tag" + + - name: Determine release type and strategy + shell: bash + id: determine_release_type + run: | + branch_name="${{ steps.branch_info.outputs.branch_name }}" + should_release="true" + should_publish="true" + + echo "🔍 Analyzing branch: $branch_name" + + # For PR events, only create pre-releases + if [ "${{ github.event_name }}" = "pull_request" ]; then + echo "📋 Pull Request detected - Creating pre-release" + should_publish="false" + fi + + if [[ "$branch_name" == "main" ]]; then + # Main branch: conditional major increment with new rule + release_type="main" + should_publish="true" + echo "✅ Main branch detected - Conditional major release strategy with new rule" + + elif [[ "$branch_name" == feature/* ]]; then + # Feature branch: patch increment with overflow logic + release_type="feature" + should_publish="false" + echo "✅ Feature branch detected - Patch increment with overflow strategy (no release publication)" + + elif [[ "$branch_name" == fix/* ]]; then + # Fix branch: minor increment with overflow logic + release_type="fix" + should_publish="false" + echo "✅ Fix branch detected - Minor increment with overflow strategy (no release publication)" + + else + echo "⚠️ Unsupported branch pattern: $branch_name" + echo "Only main, feature/*, and fix/* branches are supported for releases" + should_release="false" + should_publish="false" + release_type="none" + fi + + echo "release_type=$release_type" >> $GITHUB_OUTPUT + echo "should_release=$should_release" >> $GITHUB_OUTPUT + echo "should_publish=$should_publish" >> $GITHUB_OUTPUT + + echo "📋 Release Summary:" + echo " - Will create tag and version: $should_release" + echo " - Will publish GitHub release: $should_publish" + + - name: Count commits since last tag + shell: bash + id: count_commits + if: steps.determine_release_type.outputs.should_release == 'true' + run: | + last_tag="${{ steps.get_tag.outputs.tag }}" + branch_name="${{ steps.branch_info.outputs.branch_name }}" + + echo "🔍 Counting commits for branch: $branch_name" + echo "🏷️ Last tag: $last_tag" + + if [ "$last_tag" = "v0.0.0" ]; then + # No previous tags, count all commits + if [[ "$branch_name" == "main" ]]; then + commit_count=$(git rev-list --count HEAD) + else + # For feature/fix branches, count commits in this branch only + merge_base=$(git merge-base HEAD origin/main 2>/dev/null || git merge-base HEAD main 2>/dev/null || echo "") + if [ -n "$merge_base" ]; then + commit_count=$(git rev-list --count ${merge_base}..HEAD) + else + commit_count=$(git rev-list --count HEAD) + fi + fi + else + # Count commits since last tag + if [[ "$branch_name" == "main" ]]; then + commit_count=$(git rev-list --count ${last_tag}..HEAD) + else + # For feature/fix branches, count commits since branch diverged from main + merge_base=$(git merge-base HEAD origin/main 2>/dev/null || git merge-base HEAD main 2>/dev/null || echo "") + if [ -n "$merge_base" ]; then + commit_count=$(git rev-list --count ${merge_base}..HEAD) + else + commit_count=$(git rev-list --count ${last_tag}..HEAD) + fi + fi + fi + + # Ensure minimum commit count of 1 + if [ "$commit_count" -eq 0 ]; then + commit_count=1 + fi + + echo "commit_count=$commit_count" >> $GITHUB_OUTPUT + echo "✅ Commits to include: $commit_count" + + - name: Calculate next version + shell: bash + id: calculate_next_version + if: steps.determine_release_type.outputs.should_release == 'true' + run: | + current_version="${{ steps.get_tag.outputs.base_tag }}" + release_type="${{ steps.determine_release_type.outputs.release_type }}" + commit_count="${{ steps.count_commits.outputs.commit_count }}" + branch_name="${{ steps.branch_info.outputs.branch_name }}" + + # Remove 'v' prefix if present + current_version=${current_version#v} + IFS='.' read -r major minor patch <<< "$current_version" + # Fallback for missing minor/patch + if [ -z "$minor" ]; then minor=0; fi + if [ -z "$patch" ]; then patch=0; fi + + echo "📊 Current version: v$major.$minor.$patch" + echo "📊 Release type: $release_type" + echo "📊 Commit count: $commit_count" + + case "$release_type" in + main) + # NEW RULE: Main branch conditional major increment + # Only increase major if minor and patch are both 0 + # Otherwise, keep major and update minor/patch accordingly + + echo "🎯 Main branch: Applying new conditional major increment rule" + echo "📋 Current state: major=$major, minor=$minor, patch=$patch" + + if [ "$minor" -eq 0 ] && [ "$patch" -eq 0 ]; then + # Rule condition met: minor=0 AND patch=0 -> increment major + major=$((major + 1)) + minor=0 + patch=0 + echo "✅ Rule condition met (minor=0 AND patch=0): Incrementing major to $major" + else + # Rule condition NOT met: keep major, update minor/patch + echo "⚠️ Rule condition NOT met (minor≠0 OR patch≠0): Keeping major=$major" + + # Increment patch first, then handle overflow + new_patch=$((patch + 1)) + + if [ $new_patch -gt 99 ]; then + # Patch overflow: reset patch to 0 and increment minor + patch=0 + minor=$((minor + 1)) + + # Check if minor also overflows + if [ $minor -gt 99 ]; then + # Minor overflow: reset minor to 0 and increment major + minor=0 + major=$((major + 1)) + echo "⚠️ Minor overflow detected: Incrementing major to $major, resetting minor to 0" + fi + + echo "⚠️ Patch overflow detected: Incrementing minor to $minor, resetting patch to 0" + else + patch=$new_patch + echo "✅ Incrementing patch to $patch" + fi + fi + + version_suffix="" + echo "🎯 Main branch result: major=$major, minor=$minor, patch=$patch" + ;; + feature) + # Feature branch: patch increment with overflow logic + # patch: if previous patch + commits > 99, then patch = 0 and minor += 1 + new_patch=$((patch + commit_count)) + + echo "🔢 Calculating patch overflow: $patch + $commit_count = $new_patch" + + if [ $new_patch -gt 99 ]; then + # Overflow: reset patch to 0 and increment minor + patch=0 + minor=$((minor + 1)) + + # Check if minor also overflows + if [ $minor -gt 99 ]; then + minor=0 + major=$((major + 1)) + echo "⚠️ Minor overflow detected: Incrementing major to $major, resetting minor to 0" + fi + + echo "⚠️ Patch overflow detected: Incrementing minor to $minor, resetting patch to 0" + else + patch=$new_patch + echo "✅ No overflow: Setting patch to $patch" + fi + + # Clean branch name for version suffix + clean_branch_name=$(echo "${branch_name#feature/}" | sed 's/[^a-zA-Z0-9]/-/g') + version_suffix="-feature.$clean_branch_name" + ;; + fix) + # Fix branch: minor increment with overflow logic + # minor: if previous minor + commits > 99, then minor = 0 and major += 1 + new_minor=$((minor + commit_count)) + + echo "🔢 Calculating minor overflow: $minor + $commit_count = $new_minor" + + if [ $new_minor -gt 99 ]; then + # Overflow: reset minor to 0 and increment major + minor=0 + major=$((major + 1)) + echo "⚠️ Minor overflow detected: Incrementing major to $major, resetting minor to 0" + else + minor=$new_minor + echo "✅ No overflow: Setting minor to $minor" + fi + + # Clean branch name for version suffix + clean_branch_name=$(echo "${branch_name#fix/}" | sed 's/[^a-zA-Z0-9]/-/g') + version_suffix="-fix.$clean_branch_name" + ;; + *) + echo "❌ Invalid release type: $release_type" + exit 1 + ;; + esac + + # Add PR suffix if this is a pull request + if [ "${{ github.event_name }}" = "pull_request" ]; then + version_suffix="${version_suffix}-pr${{ github.event.number }}" + fi + + new_version="v$major.$minor.$patch$version_suffix" + echo "✅ Next version: $new_version" + echo "📊 Final version breakdown: major=$major, minor=$minor, patch=$patch" + echo "new_version=$new_version" >> $GITHUB_OUTPUT + + - name: Create and push tag + shell: bash + run: | + tag_name="${{ steps.calculate_next_version.outputs.new_version }}" + branch_name="${{ steps.branch_info.outputs.branch_name }}" + should_publish="${{ steps.determine_release_type.outputs.should_publish }}" + + echo "🏷️ Preparing to create tag: $tag_name" + echo "🔀 For branch: $branch_name" + echo "📋 Will publish release: $should_publish" + + # Fetch latest tags first to check for existing tags + git fetch --tags --force + + # Check if tag already exists before creating it + if git rev-parse "$tag_name" >/dev/null 2>&1; then + echo "⚠️ Tag $tag_name already exists locally or remotely. Skipping tag creation and push." + else + echo "✅ Tag $tag_name does not exist. Creating new tag..." + + # Create annotated tag + if [[ "$branch_name" == "main" ]]; then + git tag -a "$tag_name" -m "Release $tag_name from main branch" + else + git tag -a "$tag_name" -m "Development tag $tag_name from $branch_name branch (no release)" + fi + + # Push the newly created tag + git push origin "$tag_name" + echo "✅ Tag $tag_name created and pushed successfully" + fi \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..b0e333ce --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,50 @@ +name: Continuous Integration + +on: + push: + branches: + - main + - 'feature/**' + - 'fix/**' + +permissions: + contents: write + pull-requests: read # Added for PR triggers + +jobs: + generate-release: + runs-on: ubuntu-latest + outputs: + new_version: ${{ steps.release.outputs.new_version }} + release_type: ${{ steps.release.outputs.release_type }} + previous_tag: ${{ steps.release.outputs.previous_tag }} + should_release: ${{ steps.release.outputs.should_release }} + should_publish: ${{ steps.release.outputs.should_publish }} + branch_name: ${{ steps.release.outputs.branch_name }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.event_name == 'pull_request' && github.head_ref || github.ref }} + + - name: Generate Release + id: release + uses: ./.github/actions/ci/generate-release + + build: + runs-on: ubuntu-latest + needs: generate-release + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.event_name == 'pull_request' && github.head_ref || github.ref }} + + - name: Build Bicep Code + id: build + uses: ./.github/actions/ci/bicep-standard-ci + with: + branch_name: ${{ needs.generate-release.outputs.branch_name }} + new_version: ${{ needs.generate-release.outputs.new_version }} + should_publish: ${{ needs.generate-release.outputs.should_publish }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8daa01b6..42080a40 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,11 +1,11 @@ name: Release on: - push: - branches: - - main - - 'feature/**' - - 'fix/**' + # push: + # branches: + # - main + # - 'feature/**' + # - 'fix/**' workflow_dispatch: inputs: release_type: @@ -26,450 +26,62 @@ permissions: pull-requests: read # Added for PR triggers jobs: - build: + generate-release: runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - ref: ${{ github.event_name == 'pull_request' && github.head_ref || github.ref }} - - - name: Build Accelerator Bicep - run: | - echo "✅ Building Bicep templates..." - mkdir -p ./artifacts - - # Check if Azure CLI is available - if command -v az &> /dev/null; then - az bicep build --file ./infra/main.bicep --outdir ./artifacts - echo "✅ Bicep build completed" - else - echo "⚠️ Azure CLI not available, creating placeholder artifacts" - echo "Bicep build would be executed here" > ./artifacts/placeholder.txt - fi - - generate_release: - runs-on: ubuntu-latest - needs: build outputs: - new_version: ${{ steps.next_version.outputs.new_version }} - release_type: ${{ steps.determine_release_type.outputs.release_type }} - previous_tag: ${{ steps.get_tag.outputs.tag }} - should_release: ${{ steps.determine_release_type.outputs.should_release }} - should_publish: ${{ steps.determine_release_type.outputs.should_publish }} - branch_name: ${{ steps.branch_info.outputs.branch_name }} + new_version: ${{ steps.release.outputs.new_version }} + release_type: ${{ steps.release.outputs.release_type }} + previous_tag: ${{ steps.release.outputs.previous_tag }} + should_release: ${{ steps.release.outputs.should_release }} + should_publish: ${{ steps.release.outputs.should_publish }} + branch_name: ${{ steps.release.outputs.branch_name }} steps: - name: Checkout code uses: actions/checkout@v4 with: - fetch-depth: 0 - # Ensure we get the correct branch for PRs ref: ${{ github.event_name == 'pull_request' && github.head_ref || github.ref }} - - name: Setup Git identity - run: | - git config user.name "github-actions" - git config user.email "github-actions@github.com" - - - name: Debug trigger information - run: | - echo "🐛 Debug Information:" - echo "Event name: ${{ github.event_name }}" - echo "Ref: ${{ github.ref }}" - echo "Head ref: ${{ github.head_ref }}" - echo "Base ref: ${{ github.base_ref }}" - echo "Repository: ${{ github.repository }}" - echo "Actor: ${{ github.actor }}" - echo "SHA: ${{ github.sha }}" - - - name: Get branch information - id: branch_info - run: | - if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - branch_name="${{ github.ref_name }}" - elif [ "${{ github.event_name }}" = "pull_request" ]; then - branch_name="${{ github.head_ref }}" - else - branch_name="${{ github.ref_name }}" - fi - - echo "branch_name=$branch_name" >> $GITHUB_OUTPUT - echo "✅ Current branch: $branch_name" - - - name: Get latest tag - id: get_tag - run: | - # Fetch all tags - git fetch --tags --force - tag=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0") - echo "tag=$tag" >> $GITHUB_OUTPUT - echo "✅ Latest tag: $tag" - - - name: Determine release type and strategy - id: determine_release_type - run: | - branch_name="${{ steps.branch_info.outputs.branch_name }}" - should_release="true" - should_publish="true" - - echo "🔍 Analyzing branch: $branch_name" - - # For PR events, only create pre-releases - if [ "${{ github.event_name }}" = "pull_request" ]; then - echo "📋 Pull Request detected - Creating pre-release" - should_publish="false" - fi - - if [[ "$branch_name" == "main" ]]; then - # Main branch: conditional major increment with new rule - release_type="main" - should_publish="true" - echo "✅ Main branch detected - Conditional major release strategy with new rule" - - elif [[ "$branch_name" == feature/* ]]; then - # Feature branch: patch increment with overflow logic - release_type="feature" - should_publish="false" - echo "✅ Feature branch detected - Patch increment with overflow strategy (no release publication)" - - elif [[ "$branch_name" == fix/* ]]; then - # Fix branch: minor increment with overflow logic - release_type="fix" - should_publish="false" - echo "✅ Fix branch detected - Minor increment with overflow strategy (no release publication)" - - else - echo "⚠️ Unsupported branch pattern: $branch_name" - echo "Only main, feature/*, and fix/* branches are supported for releases" - should_release="false" - should_publish="false" - release_type="none" - fi - - echo "release_type=$release_type" >> $GITHUB_OUTPUT - echo "should_release=$should_release" >> $GITHUB_OUTPUT - echo "should_publish=$should_publish" >> $GITHUB_OUTPUT - - echo "📋 Release Summary:" - echo " - Will create tag and version: $should_release" - echo " - Will publish GitHub release: $should_publish" + - name: Generate Release + id: release + uses: ./.github/actions/ci/generate-release - - name: Count commits since last tag - id: count_commits - if: steps.determine_release_type.outputs.should_release == 'true' - run: | - last_tag="${{ steps.get_tag.outputs.tag }}" - branch_name="${{ steps.branch_info.outputs.branch_name }}" - - echo "🔍 Counting commits for branch: $branch_name" - echo "🏷️ Last tag: $last_tag" - - if [ "$last_tag" = "v0.0.0" ]; then - # No previous tags, count all commits - if [[ "$branch_name" == "main" ]]; then - commit_count=$(git rev-list --count HEAD) - else - # For feature/fix branches, count commits in this branch only - merge_base=$(git merge-base HEAD origin/main 2>/dev/null || git merge-base HEAD main 2>/dev/null || echo "") - if [ -n "$merge_base" ]; then - commit_count=$(git rev-list --count ${merge_base}..HEAD) - else - commit_count=$(git rev-list --count HEAD) - fi - fi - else - # Count commits since last tag - if [[ "$branch_name" == "main" ]]; then - commit_count=$(git rev-list --count ${last_tag}..HEAD) - else - # For feature/fix branches, count commits since branch diverged from main - merge_base=$(git merge-base HEAD origin/main 2>/dev/null || git merge-base HEAD main 2>/dev/null || echo "") - if [ -n "$merge_base" ]; then - commit_count=$(git rev-list --count ${merge_base}..HEAD) - else - commit_count=$(git rev-list --count ${last_tag}..HEAD) - fi - fi - fi - - # Ensure minimum commit count of 1 - if [ "$commit_count" -eq 0 ]; then - commit_count=1 - fi - - echo "commit_count=$commit_count" >> $GITHUB_OUTPUT - echo "✅ Commits to include: $commit_count" - - - name: Calculate next version - id: next_version - if: steps.determine_release_type.outputs.should_release == 'true' - run: | - current_version="${{ steps.get_tag.outputs.tag }}" - release_type="${{ steps.determine_release_type.outputs.release_type }}" - commit_count="${{ steps.count_commits.outputs.commit_count }}" - branch_name="${{ steps.branch_info.outputs.branch_name }}" - - # Remove 'v' prefix if present - current_version=${current_version#v} - IFS='.' read -r major minor patch <<< "$current_version" - - echo "📊 Current version: v$major.$minor.$patch" - echo "📊 Release type: $release_type" - echo "📊 Commit count: $commit_count" - - case "$release_type" in - main) - # NEW RULE: Main branch conditional major increment - # Only increase major if minor and patch are both 0 - # Otherwise, keep major and update minor/patch accordingly - - echo "🎯 Main branch: Applying new conditional major increment rule" - echo "📋 Current state: major=$major, minor=$minor, patch=$patch" - - if [ "$minor" -eq 0 ] && [ "$patch" -eq 0 ]; then - # Rule condition met: minor=0 AND patch=0 -> increment major - major=$((major + 1)) - minor=0 - patch=0 - echo "✅ Rule condition met (minor=0 AND patch=0): Incrementing major to $major" - else - # Rule condition NOT met: keep major, update minor/patch - echo "⚠️ Rule condition NOT met (minor≠0 OR patch≠0): Keeping major=$major" - - # Increment patch first, then handle overflow - new_patch=$((patch + 1)) - - if [ $new_patch -gt 99 ]; then - # Patch overflow: reset patch to 0 and increment minor - patch=0 - minor=$((minor + 1)) - - # Check if minor also overflows - if [ $minor -gt 99 ]; then - # Minor overflow: reset minor to 0 and increment major - minor=0 - major=$((major + 1)) - echo "⚠️ Minor overflow detected: Incrementing major to $major, resetting minor to 0" - fi - - echo "⚠️ Patch overflow detected: Incrementing minor to $minor, resetting patch to 0" - else - patch=$new_patch - echo "✅ Incrementing patch to $patch" - fi - fi - - version_suffix="" - echo "🎯 Main branch result: major=$major, minor=$minor, patch=$patch" - ;; - feature) - # Feature branch: patch increment with overflow logic - # patch: if previous patch + commits > 99, then patch = 0 and minor += 1 - new_patch=$((patch + commit_count)) - - echo "🔢 Calculating patch overflow: $patch + $commit_count = $new_patch" - - if [ $new_patch -gt 99 ]; then - # Overflow: reset patch to 0 and increment minor - patch=0 - minor=$((minor + 1)) - - # Check if minor also overflows - if [ $minor -gt 99 ]; then - minor=0 - major=$((major + 1)) - echo "⚠️ Minor overflow detected: Incrementing major to $major, resetting minor to 0" - fi - - echo "⚠️ Patch overflow detected: Incrementing minor to $minor, resetting patch to 0" - else - patch=$new_patch - echo "✅ No overflow: Setting patch to $patch" - fi - - # Clean branch name for version suffix - clean_branch_name=$(echo "${branch_name#feature/}" | sed 's/[^a-zA-Z0-9]/-/g') - version_suffix="-feature.$clean_branch_name" - ;; - fix) - # Fix branch: minor increment with overflow logic - # minor: if previous minor + commits > 99, then minor = 0 and major += 1 - new_minor=$((minor + commit_count)) - - echo "🔢 Calculating minor overflow: $minor + $commit_count = $new_minor" - - if [ $new_minor -gt 99 ]; then - # Overflow: reset minor to 0 and increment major - minor=0 - major=$((major + 1)) - echo "⚠️ Minor overflow detected: Incrementing major to $major, resetting minor to 0" - else - minor=$new_minor - echo "✅ No overflow: Setting minor to $minor" - fi - - # Clean branch name for version suffix - clean_branch_name=$(echo "${branch_name#fix/}" | sed 's/[^a-zA-Z0-9]/-/g') - version_suffix="-fix.$clean_branch_name" - ;; - *) - echo "❌ Invalid release type: $release_type" - exit 1 - ;; - esac - - # Add PR suffix if this is a pull request - if [ "${{ github.event_name }}" = "pull_request" ]; then - version_suffix="${version_suffix}-pr${{ github.event.number }}" - fi - - new_version="v$major.$minor.$patch$version_suffix" - echo "✅ Next version: $new_version" - echo "📊 Final version breakdown: major=$major, minor=$minor, patch=$patch" - echo "new_version=$new_version" >> $GITHUB_OUTPUT - - create_tag: + build: runs-on: ubuntu-latest - needs: generate_release - if: needs.generate_release.outputs.should_release == 'true' + needs: generate-release steps: - name: Checkout code uses: actions/checkout@v4 with: - fetch-depth: 0 ref: ${{ github.event_name == 'pull_request' && github.head_ref || github.ref }} - token: ${{ secrets.GITHUB_TOKEN }} - - name: Setup Git identity - run: | - git config user.name "github-actions" - git config user.email "github-actions@github.com" + - name: Build Bicep Code + id: build + uses: ./.github/actions/ci/bicep-standard-ci + with: + branch_name: ${{ needs.generate-release.outputs.branch_name }} + new_version: ${{ needs.generate-release.outputs.new_version }} + should_publish: ${{ needs.generate-release.outputs.should_publish }} - - name: Create and push tag - run: | - tag_name="${{ needs.generate_release.outputs.new_version }}" - branch_name="${{ needs.generate_release.outputs.branch_name }}" - should_publish="${{ needs.generate_release.outputs.should_publish }}" - - echo "🏷️ Creating tag: $tag_name" - echo "🔀 For branch: $branch_name" - echo "📋 Will publish release: $should_publish" - - # Create annotated tag - if [[ "$branch_name" == "main" ]]; then - git tag -a "$tag_name" -m "Release $tag_name from main branch" - else - git tag -a "$tag_name" -m "Development tag $tag_name from $branch_name branch (no release)" - fi - - # Push the tag - git push origin "$tag_name" - echo "✅ Tag $tag_name created and pushed successfully" - - upload_artifacts: + publish-release: runs-on: ubuntu-latest - needs: [generate_release, create_tag] - if: needs.generate_release.outputs.should_release == 'true' + needs: [generate-release, build] + if: needs.generate-release.outputs.should_release == 'true' steps: - - name: Checkout code uses: actions/checkout@v4 with: ref: ${{ github.event_name == 'pull_request' && github.head_ref || github.ref }} - - name: Build Accelerator Bicep - run: | - echo "✅ Building Bicep templates..." - mkdir -p ./artifacts - - # Check if Azure CLI is available - if command -v az &> /dev/null; then - az bicep build --file ./infra/main.bicep --outdir ./artifacts - echo "✅ Bicep build completed" - else - echo "⚠️ Azure CLI not available, creating placeholder artifacts" - echo "Bicep build would be executed here" > ./artifacts/placeholder.txt - fi - - - name: Upload Artifacts - uses: actions/upload-artifact@v4 - with: - name: artifacts-${{ needs.generate_release.outputs.new_version }} - path: ./artifacts - compression-level: 6 - overwrite: true - if-no-files-found: warn - retention-days: 7 - - - name: Artifact Summary - run: | - branch_name="${{ needs.generate_release.outputs.branch_name }}" - version="${{ needs.generate_release.outputs.new_version }}" - should_publish="${{ needs.generate_release.outputs.should_publish }}" - - echo "📦 Artifacts Summary:" - echo " - Version: $version" - echo " - Branch: $branch_name" - echo " - Tag created: ✅" - echo " - Artifacts uploaded: ✅" - echo " - GitHub release will be published: $should_publish" - - if [[ "$should_publish" == "false" ]]; then - echo "" - echo "ℹ️ This is a development build from a non-main branch." - echo " Tag and artifacts are created for tracking, but no GitHub release is published." - fi - - publish_release: - runs-on: ubuntu-latest - needs: [generate_release, create_tag, upload_artifacts] - if: needs.generate_release.outputs.should_publish == 'true' - steps: - - name: Download artifacts - uses: actions/download-artifact@v4 + - name: Publish Release + uses: softprops/action-gh-release@v1 + id: publish with: - name: artifacts-${{ needs.generate_release.outputs.new_version }} - path: ./artifacts - - - name: Create Git Tag and GitHub Release - uses: softprops/action-gh-release@v2 - with: - tag_name: ${{ needs.generate_release.outputs.new_version }} - name: Release ${{ needs.generate_release.outputs.new_version }} - body: | - 🌟 **Branch-Based Release Strategy with Conditional Major Increment** - - 🔀 **Branch**: `${{ needs.generate_release.outputs.branch_name }}` - 🏷️ **Version**: `${{ needs.generate_release.outputs.new_version }}` - 📦 **Previous Version**: `${{ needs.generate_release.outputs.previous_tag }}` - 🚀 **Release Type**: `${{ needs.generate_release.outputs.release_type }}` - 🤖 **Trigger**: ${{ github.event_name == 'workflow_dispatch' && 'Manual' || github.event_name == 'pull_request' && 'Pull Request' || 'Push' }} - 📝 **Commit**: `${{ github.sha }}` - - ## Release Strategy Applied - - ${{ needs.generate_release.outputs.release_type == 'main' && '🎯 **Main Branch**: Conditional major increment (only if minor=0 AND patch=0, otherwise increment patch with overflow logic)' || '' }} - ${{ needs.generate_release.outputs.release_type == 'feature' && '✨ **Feature Branch**: Patch increment with overflow logic (patch += commits, overflow at 99 → minor+1)' || '' }} - ${{ needs.generate_release.outputs.release_type == 'fix' && '🔧 **Fix Branch**: Minor increment with overflow logic (minor += commits, overflow at 99 → major+1)' || '' }} - - ## Main Branch Logic (NEW RULE) - - - **If minor=0 AND patch=0**: Increment major → `major+1.0.0` - - **If minor≠0 OR patch≠0**: Keep major, increment patch → `major.minor.(patch+1)` - - **Overflow handling**: If patch > 99 → `minor+1, patch=0`; if minor > 99 → `major+1, minor=0` - - ## Feature/Fix Branch Overflow Logic - - - **Feature branches**: If `patch + commits > 99` → `patch = 0, minor += 1` - - **Fix branches**: If `minor + commits > 99` → `minor = 0, major += 1` - - **Cascading overflow**: If minor overflows during patch overflow → `minor = 0, major += 1` - - ## Artifacts - - 📄 Bicep templates compiled to ARM templates - - 🏗️ Infrastructure deployment files - - 📋 Release metadata and documentation - files: ./artifacts/* + tag_name: ${{ needs.generate-release.outputs.new_version }} + name: Release ${{ needs.generate-release.outputs.new_version }} draft: false prerelease: false - make_latest: true \ No newline at end of file + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + \ No newline at end of file