diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a535bdd..ba6630b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,7 +2,8 @@ name: App Search Tests on: push: - merge_group: + branches-ignore: + - master # release.yml handles master schedule: - cron: '0 1 * * 5' @@ -13,6 +14,7 @@ concurrency: jobs: outdated: name: Outdated packages + if: ${{ !startsWith(github.ref, 'refs/heads/gh-readonly-queue/') }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -31,7 +33,7 @@ jobs: run: pip list --outdated --not-required --user | grep . && echo "There are outdated packages" && exit 1 || echo "All packages up to date" ruff: - name: Ruff Format + name: Lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -53,7 +55,7 @@ jobs: run: ruff check security: - name: Bandit Security + name: Security runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -82,6 +84,7 @@ jobs: path: report.json tests: + if: ${{ !startsWith(github.ref, 'refs/heads/gh-readonly-queue/') }} name: Python ${{ matrix.python-version }} / ${{ matrix.db }} / Django ${{ matrix.django-version}} runs-on: ubuntu-latest continue-on-error: ${{ matrix.django-version == '~=6.0' || matrix.python-version == '3.14' }} @@ -148,7 +151,8 @@ jobs: run: python demo_app/manage.py check coverage: - name: Upload Coverage to Codecov + name: Coverage + if: ${{ !startsWith(github.ref, 'refs/heads/gh-readonly-queue/') }} needs: [tests] runs-on: ubuntu-latest steps: @@ -178,35 +182,3 @@ jobs: directory: . token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: true - - release: - name: Release - runs-on: ubuntu-latest - needs: ['outdated', 'ruff', 'security', 'tests', 'coverage'] - if: always() && github.ref == 'refs/heads/master' && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') - permissions: write-all - outputs: - bumped: ${{ steps.release.outputs.bumped }} - bump_version: ${{ steps.release.outputs.bump_version }} - bump_sha: ${{ steps.release.outputs.bump_sha }} - steps: - - uses: actions/checkout@v4 - with: - ref: master - fetch-depth: 0 # Need full history for git tags and version calculation - - uses: actions/setup-python@v5 - with: - python-version: "3.13" - - name: Install dependencies - run: | - pip install git+https://${{ secrets.ORGANIZATIONAL_REPO_TOKEN }}@github.com/pivotal-energy-solutions/tensor-infrastructure@master#egg=infrastructure - - name: Release - id: release - env: - PYTHONWARNINGS: once::DeprecationWarning - GITHUB_TOKEN: ${{ secrets.ORGANIZATIONAL_REPO_TOKEN }} - run: | - bumper -P - echo "bumped=$(jq '.bumped' out.json)" >> $GITHUB_OUTPUT - echo "bump_version=$(jq '.bump_version' out.json)" >> $GITHUB_OUTPUT - echo "bump_sha=$(jq '.bump_sha' out.json)" >> $GITHUB_OUTPUT diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..29cbed3 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,126 @@ +name: Release + +on: + push: + branches: + - master + +jobs: + release: + name: Release + runs-on: ubuntu-latest + permissions: write-all + outputs: + bumped: ${{ steps.release.outputs.bumped }} + bump_version: ${{ steps.release.outputs.bump_version }} + bump_sha: ${{ steps.release.outputs.bump_sha }} + steps: + - uses: actions/checkout@v4 + with: + ref: master + fetch-depth: 0 + + - name: Verify commit came from a PR with passing checks + id: verify + env: + GH_TOKEN: ${{ github.token }} + run: | + echo "🔍 Checking if commit ${{ github.sha }} came from a valid PR..." + for i in 1 2 3; do + PR_JSON=$(gh api repos/${{ github.repository }}/commits/${{ github.sha }}/pulls 2>/dev/null || echo "[]") + PR_COUNT=$(echo "$PR_JSON" | jq 'length') + if [ "$PR_COUNT" -gt 0 ]; then + break + fi + echo " Attempt $i: No PR found yet, waiting 2s..." + sleep 2 + done + + if [ "$PR_COUNT" -eq 0 ]; then + echo "❌ This commit did not come from a PR - skipping release" + echo "should_release=false" >> $GITHUB_OUTPUT + exit 0 + fi + + PR_NUMBER=$(echo "$PR_JSON" | jq -r '.[0].number') + HEAD_SHA=$(echo "$PR_JSON" | jq -r '.[0].head.sha') + PR_TITLE=$(echo "$PR_JSON" | jq -r '.[0].title') + echo "📋 Found PR #$PR_NUMBER: $PR_TITLE" + + REQUIRED_CHECKS=("Lint" "Security" "Coverage") + ALL_PASSED=true + for CHECK_NAME in "${REQUIRED_CHECKS[@]}"; do + RESULT=$(gh api repos/${{ github.repository }}/commits/$HEAD_SHA/check-runs \ + --jq ".check_runs[] | select(.name == \"$CHECK_NAME\") | .conclusion" 2>/dev/null | head -1) + if [ "$RESULT" = "success" ]; then + echo " ✅ $CHECK_NAME: passed" + else + echo " ❌ $CHECK_NAME: $RESULT" + ALL_PASSED=false + fi + done + + if [ "$ALL_PASSED" = "false" ]; then + echo "❌ Required checks did not pass - skipping release" + echo "should_release=false" >> $GITHUB_OUTPUT + exit 0 + fi + + echo "✅ All required checks passed on PR #$PR_NUMBER" + echo "should_release=true" >> $GITHUB_OUTPUT + echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT + + - name: Skip release notification + if: steps.verify.outputs.should_release != 'true' + run: | + echo "⚠️ Release skipped - commit did not meet release criteria" + + - uses: actions/setup-python@v5 + if: steps.verify.outputs.should_release == 'true' + with: + python-version: "3.13" + + - name: Install dependencies + if: steps.verify.outputs.should_release == 'true' + run: | + pip install git+https://${{ secrets.ORGANIZATIONAL_REPO_TOKEN }}@github.com/pivotal-energy-solutions/tensor-infrastructure@master#egg=infrastructure + + - name: Release + id: release + if: steps.verify.outputs.should_release == 'true' + env: + PYTHONWARNINGS: once::DeprecationWarning + GITHUB_TOKEN: ${{ secrets.ORGANIZATIONAL_REPO_TOKEN }} + GH_TOKEN: ${{ github.token }} + run: | + echo "🚀 Creating release for PR #${{ steps.verify.outputs.pr_number }}..." + bumper -P + echo "bumped=$(jq '.bumped' out.json)" >> $GITHUB_OUTPUT + echo "bump_version=$(jq '.bump_version' out.json)" >> $GITHUB_OUTPUT + echo "bump_sha=$(jq '.bump_sha' out.json)" >> $GITHUB_OUTPUT + + VERSION=$(jq -r '.bump_version' out.json) + BUMPED=$(jq -r '.bumped' out.json) + + if [ "$BUMPED" = "true" ]; then + PR_INFO=$(gh api repos/${{ github.repository }}/pulls/${{ steps.verify.outputs.pr_number }} --jq '{title: .title, user: .user.login, html_url: .html_url}') + PR_TITLE=$(echo "$PR_INFO" | jq -r '.title') + PR_AUTHOR=$(echo "$PR_INFO" | jq -r '.user') + PR_URL=$(echo "$PR_INFO" | jq -r '.html_url') + + cat >> $GITHUB_STEP_SUMMARY << EOF + ## 🚀 Release $VERSION + + | | | + |---|---| + | **Version** | \`$VERSION\` | + | **PR** | [#${{ steps.verify.outputs.pr_number }}]($PR_URL) | + | **Title** | $PR_TITLE | + | **Author** | @$PR_AUTHOR | + | **Commit** | \`${{ github.sha }}\` | + + ### Release Assets + - 📦 [View Release](https://github.com/${{ github.repository }}/releases/tag/$VERSION) + - 🏷️ Tag: \`$VERSION\` + EOF + fi