Auto Merge main -> develop (UTC 04/12/20) #454
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Auto Merge main -> develop (UTC 04/12/20) | |
| on: | |
| schedule: | |
| - cron: "0 4 * * *" # 04:00 UTC | |
| - cron: "0 12 * * *" # 12:00 UTC | |
| - cron: "0 20 * * *" # 20:00 UTC | |
| workflow_dispatch: {} | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| # Optional: flip to "1" to turn on wire-level Git logs when debugging | |
| env: | |
| GIT_TRACE: "0" | |
| GIT_CURL_VERBOSE: "0" | |
| concurrency: | |
| group: sync-main-to-develop | |
| cancel-in-progress: false | |
| jobs: | |
| sync: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Print basic context | |
| run: | | |
| echo "Actor: $GITHUB_ACTOR" | |
| echo "Repo : $GITHUB_REPOSITORY" | |
| echo "SHA : $GITHUB_SHA" | |
| echo "When : $(date -u '+%Y-%m-%d %H:%M:%S UTC')" | |
| { | |
| echo "## Scheduled sync run" | |
| echo "- Actor: \`$GITHUB_ACTOR\`" | |
| echo "- When (UTC): $(date -u '+%Y-%m-%d %H:%M:%S')" | |
| echo "- Runner: $(uname -a)" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| - name: Check develop branch exists | |
| id: check_develop | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| REPO_URL="https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" | |
| if git ls-remote --heads "$REPO_URL" develop | grep develop >/dev/null; then | |
| echo "develop branch exists on remote." | |
| echo "exists=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "develop branch does NOT exist on remote. Skipping sync." | |
| echo "exists=false" >> "$GITHUB_OUTPUT" | |
| { | |
| echo "### Outcome" | |
| echo "- develop branch present: **false**" | |
| echo "- Action: skipped scheduled sync (no develop branch)." | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| - name: Checkout develop | |
| if: steps.check_develop.outputs.exists == 'true' | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: develop | |
| fetch-depth: 0 | |
| # If you use a bot PAT to bypass branch protection, switch to: | |
| # token: ${{ secrets.BOT_PAT }} | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Show remotes and branches | |
| if: steps.check_develop.outputs.exists == 'true' | |
| run: | | |
| git remote -v | |
| echo | |
| git fetch --prune origin | |
| echo "Remote branches:" | |
| git branch -r | |
| echo | |
| echo "HEAD : $(git rev-parse --short HEAD)" | |
| echo "origin/develop : $(git rev-parse --short origin/develop)" | |
| echo "origin/main : $(git rev-parse --short origin/main)" | |
| - name: Ahead/behind summary (before) | |
| if: steps.check_develop.outputs.exists == 'true' | |
| id: ab | |
| run: | | |
| set -euo pipefail | |
| git checkout develop | |
| git reset --hard origin/develop | |
| # A = commits in main not in develop; B = commits in develop not in main | |
| read A B < <(git rev-list --left-right --count origin/main...origin/develop | awk '{print $1" "$2}') | |
| echo "main ahead of develop by: $A, develop ahead of main by: $B" | |
| echo "ahead_main=$A" >> "$GITHUB_OUTPUT" | |
| echo "ahead_develop=$B" >> "$GITHUB_OUTPUT" | |
| { | |
| echo "### Before merge" | |
| echo "- main ahead of develop: **$A**" | |
| echo "- develop ahead of main: **$B**" | |
| echo | |
| echo "<details><summary>Last 5 on main</summary>" | |
| git log --oneline --decorate=no -n 5 origin/main | |
| echo "</details>" | |
| echo "<details><summary>Last 5 on develop</summary>" | |
| git log --oneline --decorate=no -n 5 origin/develop | |
| echo "</details>" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| - name: Check if develop already contains main | |
| if: steps.check_develop.outputs.exists == 'true' | |
| id: up | |
| run: | | |
| if git merge-base --is-ancestor origin/main HEAD; then | |
| echo "up_to_date=true" >> "$GITHUB_OUTPUT" | |
| echo "Develop already contains main. No merge needed." | |
| else | |
| echo "up_to_date=false" >> "$GITHUB_OUTPUT" | |
| echo "Develop does not contain main yet. Will merge." | |
| fi | |
| - name: Merge main into develop | |
| if: steps.check_develop.outputs.exists == 'true' && steps.up.outputs.up_to_date == 'false' | |
| id: merge | |
| run: | | |
| set -e | |
| echo "Attempting merge origin/main -> develop..." | |
| if git merge -m "chore: scheduled sync main -> develop" origin/main; then | |
| echo "conflicted=false" >> "$GITHUB_OUTPUT" | |
| echo "Merge done (maybe fast-forward)." | |
| else | |
| echo "conflicted=true" >> "$GITHUB_OUTPUT" | |
| echo "::group::Conflict details" | |
| git status --porcelain | |
| echo | |
| echo "Conflicted files:" | |
| git diff --name-only --diff-filter=U || true | |
| echo "::endgroup::" | |
| git merge --abort || true | |
| fi | |
| echo "Post-merge HEAD: $(git rev-parse --short HEAD)" | |
| - name: Diff summary after merge | |
| if: steps.check_develop.outputs.exists == 'true' && steps.up.outputs.up_to_date == 'false' && steps.merge.outputs.conflicted == 'false' | |
| run: | | |
| echo "Files changed (first 100):" | |
| git diff --name-status ORIG_HEAD..HEAD | head -n 100 || true | |
| { | |
| echo "### After merge" | |
| echo "- New HEAD: \`$(git rev-parse --short HEAD)\`" | |
| echo "<details><summary>Files changed (first 100)</summary>" | |
| git diff --name-status ORIG_HEAD..HEAD | head -n 100 || true | |
| echo "</details>" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| - name: Push to develop (direct) | |
| if: steps.check_develop.outputs.exists == 'true' && steps.up.outputs.up_to_date == 'false' && steps.merge.outputs.conflicted == 'false' | |
| id: push | |
| shell: bash | |
| run: | | |
| set +e | |
| echo "Pushing to origin/develop..." | |
| git push origin HEAD:develop | |
| status=$? | |
| echo "exit_code=$status" >> "$GITHUB_OUTPUT" | |
| if [ $status -eq 0 ]; then | |
| echo "Push OK." | |
| else | |
| echo "Push failed (exit $status). This may be due to branch protection; will open a PR instead." | |
| fi | |
| - name: Open PR main -> develop (on conflict OR push failure) | |
| if: steps.check_develop.outputs.exists == 'true' && steps.up.outputs.up_to_date == 'false' && (steps.merge.outputs.conflicted == 'true' || steps.push.outputs.exit_code != '0') | |
| id: pr | |
| shell: bash | |
| run: | | |
| PR_BODY=$(cat <<'EOF' | |
| Automated sync needs review: | |
| - Merge conflicted: ${{ steps.merge.outputs.conflicted == 'true' }} | |
| - Direct push exit code: ${{ steps.push.outputs.exit_code || 'n/a' }} | |
| Please resolve and merge to bring `develop` up to date with `main`. | |
| EOF | |
| ) | |
| PR_URL=$(gh pr create \ | |
| --base develop \ | |
| --head main \ | |
| --title "Scheduled sync: main → develop" \ | |
| --body "$PR_BODY" \ | |
| --label "auto-develop-branch-sync") | |
| echo "pr_url=$PR_URL" >> "$GITHUB_OUTPUT" | |
| echo "Created PR: $PR_URL" | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Enable auto-merge with merge commit | |
| if: steps.check_develop.outputs.exists == 'true' && steps.up.outputs.up_to_date == 'false' && (steps.merge.outputs.conflicted == 'true' || steps.push.outputs.exit_code != '0') | |
| continue-on-error: true | |
| run: | | |
| gh pr merge ${{ steps.pr.outputs.pr_url }} --merge --auto | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Send Slack notification | |
| if: steps.check_develop.outputs.exists == 'true' && steps.up.outputs.up_to_date == 'false' && (steps.merge.outputs.conflicted == 'true' || steps.push.outputs.exit_code != '0') | |
| uses: slackapi/slack-github-action@v2 | |
| with: | |
| webhook-type: incoming-webhook | |
| payload: | | |
| { | |
| "text": "<!channel> 🔄 Sync PR Created: main → develop", | |
| "blocks": [ | |
| { | |
| "type": "section", | |
| "text": { | |
| "type": "mrkdwn", | |
| "text": "<!channel> 🔄 *Scheduled sync PR created*\n\n<${{ steps.pr.outputs.pr_url }}|View Pull Request>\n\n• Merge conflicted: `${{ steps.merge.outputs.conflicted == 'true' }}`\n• Direct push failed: `${{ steps.push.outputs.exit_code != '0' }}`" | |
| } | |
| } | |
| ] | |
| } | |
| env: | |
| SLACK_WEBHOOK_URL: ${{ secrets.SLACK_MERGE_NOTIFICATION_URL }} | |
| - name: Final summary | |
| if: steps.check_develop.outputs.exists == 'true' | |
| run: | | |
| { | |
| echo "### Outcome" | |
| echo "- Up to date before merge: **${{ steps.up.outputs.up_to_date || 'n/a' }}**" | |
| echo "- Merge conflicted: **${{ steps.merge.outputs.conflicted || 'n/a' }}**" | |
| echo "- Push exit code: **${{ steps.push.outputs.exit_code || 'n/a' }}**" | |
| } >> "$GITHUB_STEP_SUMMARY" |