-
Notifications
You must be signed in to change notification settings - Fork 7
149 lines (137 loc) · 5.9 KB
/
Copy pathrelease.yml
File metadata and controls
149 lines (137 loc) · 5.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
name: Release (Merge dev to main)
on:
workflow_dispatch:
inputs:
dry_run:
description: 'Dry run - validate only, do not merge'
required: false
type: boolean
default: false
jobs:
release:
name: Merge dev to main
runs-on: self-hosted
concurrency:
group: release
cancel-in-progress: false
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Fetch latest branches
run: |
git fetch origin main dev
- name: Validate dev is ahead of main
run: |
COMMITS=$(git log origin/main..origin/dev --oneline)
if [ -z "$COMMITS" ]; then
echo "::error::Nothing to release — dev has no commits ahead of main"
exit 1
fi
COMMIT_COUNT=$(echo "$COMMITS" | wc -l | tr -d ' ')
echo "Found $COMMIT_COUNT commit(s) to release:"
echo "$COMMITS"
echo "COMMITS<<EOF" >> "$GITHUB_ENV"
echo "$COMMITS" >> "$GITHUB_ENV"
echo "EOF" >> "$GITHUB_ENV"
echo "COMMIT_COUNT=$COMMIT_COUNT" >> "$GITHUB_ENV"
- name: Write step summary (pre-merge)
run: |
echo "## 🚀 Release Summary" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "**Mode:** ${{ inputs.dry_run && 'Dry run (validation only)' || 'Live merge' }}" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "### Commits to be released ($COMMIT_COUNT):" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"
echo "$COMMITS" >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"
- name: Dry run - show diff summary
if: ${{ inputs.dry_run }}
run: |
echo "=== DRY RUN MODE — No changes will be made ==="
echo ""
echo "Diff summary (dev vs main):"
git diff --stat origin/main..origin/dev
echo ""
echo "=== Dry run complete. Re-run without dry_run to perform the actual release. ==="
- name: Check for existing release PR
if: ${{ !inputs.dry_run }}
run: |
EXISTING_PR=$(gh pr list --base main --head dev --json number --jq '.[0].number')
if [ -n "$EXISTING_PR" ]; then
echo "::error::An open PR from dev to main already exists (#$EXISTING_PR). Close or merge it first."
exit 1
fi
env:
GH_TOKEN: ${{ secrets.GHCR_PAT }}
- name: Create release PR
if: ${{ !inputs.dry_run }}
run: |
# Build the PR body via a file using SHELL env vars ($COMMITS), NOT
# GitHub Actions expression interpolation, so a commit message that
# contains a double-quote or backtick is not pasted raw into the gh
# command (which previously aborted the release with "unknown argument").
BODY_FILE="$(mktemp)"
{
echo "Automated release PR created by the release workflow."
echo ""
echo "### Commits ($COMMIT_COUNT):"
echo '```'
echo "$COMMITS"
echo '```'
} > "$BODY_FILE"
PR_URL=$(gh pr create --base main --head dev \
--title "chore: release - merge dev into main" \
--body-file "$BODY_FILE")
echo "Created PR: $PR_URL"
PR_NUMBER=$(echo "$PR_URL" | grep -o '[0-9]*$')
echo "PR_NUMBER=$PR_NUMBER" >> "$GITHUB_ENV"
echo "PR_URL=$PR_URL" >> "$GITHUB_ENV"
env:
GH_TOKEN: ${{ secrets.GHCR_PAT }}
- name: Enable auto-merge
if: ${{ !inputs.dry_run }}
run: |
# GitHub rejects enablePullRequestAutoMerge with "Pull request is in
# unstable status" for a few seconds after a PR is created (its checks /
# merge-state aren't registered yet) — and `gh pr merge --auto` can even
# exit 0 without enabling while the state is UNKNOWN. So retry AND verify
# that auto-merge actually stuck, rather than trusting the exit code.
MAX_ATTEMPTS=12
RETRY_DELAY_SECONDS=6
attempt=1
while [ "$attempt" -le "$MAX_ATTEMPTS" ]; do
gh pr merge "$PR_NUMBER" --auto --merge || true
enabled=$(gh pr view "$PR_NUMBER" --json autoMergeRequest --jq '.autoMergeRequest != null')
if [ "$enabled" = "true" ]; then
echo "Auto-merge enabled on PR #$PR_NUMBER. It will merge once required checks pass."
exit 0
fi
echo "Auto-merge not registered yet (attempt $attempt/$MAX_ATTEMPTS) — retrying in ${RETRY_DELAY_SECONDS}s..."
sleep "$RETRY_DELAY_SECONDS"
attempt=$((attempt + 1))
done
echo "::error::Failed to enable auto-merge on PR #$PR_NUMBER after $MAX_ATTEMPTS attempts."
exit 1
env:
GH_TOKEN: ${{ secrets.GHCR_PAT }}
- name: Write step summary (post-create)
if: ${{ !inputs.dry_run }}
run: |
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "### ✅ Release PR created!" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "PR [#${{ env.PR_NUMBER }}](${{ env.PR_URL }}) has been created with auto-merge enabled." >> "$GITHUB_STEP_SUMMARY"
echo "It will merge into main automatically once required status checks pass." >> "$GITHUB_STEP_SUMMARY"
- name: Write step summary (dry run complete)
if: ${{ inputs.dry_run }}
run: |
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "### ✅ Dry run complete — validation passed!" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "Re-run this workflow with **dry_run = false** to perform the actual release." >> "$GITHUB_STEP_SUMMARY"