Docs: add section on overriding config options with environment variables #31
Workflow file for this run
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: Sync main → develop | |
on: | |
pull_request: | |
types: [closed] | |
branches: [main] | |
workflow_dispatch: | |
inputs: | |
head_branch: | |
description: "Branch to merge FROM (default: main)." | |
required: false | |
default: "main" | |
base_branch: | |
description: "Branch to merge INTO (default: develop)." | |
required: false | |
default: "develop" | |
source_pr_number: | |
description: "If testing, the PR number to comment on (optional)." | |
required: false | |
test_mode: | |
description: "Bypass PR/push guards for manual testing" | |
required: false | |
default: "true" | |
permissions: | |
contents: write | |
pull-requests: write | |
issues: write | |
concurrency: | |
group: sync-main-into-develop | |
cancel-in-progress: false | |
jobs: | |
open-sync-pr: | |
if: | | |
github.actor != 'github-actions[bot]' && ( | |
( | |
github.event_name == 'pull_request' && github.event.pull_request.merged == true | |
) || ( | |
github.event_name == 'workflow_dispatch' && (inputs.test_mode == 'true') | |
) | |
) | |
runs-on: ubuntu-latest | |
env: | |
# Use inputs for dispatch (testing), defaults for normal triggers | |
HEAD_BRANCH: ${{ (github.event_name == 'workflow_dispatch' && inputs.head_branch) || 'main' }} | |
BASE_BRANCH: ${{ (github.event_name == 'workflow_dispatch' && inputs.base_branch) || 'develop' }} | |
SOURCE_PR: ${{ (github.event_name == 'pull_request' && github.event.pull_request.number) || inputs.source_pr_number || '' }} | |
GH_TOKEN: ${{ secrets.GH_PAT }} | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 | |
token: ${{ secrets.GH_PAT }} | |
- name: Configure git author | |
run: | | |
git config --local user.name "Apollo Bot" | |
git config --local user.email "[email protected]" | |
# Generate branch name from PR# when available, otherwise use first 7 commit SHA characters | |
- name: Compute branch/name metadata | |
id: meta | |
run: | | |
pr=${{ github.event.pull_request.number }} | |
echo "sync_branch=sync/main-into-develop-pr-${pr}" >> $GITHUB_OUTPUT | |
echo "sync_title=Sync main → develop (PR #${pr})" >> $GITHUB_OUTPUT | |
echo "sync_body=Auto-opened after merging \`${{ github.event.pull_request.head.ref }}\` into \`main\`. Source PR: #${pr}." >> $GITHUB_OUTPUT | |
echo "conflict_branch=conflict/main-into-develop-pr-${pr}" >> $GITHUB_OUTPUT | |
echo "conflict_title=Sync main → develop (resolve conflicts)" >> $GITHUB_OUTPUT | |
echo "conflict_body=Opened from a copy of \`main\` so conflicts can be resolved without pushing to a protected branch." >> $GITHUB_OUTPUT | |
# Short-lived sync branch from develop and merge main into it (do NOT rebase) | |
# use +e to stop errors from short-circuiting the script | |
- name: Prepare sync branch | |
id: prep | |
run: | | |
set -e | |
git fetch origin "${BASE_BRANCH}" "${HEAD_BRANCH}" | |
git switch -c "${{ steps.meta.outputs.sync_branch }}" "origin/${BASE_BRANCH}" | |
set +e | |
git merge --no-ff "origin/${HEAD_BRANCH}" | |
rc=$? | |
set -e | |
git add -A || true | |
git commit -m "WIP: merge ${HEAD_BRANCH} into ${BASE_BRANCH} via ${{ steps.meta.outputs.branch }}" || true | |
git push origin HEAD | |
right=$(git rev-list --count --right-only "origin/${BASE_BRANCH}...HEAD") | |
echo "merge_status=$rc" >> "$GITHUB_OUTPUT" | |
echo "sync_right=$right" >> "$GITHUB_OUTPUT" | |
echo "Merge exit=$rc, sync branch ahead-by=$right" | |
# If no merge conflicts and there are changes, open the PR targeting develop | |
- name: Open clean PR to develop | |
id: sync_pr | |
if: ${{ steps.prep.outputs.merge_status == '0' && steps.prep.outputs.sync_right != '0' }} | |
run: | | |
# Avoid duplicate PRs | |
existing=$(gh pr list --base "${BASE_BRANCH}" --head "${{ steps.meta.outputs.sync_branch }}" --state open --json number --jq '.[0].number' || true) | |
if [ -n "$existing" ] && [ "$existing" != "null" ]; then | |
echo "pr_number=$existing" >> "$GITHUB_OUTPUT" | |
url=$(gh pr view "$existing" --json url --jq .url) | |
echo "pr_url=$url" >> "$GITHUB_OUTPUT" | |
exit 0 | |
fi | |
gh pr create \ | |
--base "${BASE_BRANCH}" \ | |
--head "${{ steps.meta.outputs.sync_branch }}" \ | |
--title "${{ steps.meta.outputs.sync_title }}" \ | |
--body "${{ steps.meta.outputs.sync_body }} (created via gh CLI)" \ | |
--label back-merge \ | |
--label automation | |
# Emit outputs for later steps | |
gh pr view --base "${BASE_BRANCH}" --head "${{ steps.meta.outputs.sync_branch }}" \ | |
--json number,url | jq -r '"pr_number=\(.number)\npr_url=\(.url)"' >> "$GITHUB_OUTPUT" | |
# If the merge hit conflicts, open a DIRECT PR: HEAD_BRANCH -> BASE_BRANCH so conflicts can be resolved prior to merge | |
- name: Open conflict PR | |
id: conflict_pr | |
if: ${{ steps.prep.outputs.merge_status != '0' }} | |
run: | | |
set -e | |
git fetch origin "${HEAD_BRANCH}" "${BASE_BRANCH}" | |
git switch -c "${{ steps.meta.outputs.conflict_branch }}" "origin/${HEAD_BRANCH}" | |
git push -u origin HEAD | |
# Skip if no diff between conflict branch and base (should be unlikely) | |
right=$(git rev-list --right-only --count "origin/${BASE_BRANCH}...origin/${{ steps.meta.outputs.conflict_branch }}") | |
if [ "$right" -eq 0 ]; then | |
echo "No diff between ${HEAD_BRANCH} and ${BASE_BRANCH}; nothing to open." | |
exit 0 | |
fi | |
# Reuse existing open PR if present | |
existing=$(gh pr list --base "${BASE_BRANCH}" --head "${{ steps.meta.outputs.conflict_branch }}" --state open --json number --jq '.[0].number' || true) | |
if [ -n "$existing" ] && [ "$existing" != "null" ]; then | |
echo "pr_number=$existing" >> "$GITHUB_OUTPUT" | |
url=$(gh pr view "$existing" --json url --jq .url) | |
echo "pr_url=$url" >> "$GITHUB_OUTPUT" | |
exit 0 | |
fi | |
gh pr create \ | |
--base "${BASE_BRANCH}" \ | |
--head "${{ steps.meta.outputs.conflict_branch }}" \ | |
--title "${{ steps.meta.outputs.conflict_title }}" \ | |
--body "${{ steps.meta.outputs.conflict_body }}" \ | |
--label back-merge \ | |
--label automation \ | |
--label conflicts | |
gh pr view --base "${BASE_BRANCH}" --head "${{ steps.meta.outputs.conflict_branch }}" \ | |
--json number,url | jq -r '"pr_number=\(.number)\npr_url=\(.url)"' >> "$GITHUB_OUTPUT" | |
# Comment back on the ORIGINAL merged PR with a link to the sync PR | |
- name: Comment on source PR with sync PR link | |
if: ${{ env.SOURCE_PR != '' && (steps.sync_pr.outputs.pr_number != '' || steps.conflict_pr.outputs.pr_number != '') }} | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const owner = context.repo.owner; | |
const repo = context.repo.repo; | |
const issue_number = Number(process.env.SOURCE_PR); | |
const hadConflicts = '${{ steps.prep.outputs.merge_status }}' !== '0'; | |
const syncUrl = '${{ steps.sync_pr.outputs.pr_url || steps.conflict_pr.outputs.pr_url }}'; | |
const head = process.env.HEAD_BRANCH; | |
const base = process.env.BASE_BRANCH; | |
const status = hadConflicts ? 'conflicts ❗' : 'clean ✅'; | |
const note = hadConflicts | |
? 'Opened from a copy of main so conflicts can be resolved safely.' | |
: 'Opened from a sync branch created off develop.'; | |
const body = [ | |
`Opened sync PR **${head} → ${base}**: ${syncUrl}`, | |
``, | |
`Merge status: **${status}**`, | |
note | |
].join('\n'); | |
await github.rest.issues.createComment({ owner, repo, issue_number, body }); |