Repository navigation
154 lines (144 loc) · 5.94 KB
/
Copy pathrelease.yml
File metadata and controls
154 lines (144 loc) · 5.94 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
150
151
152
153
154
name: Release
on:
pull_request:
branches: [production]
types: [closed]
jobs:
release:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: read
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
ref: production
- name: Get latest tag
id: latest_tag
run: |
TAG=$(git tag -l "v*" --sort=-v:refname | head -n1)
echo "tag=${TAG:-v0.0.0}" >> "$GITHUB_OUTPUT"
- name: Determine next version
id: next_version
env:
LABELS: ${{ join(github.event.pull_request.labels.*.name, ',') }}
CURRENT: ${{ steps.latest_tag.outputs.tag }}
run: |
VERSION="${CURRENT#v}"
IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"
if echo "$LABELS" | grep -q "release:major"; then
MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0
elif echo "$LABELS" | grep -q "release:minor"; then
MINOR=$((MINOR + 1)); PATCH=0
else
PATCH=$((PATCH + 1))
fi
NEXT="v${MAJOR}.${MINOR}.${PATCH}"
echo "version=$NEXT" >> "$GITHUB_OUTPUT"
echo "Next version: $NEXT"
- name: Generate changelog
env:
PR_TITLE: ${{ github.event.pull_request.title }}
PR_BODY: ${{ github.event.pull_request.body }}
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_URL: ${{ github.event.pull_request.html_url }}
PREV_TAG: ${{ steps.latest_tag.outputs.tag }}
NEXT_VERSION: ${{ steps.next_version.outputs.version }}
run: |
{
echo "## $PR_TITLE (#$PR_NUMBER)"
echo ""
echo "$PR_BODY" | head -100
echo ""
echo "---"
echo "**Full diff:** [$PREV_TAG...$NEXT_VERSION]($PR_URL)"
} > release-notes.md
- name: Create release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release create "${{ steps.next_version.outputs.version }}" \
--target production \
--title "${{ steps.next_version.outputs.version }}" \
--notes-file release-notes.md
smoke-test:
needs: release
runs-on: ubuntu-latest
env:
# Set the SMOKE_BASE_URL repo variable to check another host.
BASE_URL: ${{ vars.SMOKE_BASE_URL || 'https://tools.codercops.com' }}
EXPECTED_SHA: ${{ github.event.pull_request.merge_commit_sha }}
steps:
# Bot Fight Mode on codercops.com answers GitHub's runners with a
# Cloudflare challenge (403 with cf-mitigated: challenge) on HTML pages,
# but not on static files like version.txt. So the deploy is checked
# through version.txt, and a page that gets a challenge is reported with
# a notice instead of failing the release (CI already requested every
# page from the built Worker). Any other status still fails.
- name: Wait for the Cloudflare deploy of this merge
run: |
# Workers Builds deploys production on its own. version.txt carries
# the commit it built, so poll until it matches (up to 15 minutes).
LIVE=""
for i in $(seq 1 90); do
read -r STATUS MITIGATED < <(curl -s -o version.out -w "%{http_code} %header{cf-mitigated}\n" --max-time 10 "$BASE_URL/version.txt")
if [ "$STATUS" = "403" ] && [ "$MITIGATED" = "challenge" ]; then
echo "::notice::Cloudflare challenged this runner on $BASE_URL/version.txt, so the deploy wasn't checked from here. Check the live site by hand."
exit 0
fi
LIVE=$(tr -d '[:space:]' < version.out 2>/dev/null || true)
if [ "$STATUS" = "200" ] && [ "$LIVE" = "$EXPECTED_SHA" ]; then
echo "Live on $LIVE"
exit 0
fi
sleep 10
done
echo "::error::$BASE_URL serves ${LIVE:-nothing}, expected $EXPECTED_SHA"
exit 1
- name: Pages reachable
run: |
fail=0
challenged=""
for path in / /json-formatter /sitemap.xml; do
read -r STATUS MITIGATED < <(curl -sL -o /dev/null -w "%{http_code} %header{cf-mitigated}\n" --max-time 15 "$BASE_URL$path")
if [ "$STATUS" = "200" ]; then
echo "200 $path"
elif [ "$STATUS" = "403" ] && [ "$MITIGATED" = "challenge" ]; then
echo "challenged $path"
challenged="$challenged $path"
else
echo "::error::$path returned status $STATUS"
fail=1
fi
done
if [ -n "$challenged" ]; then
echo "::notice::Cloudflare challenged this runner on$challenged, so those pages weren't checked from here. Check them by hand."
fi
exit "$fail"
sync-develop:
needs: [release, smoke-test]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
ref: develop
# develop requires PRs, so the default token cannot push to it. Use a
# maintainer PAT in RELEASE_TOKEN (a bypass actor) for the fast-forward.
# Without it this step warns and you sync develop manually.
token: ${{ secrets.RELEASE_TOKEN || secrets.GITHUB_TOKEN }}
- name: Sync develop with production
run: |
git fetch origin production
# A release PR is a merge commit, so develop should fast-forward.
git merge --ff-only origin/production || {
echo "::warning::develop did not fast-forward; leaving it unchanged."
exit 0
}
if ! git push origin develop; then
echo "::warning::Could not push develop (branch protection). Add a RELEASE_TOKEN secret with a bypass-capable PAT, or sync manually: git checkout develop && git merge --ff-only origin/production && git push origin develop"
fi