diff --git a/.github/workflows/release-scrapyard.yml b/.github/workflows/release-scrapyard.yml index 3132c06..7789f74 100644 --- a/.github/workflows/release-scrapyard.yml +++ b/.github/workflows/release-scrapyard.yml @@ -630,4 +630,110 @@ jobs: echo "✅ GitHub release created" echo "⏭️ AUR publishing skipped (dry run mode)" echo "" - echo "To publish to AUR, re-run with dry_run=false" \ No newline at end of file + echo "To publish to AUR, re-run with dry_run=false" + + + + + inputs: + new_version: + description: "New version to release (e.g., 0.2.0)" + required: false + type: string + pr_check_timeout: + description: "Timeout in seconds for PR status checks (default: 1800)" + required: false + type: number + default: 1800 + jobs: + description: "Comma-separated jobs to run (e.g., build-windows,build-debian,build-arch,build-rhel)" + required: true + default: "build-windows,build-debian,build-arch,build-rhel" + start_from_step: + description: "Start workflow from this step (all subsequent steps will run)" + required: false + type: choice + default: "bump-version" + options: + - "bump-version" + - "merge-develop-to-main" + + + + rollback-on-build-failure: + needs: [merge-develop-to-main, wait-for-main-pr, build-windows, build-debian, build-arch, build-rhel] + if: | + ${{ + always() && + (github.event.inputs.start_from_step == 'bump-version' || + github.event.inputs.start_from_step == 'merge-develop-to-main') && + needs.wait-for-main-pr.result == 'success' && + (needs.build-windows.result == 'failure' || + needs.build-debian.result == 'failure' || + needs.build-arch.result == 'failure' || + needs.build-rhel.result == 'failure') + }} + runs-on: ubuntu-latest + permissions: + contents: write + # Prevent multiple rollback operations from running concurrently + concurrency: + group: main-branch-rollback + cancel-in-progress: false + steps: + - name: Checkout main branch + uses: actions/checkout@v4 + with: + ref: main + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Rollback merge commit on build failure + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -e + echo "::error::One or more build jobs failed - initiating rollback" + + # Configure git + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + # Fetch latest + git fetch origin main + + # Get the previous main SHA (before the merge) + PREVIOUS_MAIN_SHA="${{ needs.merge-develop-to-main.outputs.previous_main_sha }}" + echo "Resetting main to previous commit: $PREVIOUS_MAIN_SHA" + + # Checkout main and reset to previous commit + git checkout main + git reset --hard "$PREVIOUS_MAIN_SHA" + + # Force push the rollback + if ! git push --force origin main; then + git notes -m 'release commit could not be rolled back' + echo "::error::Failed to push rollback to main" + exit 1 + fi + git notes -m 'release commit rolled back' + echo "::error::Reset main branch to commit $PREVIOUS_MAIN_SHA" + echo "::error::Build failures detected:" + + # Report which builds failed + if [ "${{ needs.build-windows.result }}" = "failure" ]; then + echo "::error:: - build-windows: FAILED" + fi + if [ "${{ needs.build-debian.result }}" = "failure" ]; then + echo "::error:: - build-debian: FAILED" + fi + if [ "${{ needs.build-arch.result }}" = "failure" ]; then + echo "::error:: - build-arch: FAILED" + fi + if [ "${{ needs.build-rhel.result }}" = "failure" ]; then + echo "::error:: - build-rhel: FAILED" + fi + + exit 1 + + diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6ae8bac..7f1c294 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,38 +11,17 @@ name: Build Multi-Platform Binaries on: workflow_dispatch: - inputs: - new_version: - description: "New version to release (e.g., 0.2.0)" - required: false - type: string - pr_check_timeout: - description: "Timeout in seconds for PR status checks (default: 1800)" - required: false - type: number - default: 1800 - jobs: - description: "Comma-separated jobs to run (e.g., build-windows,build-debian,build-arch,build-rhel)" - required: true - default: "build-windows,build-debian,build-arch,build-rhel" - start_from_step: - description: "Start workflow from this step (all subsequent steps will run)" - required: false - type: choice - default: "bump-version" - options: - - "bump-version" - - "merge-develop-to-main" permissions: - contents: write - pull-requests: write + contents: read + packages: read + # Prevent multiple release workflows from running simultaneously # This is critical to prevent concurrent rollbacks concurrency: group: release-workflow - cancel-in-progress: false + cancel-in-progress: true env: # change this if you prefer a different pinned fpm version @@ -52,497 +31,16 @@ env: CI_CD: true jobs: - bump-version: - if: ${{ github.event.inputs.start_from_step == 'bump-version' }} - runs-on: ubuntu-latest - permissions: - contents: write - pull-requests: write - outputs: - pr_number: ${{ steps.create-pr.outputs.pr_number }} - steps: - - name: Validate version input - run: | - VERSION="${{ github.event.inputs.new_version }}" - if [ -z "$VERSION" ]; then - echo "::error::Version number is required for the bump-version task" - echo "::error::Please provide a version number in the 'new_version' input field" - echo "::error::Expected semantic version format (e.g., 1.2.3, 1.2.3-beta.1, 1.2.3+build.123)" - exit 1 - fi - # Full semver regex supporting: - # - Basic: 1.2.3 - # - Prerelease: 1.2.3-beta, 1.2.3-rc.1, 1.2.3-alpha.1.2 - # - Build metadata: 1.2.3+build, 1.2.3+20130313144700 - # - Combined: 1.2.3-beta.1+build.123 - if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*)?(\+[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*)?$ ]]; then - echo "::error::Invalid version format: $VERSION" - echo "::error::Expected semantic version format (e.g., 1.2.3, 1.2.3-beta.1, 1.2.3+build.123)" - exit 1 - fi - echo "Version format is valid: $VERSION" - - - name: Checkout develop branch - uses: actions/checkout@v4 - with: - ref: develop - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Configure git - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: '3.13' - - - name: Install Poetry - uses: snok/install-poetry@v1 - - - name: Check for existing PR or branch - id: check-existing - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -e - VERSION="${{ github.event.inputs.new_version }}" - BRANCH_NAME="release/v${VERSION}" - - # Check if branch already exists - if git ls-remote --heads origin "$BRANCH_NAME" | grep -q "$BRANCH_NAME"; then - echo "::warning::Branch $BRANCH_NAME already exists" - - # Check if there's an open PR for this branch - EXISTING_PR=$(gh pr list --base develop --head "$BRANCH_NAME" --state open --json number --jq '.[0].number' || echo "") - - if [ -n "$EXISTING_PR" ]; then - echo "::error::PR #$EXISTING_PR already exists for version $VERSION" - echo "::error::Please close or merge the existing PR before creating a new release" - exit 1 - fi - - echo "::error::Branch $BRANCH_NAME exists but no open PR found" - echo "::error::Please delete the branch or use a different version number" - exit 1 - fi - - echo "✓ No existing branch or PR found for version $VERSION" - echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT - - - name: Create release branch and bump version - id: bump - run: | - set -e - VERSION="${{ github.event.inputs.new_version }}" - BRANCH_NAME="${{ steps.check-existing.outputs.branch_name }}" - - # Create and checkout release branch - git checkout -b "$BRANCH_NAME" - - # Update version in pyproject.toml - poetry version "$VERSION" - - # Commit the version change - git add pyproject.toml - git commit -m "Bump version to ${VERSION}" - - # Push the branch with error handling - if ! git push origin "$BRANCH_NAME"; then - echo "::error::Failed to push branch $BRANCH_NAME" - exit 1 - fi - - echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT - - - name: Create PR to develop - id: create-pr - env: - GH_TOKEN: ${{ secrets.CI_CD_PAT }} - run: | - set -e - VERSION="${{ github.event.inputs.new_version }}" - BRANCH_NAME="${{ steps.bump.outputs.branch_name }}" - - # Create PR with auto-merge enabled - PR_URL=$(gh pr create \ - --base develop \ - --head "$BRANCH_NAME" \ - --title "Release v${VERSION}" \ - --body "This PR bumps the version to ${VERSION} as part of the release process. - - **Auto-generated by release workflow** - - Once status checks pass, this PR will be automatically merged." \ - --repo ${{ github.repository }} || { - echo "::error::Failed to create PR" - exit 1 - }) - - # Extract PR number from URL - PR_NUMBER=$(echo "$PR_URL" | grep -oP '\d+$') - echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT - echo "Created PR #$PR_NUMBER: $PR_URL" - - # Enable auto-merge (rebase) - if ! gh pr merge "$PR_NUMBER" --auto --rebase --repo ${{ github.repository }}; then - echo "::error::Failed to enable auto-merge for PR #$PR_NUMBER" - exit 1 - fi - echo "Auto-merge enabled for PR #$PR_NUMBER" - - wait-for-version-pr: - needs: [bump-version] - if: ${{ github.event.inputs.start_from_step == 'bump-version' }} - runs-on: ubuntu-latest - permissions: - contents: read - pull-requests: read - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Wait for PR status checks and merge - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -e - PR_NUMBER="${{ needs.bump-version.outputs.pr_number }}" - echo "Monitoring PR #$PR_NUMBER for status checks..." - - MAX_WAIT=${{ github.event.inputs.pr_check_timeout || 1800 }} - INITIAL_INTERVAL=10 - MAX_INTERVAL=300 # 5 minutes maximum - BACKOFF_MULTIPLIER=1.5 - SLEEP_INTERVAL=$INITIAL_INTERVAL - ELAPSED=0 - PR_STATE_RETRY_COUNT=0 - STATUS_CHECK_RETRY_COUNT=0 - MAX_RETRIES=3 - echo "Max wait time: ${MAX_WAIT}s, using exponential backoff (max interval: ${MAX_INTERVAL}s)" - - while [ $ELAPSED -lt $MAX_WAIT ]; do - # Get PR status with retry logic - if ! PR_STATE=$(gh pr view "$PR_NUMBER" --json state --jq '.state' --repo ${{ github.repository }} 2>&1); then - PR_STATE_RETRY_COUNT=$((PR_STATE_RETRY_COUNT + 1)) - if [ $PR_STATE_RETRY_COUNT -ge $MAX_RETRIES ]; then - echo "::error::Failed to fetch PR status after $MAX_RETRIES retries" - exit 1 - fi - echo "::warning::Failed to fetch PR status (attempt $PR_STATE_RETRY_COUNT/$MAX_RETRIES), retrying..." - sleep $((2 ** PR_STATE_RETRY_COUNT)) - continue - fi - PR_STATE_RETRY_COUNT=0 - - if [ "$PR_STATE" = "MERGED" ]; then - echo "✓ PR #$PR_NUMBER has been merged successfully!" - exit 0 - fi - - if [ "$PR_STATE" = "CLOSED" ]; then - echo "::error::PR #$PR_NUMBER was closed without merging" - exit 1 - fi - - # Check status checks with retry logic - if ! STATUS_JSON=$(gh pr view "$PR_NUMBER" --json statusCheckRollup --jq '.statusCheckRollup' --repo ${{ github.repository }} 2>&1); then - STATUS_CHECK_RETRY_COUNT=$((STATUS_CHECK_RETRY_COUNT + 1)) - if [ $STATUS_CHECK_RETRY_COUNT -ge $MAX_RETRIES ]; then - echo "::error::Failed to fetch status checks after $MAX_RETRIES retries" - exit 1 - fi - echo "::warning::Failed to fetch status checks (attempt $STATUS_CHECK_RETRY_COUNT/$MAX_RETRIES), retrying..." - sleep $((2 ** STATUS_CHECK_RETRY_COUNT)) - continue - fi - STATUS_CHECK_RETRY_COUNT=0 - - # Count check states - TOTAL=$(echo "$STATUS_JSON" | jq 'length') - COMPLETED=$(echo "$STATUS_JSON" | jq '[.[] | select(.conclusion != null)] | length') - SUCCESS=$(echo "$STATUS_JSON" | jq '[.[] | select(.conclusion == "SUCCESS" or .conclusion == "NEUTRAL" or .conclusion == "SKIPPED")] | length') - FAILED=$(echo "$STATUS_JSON" | jq '[.[] | select(.conclusion == "FAILURE" or .conclusion == "CANCELLED" or .conclusion == "TIMED_OUT")] | length') - - echo "Status checks: $COMPLETED/$TOTAL completed, $SUCCESS passed, $FAILED failed (interval: ${SLEEP_INTERVAL}s)" - - # Check for failures - if [ "$FAILED" -gt 0 ]; then - echo "::error::Status checks failed for PR #$PR_NUMBER" - gh pr view "$PR_NUMBER" --json statusCheckRollup --jq '.statusCheckRollup[] | select(.conclusion == "FAILURE" or .conclusion == "CANCELLED" or .conclusion == "TIMED_OUT") | "- " + .name + ": " + .conclusion' --repo ${{ github.repository }} - exit 1 - fi - - echo "Waiting for checks to complete... (${ELAPSED}s elapsed)" - sleep $SLEEP_INTERVAL - ELAPSED=$((ELAPSED + SLEEP_INTERVAL)) - - # Calculate next interval with exponential backoff (capped at MAX_INTERVAL) - NEXT_INTERVAL=$(awk "BEGIN {printf \"%.0f\", $SLEEP_INTERVAL * $BACKOFF_MULTIPLIER}") - if [ $NEXT_INTERVAL -gt $MAX_INTERVAL ]; then - SLEEP_INTERVAL=$MAX_INTERVAL - else - SLEEP_INTERVAL=$NEXT_INTERVAL - fi - done - - echo "::error::Timeout waiting for PR #$PR_NUMBER to merge" - exit 1 - - merge-develop-to-main: - needs: [wait-for-version-pr] - if: | - ${{ - always() && - (github.event.inputs.start_from_step == 'bump-version' || github.event.inputs.start_from_step == 'merge-develop-to-main') && - (needs.wait-for-version-pr.result == 'success' || needs.wait-for-version-pr.result == 'skipped') - }} - runs-on: ubuntu-latest - permissions: - contents: write - pull-requests: write - # Ensure only one merge operation runs at a time - concurrency: - group: main-branch-merge - cancel-in-progress: false - outputs: - pr_number: ${{ steps.create-pr.outputs.pr_number }} - previous_main_sha: ${{ steps.check-branches.outputs.previous_main_sha }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - ref: develop - fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Configure git - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: '3.13' - - - name: Install Poetry - uses: snok/install-poetry@v1 - - - name: Check branches and verify merge readiness - id: check-branches - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -e - git fetch origin main develop - - # Store current main SHA for reference - PREVIOUS_MAIN_SHA=$(git rev-parse origin/main) - echo "previous_main_sha=$PREVIOUS_MAIN_SHA" >> $GITHUB_OUTPUT - echo "Previous main SHA: $PREVIOUS_MAIN_SHA" - - # Verify develop is ahead of main - MERGE_BASE=$(git merge-base origin/main origin/develop) - MAIN_SHA=$(git rev-parse origin/main) - - if [ "$MERGE_BASE" != "$MAIN_SHA" ]; then - echo "::error::Main branch has commits not in develop. Cannot fast-forward." - echo "::error::Please merge or rebase main into develop first." - exit 1 - fi - - # Ensure develop is actually ahead - DEVELOP_SHA=$(git rev-parse origin/develop) - if [ "$MAIN_SHA" = "$DEVELOP_SHA" ]; then - echo "::warning::Main is already up to date with develop. Nothing to merge." - exit 0 - fi - - # Check for existing open PR from develop to main - EXISTING_PR=$(gh pr list --base main --head develop --state open --json number --jq '.[0].number' || echo "") - if [ -n "$EXISTING_PR" ]; then - echo "::error::PR #$EXISTING_PR already exists from develop to main" - echo "::error::Please close or merge the existing PR before creating a new one" - exit 1 - fi - echo "✓ Ready to create PR from develop to main" - - - name: Create PR from develop to main - id: create-pr - env: - GH_TOKEN: ${{ secrets.CI_CD_PAT }} - run: | - set -e - # Read version from pyproject.toml (single source of truth) - if ! VERSION="$(poetry version -s 2>&1)"; then - echo "::error::Failed to read version from pyproject.toml" - echo "::error::Poetry output: $VERSION" - exit 1 - fi - if [ -z "$VERSION" ]; then - echo "::error::Version is empty in pyproject.toml" - exit 1 - fi - # Validate semver format - if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*)?(\+[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*)?$ ]]; then - echo "::error::Invalid version format in pyproject.toml: $VERSION" - echo "::error::Expected semantic version format (e.g., 1.2.3, 1.2.3-beta.1, 1.2.3+build.123)" - exit 1 - fi - echo "Using version from pyproject.toml: $VERSION" - - # Create PR with auto-merge enabled - PR_URL=$(gh pr create \ - --base main \ - --head develop \ - --title "Release v${VERSION} - Merge develop into main" \ - --body "This PR merges develop into main for release v${VERSION}. - - **Auto-generated by release workflow** - - Once status checks pass, this PR will be automatically merged." \ - --repo ${{ github.repository }} || { - echo "::error::Failed to create PR" - exit 1 - }) - - # Extract PR number from URL - PR_NUMBER=$(echo "$PR_URL" | grep -oP '\d+$') - echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT - echo "Created PR #$PR_NUMBER: $PR_URL" - - # Enable auto-merge (rebase for fast-forward) - if ! gh pr merge "$PR_NUMBER" --auto --rebase --repo ${{ github.repository }}; then - echo "::error::Failed to enable auto-merge for PR #$PR_NUMBER" - exit 1 - fi - echo "Auto-merge enabled for PR #$PR_NUMBER" - - wait-for-main-pr: - needs: [merge-develop-to-main] - if: | - ${{ - always() && - (github.event.inputs.start_from_step == 'bump-version' || - github.event.inputs.start_from_step == 'merge-develop-to-main') && - needs.merge-develop-to-main.result == 'success' - }} - runs-on: ubuntu-latest + status-checks: permissions: contents: read - pull-requests: read - outputs: - merge_commit_sha: ${{ steps.wait-merge.outputs.merge_commit_sha }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 + packages: read - - name: Wait for PR status checks and merge - id: wait-merge - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -e - PR_NUMBER="${{ needs.merge-develop-to-main.outputs.pr_number }}" - echo "Monitoring PR #$PR_NUMBER for status checks..." - - MAX_WAIT=${{ github.event.inputs.pr_check_timeout || 1800 }} - INITIAL_INTERVAL=10 - MAX_INTERVAL=300 # 5 minutes maximum - BACKOFF_MULTIPLIER=1.5 - SLEEP_INTERVAL=$INITIAL_INTERVAL - ELAPSED=0 - PR_STATE_RETRY_COUNT=0 - STATUS_CHECK_RETRY_COUNT=0 - MAX_RETRIES=3 - echo "Max wait time: ${MAX_WAIT}s, using exponential backoff (max interval: ${MAX_INTERVAL}s)" - - while [ $ELAPSED -lt $MAX_WAIT ]; do - # Get PR status with retry logic - if ! PR_STATE=$(gh pr view "$PR_NUMBER" --json state --jq '.state' --repo ${{ github.repository }} 2>&1); then - PR_STATE_RETRY_COUNT=$((PR_STATE_RETRY_COUNT + 1)) - if [ $PR_STATE_RETRY_COUNT -ge $MAX_RETRIES ]; then - echo "::error::Failed to fetch PR status after $MAX_RETRIES retries" - exit 1 - fi - echo "::warning::Failed to fetch PR status (attempt $PR_STATE_RETRY_COUNT/$MAX_RETRIES), retrying..." - sleep $((2 ** PR_STATE_RETRY_COUNT)) - continue - fi - PR_STATE_RETRY_COUNT=0 - - if [ "$PR_STATE" = "MERGED" ]; then - echo "✓ PR #$PR_NUMBER has been merged successfully!" - - # Get the merge commit SHA - MERGE_COMMIT_SHA=$(gh pr view "$PR_NUMBER" --json mergeCommit --jq '.mergeCommit.oid' --repo ${{ github.repository }}) - echo "merge_commit_sha=$MERGE_COMMIT_SHA" >> $GITHUB_OUTPUT - echo "Merge commit SHA: $MERGE_COMMIT_SHA" - exit 0 - fi - - if [ "$PR_STATE" = "CLOSED" ]; then - echo "::error::PR #$PR_NUMBER was closed without merging" - exit 1 - fi - - # Check status checks with retry logic - if ! STATUS_JSON=$(gh pr view "$PR_NUMBER" --json statusCheckRollup --jq '.statusCheckRollup' --repo ${{ github.repository }} 2>&1); then - STATUS_CHECK_RETRY_COUNT=$((STATUS_CHECK_RETRY_COUNT + 1)) - if [ $STATUS_CHECK_RETRY_COUNT -ge $MAX_RETRIES ]; then - echo "::error::Failed to fetch status checks after $MAX_RETRIES retries" - exit 1 - fi - echo "::warning::Failed to fetch status checks (attempt $STATUS_CHECK_RETRY_COUNT/$MAX_RETRIES), retrying..." - sleep $((2 ** STATUS_CHECK_RETRY_COUNT)) - continue - fi - STATUS_CHECK_RETRY_COUNT=0 - - # Count check states - TOTAL=$(echo "$STATUS_JSON" | jq 'length') - COMPLETED=$(echo "$STATUS_JSON" | jq '[.[] | select(.conclusion != null)] | length') - SUCCESS=$(echo "$STATUS_JSON" | jq '[.[] | select(.conclusion == "SUCCESS" or .conclusion == "NEUTRAL" or .conclusion == "SKIPPED")] | length') - FAILED=$(echo "$STATUS_JSON" | jq '[.[] | select(.conclusion == "FAILURE" or .conclusion == "CANCELLED" or .conclusion == "TIMED_OUT")] | length') - - echo "Status checks: $COMPLETED/$TOTAL completed, $SUCCESS passed, $FAILED failed (interval: ${SLEEP_INTERVAL}s)" - - # Check for failures - if [ "$FAILED" -gt 0 ]; then - echo "::error::Status checks failed for PR #$PR_NUMBER" - gh pr view "$PR_NUMBER" --json statusCheckRollup --jq '.statusCheckRollup[] | select(.conclusion == "FAILURE" or .conclusion == "CANCELLED" or .conclusion == "TIMED_OUT") | "- " + .name + ": " + .conclusion' --repo ${{ github.repository }} - exit 1 - fi - - echo "Waiting for checks to complete... (${ELAPSED}s elapsed)" - sleep $SLEEP_INTERVAL - ELAPSED=$((ELAPSED + SLEEP_INTERVAL)) - - # Calculate next interval with exponential backoff (capped at MAX_INTERVAL) - NEXT_INTERVAL=$(awk "BEGIN {printf \"%.0f\", $SLEEP_INTERVAL * $BACKOFF_MULTIPLIER}") - if [ $NEXT_INTERVAL -gt $MAX_INTERVAL ]; then - SLEEP_INTERVAL=$MAX_INTERVAL - else - SLEEP_INTERVAL=$NEXT_INTERVAL - fi - done - - echo "::error::Timeout waiting for PR #$PR_NUMBER to merge" - exit 1 + uses: ./.github/workflows/status-checks.yml build-windows: - needs: [wait-for-main-pr] - if: | - ${{ - always() && - contains(github.event.inputs.jobs, 'build-windows') && - (github.event.inputs.start_from_step == 'bump-version' || - github.event.inputs.start_from_step == 'merge-develop-to-main') && - needs.wait-for-main-pr.result == 'success' - }} + needs: status-checks runs-on: windows-latest steps: - name: Checkout code @@ -628,15 +126,7 @@ jobs: dist/android-file-handler-windows.sha256 build-debian: - needs: [wait-for-main-pr] - if: | - ${{ - always() && - contains(github.event.inputs.jobs, 'build-debian') && - (github.event.inputs.start_from_step == 'bump-version' || - github.event.inputs.start_from_step == 'merge-develop-to-main') && - needs.wait-for-main-pr.result == 'success' - }} + needs: status-checks permissions: contents: read packages: read @@ -744,15 +234,7 @@ jobs: pkg_dist_debian/** build-arch: - needs: [wait-for-main-pr] - if: | - ${{ - always() && - contains(github.event.inputs.jobs, 'build-arch') && - (github.event.inputs.start_from_step == 'bump-version' || - github.event.inputs.start_from_step == 'merge-develop-to-main') && - needs.wait-for-main-pr.result == 'success' - }} + needs: status-checks permissions: contents: read packages: read @@ -878,15 +360,7 @@ jobs: build-rhel: - needs: [wait-for-main-pr] - if: | - ${{ - always() && - contains(github.event.inputs.jobs, 'build-rhel') && - (github.event.inputs.start_from_step == 'bump-version' || - github.event.inputs.start_from_step == 'merge-develop-to-main') && - needs.wait-for-main-pr.result == 'success' - }} + needs: status-checks permissions: contents: read packages: read @@ -991,96 +465,14 @@ jobs: dist/android-file-handler-rhel.sha256 pkg_dist_rhel/** - rollback-on-build-failure: - needs: [merge-develop-to-main, wait-for-main-pr, build-windows, build-debian, build-arch, build-rhel] - if: | - ${{ - always() && - (github.event.inputs.start_from_step == 'bump-version' || - github.event.inputs.start_from_step == 'merge-develop-to-main') && - needs.wait-for-main-pr.result == 'success' && - (needs.build-windows.result == 'failure' || - needs.build-debian.result == 'failure' || - needs.build-arch.result == 'failure' || - needs.build-rhel.result == 'failure') - }} - runs-on: ubuntu-latest - permissions: - contents: write - # Prevent multiple rollback operations from running concurrently - concurrency: - group: main-branch-rollback - cancel-in-progress: false - steps: - - name: Checkout main branch - uses: actions/checkout@v4 - with: - ref: main - fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Rollback merge commit on build failure - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -e - echo "::error::One or more build jobs failed - initiating rollback" - - # Configure git - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - - # Fetch latest - git fetch origin main - - # Get the previous main SHA (before the merge) - PREVIOUS_MAIN_SHA="${{ needs.merge-develop-to-main.outputs.previous_main_sha }}" - echo "Resetting main to previous commit: $PREVIOUS_MAIN_SHA" - - # Checkout main and reset to previous commit - git checkout main - git reset --hard "$PREVIOUS_MAIN_SHA" - - # Force push the rollback - if ! git push --force origin main; then - git notes -m 'release commit could not be rolled back' - echo "::error::Failed to push rollback to main" - exit 1 - fi - git notes -m 'release commit rolled back' - echo "::error::Reset main branch to commit $PREVIOUS_MAIN_SHA" - echo "::error::Build failures detected:" - - # Report which builds failed - if [ "${{ needs.build-windows.result }}" = "failure" ]; then - echo "::error:: - build-windows: FAILED" - fi - if [ "${{ needs.build-debian.result }}" = "failure" ]; then - echo "::error:: - build-debian: FAILED" - fi - if [ "${{ needs.build-arch.result }}" = "failure" ]; then - echo "::error:: - build-arch: FAILED" - fi - if [ "${{ needs.build-rhel.result }}" = "failure" ]; then - echo "::error:: - build-rhel: FAILED" - fi - - exit 1 - - + do-release: - needs: [rollback-on-build-failure, build-windows, build-debian, build-arch, build-rhel] - if: | - ${{ - always() && - (github.event.inputs.start_from_step == 'bump-version' || - github.event.inputs.start_from_step == 'merge-develop-to-main') && - needs.rollback-on-build-failure.result != 'failure' && - (needs.build-windows.result == 'success' || needs.build-windows.result == 'skipped') && - (needs.build-debian.result == 'success' || needs.build-debian.result == 'skipped') && - (needs.build-arch.result == 'success' || needs.build-arch.result == 'skipped') && - (needs.build-rhel.result == 'success' || needs.build-rhel.result == 'skipped') - }} + needs: + - status-checks + - build-windows + - build-debian + - build-arch + - build-rhel runs-on: ubuntu-latest steps: - name: Checkout code @@ -1146,19 +538,9 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} upload-s3: - needs: [rollback-on-build-failure, build-windows, build-debian, build-arch, build-rhel] - if: | - ${{ - always() && - (github.event.inputs.start_from_step == 'bump-version' || - github.event.inputs.start_from_step == 'merge-develop-to-main') && - needs.rollback-on-build-failure.result != 'failure' && - (needs.build-windows.result == 'success' || needs.build-windows.result == 'skipped') && - (needs.build-debian.result == 'success' || needs.build-debian.result == 'skipped') && - (needs.build-arch.result == 'success' || needs.build-arch.result == 'skipped') && - (needs.build-rhel.result == 'success' || needs.build-rhel.result == 'skipped') - }} runs-on: ubuntu-latest + needs: do-release + if: needs.do-release.result == 'success' steps: - name: Checkout code uses: actions/checkout@v4 @@ -1195,7 +577,10 @@ jobs: sync-wiki: needs: do-release - if: always() && needs.do-release.result == 'success' + if: needs.do-release.result == 'success' + permissions: + contents: write + pull-requests: write uses: ./.github/workflows/sync-wiki.yml with: branch: main diff --git a/.github/workflows/status-checks.yml b/.github/workflows/status-checks.yml index 53653cd..9b34ffa 100644 --- a/.github/workflows/status-checks.yml +++ b/.github/workflows/status-checks.yml @@ -5,11 +5,11 @@ name: Status Checks on: pull_request: branches: [ main, develop ] + workflow_call: permissions: contents: read packages: read - pull-requests: write env: FPM_VERSION: "1.16.0" @@ -42,6 +42,7 @@ jobs: permissions: contents: read packages: read + container: image: ghcr.io/jmr-dev/android-file-handler-arch-builder:latest credentials: