Repository navigation
97 lines (90 loc) · 4.14 KB
/
Copy pathpromote.yml
File metadata and controls
97 lines (90 loc) · 4.14 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
name: promote
on:
workflow_dispatch:
inputs:
pull_request:
description: Open pull request from dev into main whose exact head passed quality
required: true
type: number
permissions:
contents: read
concurrency:
group: promote-main
cancel-in-progress: false
jobs:
promote:
name: Fast-forward main to validated dev
runs-on: ubuntu-24.04
timeout-minutes: 10
permissions:
actions: read
contents: write
pull-requests: read
steps:
- name: Verify exact validated promotion
id: promotion
env:
GH_TOKEN: ${{ github.token }}
PULL_REQUEST: ${{ inputs.pull_request }}
run: |
set -euo pipefail
fail() { echo "::error::$1"; exit 1; }
[[ "$GITHUB_REF" == refs/heads/dev ]] || fail "Run promote from dev: gh workflow run promote.yml --ref dev -f pull_request=$PULL_REQUEST."
pr="$(gh api "repos/$GITHUB_REPOSITORY/pulls/$PULL_REQUEST")"
jq -e --arg repo "$GITHUB_REPOSITORY" \
'.state == "open" and .base.ref == "main" and .head.ref == "dev" and .head.repo.full_name == $repo' \
<<<"$pr" >/dev/null || fail "Pull request #$PULL_REQUEST is not an open promotion from dev into main."
head="$(jq -r .head.sha <<<"$pr")"
dev="$(gh api "repos/$GITHUB_REPOSITORY/branches/dev" --jq .commit.sha)"
main="$(gh api "repos/$GITHUB_REPOSITORY/branches/main" --jq .commit.sha)"
[[ "$head" == "$dev" ]] || fail "dev moved to $dev after pull request head $head. Wait for checks on the new head."
status="$(gh api "repos/$GITHUB_REPOSITORY/compare/$main...$head" --jq .status)"
[[ "$status" == ahead ]] || fail "main $main cannot fast-forward to $head (comparison: $status)."
run="$(gh api "repos/$GITHUB_REPOSITORY/actions/workflows/ci.yml/runs?event=pull_request&head_sha=$head&per_page=100" |
jq -c --argjson pr "$PULL_REQUEST" \
'[.workflow_runs[] | select(any(.pull_requests[]; .number == $pr and .base.ref == "main"))] | max_by(.run_number) // {}')"
url="$(jq -r '.html_url // "none"' <<<"$run")"
jq -e '.status == "completed" and .conclusion == "success"' <<<"$run" >/dev/null ||
fail "quality has not passed for pull request #$PULL_REQUEST on exact head $head (latest run: $url)."
{
echo "head=$head"
echo "main=$main"
echo "run=$url"
} >> "$GITHUB_OUTPUT"
- name: Check out exact promotion history
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
ref: ${{ steps.promotion.outputs.head }}
- name: Fast-forward main to the validated commit
env:
HEAD_SHA: ${{ steps.promotion.outputs.head }}
# A plain push refuses anything but a fast-forward; branch protection still applies.
run: git push origin "$HEAD_SHA:refs/heads/main"
- name: Confirm promotion
env:
GH_TOKEN: ${{ github.token }}
HEAD_SHA: ${{ steps.promotion.outputs.head }}
PREVIOUS_MAIN: ${{ steps.promotion.outputs.main }}
PULL_REQUEST: ${{ inputs.pull_request }}
VALIDATION_RUN: ${{ steps.promotion.outputs.run }}
run: |
set -euo pipefail
main="$(gh api "repos/$GITHUB_REPOSITORY/branches/main" --jq .commit.sha)"
if [[ "$main" != "$HEAD_SHA" ]]; then
echo "::error::main is $main, expected $HEAD_SHA."
exit 1
fi
merged=false
for _ in 1 2 3 4 5 6; do
merged="$(gh api "repos/$GITHUB_REPOSITORY/pulls/$PULL_REQUEST" --jq .merged)"
[[ "$merged" != true ]] || break
sleep 5
done
[[ "$merged" == true ]] || echo "::warning::GitHub has not yet marked pull request #$PULL_REQUEST as merged."
{
echo "## Promotion"
echo
echo "main moved from \`$PREVIOUS_MAIN\` to \`$HEAD_SHA\`, the exact head validated by $VALIDATION_RUN."
echo "Pull request #$PULL_REQUEST merged: $merged."
} >> "$GITHUB_STEP_SUMMARY"