Skip to content

Auto Merge main -> develop (UTC 04/12/20) #378

Auto Merge main -> develop (UTC 04/12/20)

Auto Merge main -> develop (UTC 04/12/20) #378

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"