-
Notifications
You must be signed in to change notification settings - Fork 0
221 lines (193 loc) · 9.78 KB
/
release.yml
File metadata and controls
221 lines (193 loc) · 9.78 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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
name: Release
on:
workflow_run:
workflows: ["Verify"]
types: [completed]
branches: [main]
workflow_dispatch:
permissions:
contents: write
packages: write
concurrency:
group: release-main
cancel-in-progress: false
jobs:
release:
if: |
(
github.event_name == 'workflow_dispatch' &&
github.ref == 'refs/heads/main'
) ||
(
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'push' &&
github.event.workflow_run.head_branch == 'main'
)
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout main
uses: actions/checkout@v6
with:
ref: main
fetch-depth: 0
- name: Determine release eligibility
id: freshness
env:
EVENT_NAME: ${{ github.event_name }}
WORKFLOW_HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
run: |
if [ "$EVENT_NAME" = "workflow_run" ]; then
CURRENT_MAIN_SHA="$(git rev-parse origin/main)"
if [ "$CURRENT_MAIN_SHA" != "$WORKFLOW_HEAD_SHA" ]; then
echo "should_release=false" >> "$GITHUB_OUTPUT"
echo "Skipping release: verify run is not for the latest main commit."
echo "Current main: $CURRENT_MAIN_SHA"
echo "Verify head: $WORKFLOW_HEAD_SHA"
exit 0
fi
fi
echo "should_release=true" >> "$GITHUB_OUTPUT"
- name: Setup Node
if: steps.freshness.outputs.should_release == 'true'
uses: actions/setup-node@v6
with:
node-version: 20
- name: Detect release version
if: steps.freshness.outputs.should_release == 'true'
id: version
run: |
CURRENT_VERSION="$(node -p "require('./package.json').version || ''")"
if [ -z "$CURRENT_VERSION" ]; then
echo "Missing package.json version"
exit 1
fi
if git cat-file -e HEAD^:package.json 2>/dev/null; then
PREVIOUS_VERSION="$(
git show HEAD^:package.json | node -e "const fs=require('node:fs');const raw=fs.readFileSync(0,'utf8');try{process.stdout.write(JSON.parse(raw).version||'')}catch{process.stdout.write('')}"
)"
else
PREVIOUS_VERSION="0.0.0"
fi
if [ -z "$PREVIOUS_VERSION" ]; then
PREVIOUS_VERSION="0.0.0"
fi
VERSION_RELATION="$(
node -e "const parse=(v)=>{const m=/^(\\d+)\\.(\\d+)\\.(\\d+)$/.exec(v||'');return m?m.slice(1).map(Number):null};const cmp=(a,b)=>a[0]!==b[0]?Math.sign(a[0]-b[0]):a[1]!==b[1]?Math.sign(a[1]-b[1]):Math.sign(a[2]-b[2]);const current=parse(process.argv[1]);const previous=parse(process.argv[2]);if(!current||!previous){process.stdout.write('invalid');process.exit(0)}const value=cmp(current,previous);process.stdout.write(value>0?'gt':value<0?'lt':'eq')" "$CURRENT_VERSION" "$PREVIOUS_VERSION"
)"
if [ "$VERSION_RELATION" = "invalid" ]; then
echo "Invalid semantic version. Current: $CURRENT_VERSION Previous: $PREVIOUS_VERSION"
exit 1
fi
echo "version=$CURRENT_VERSION" >> "$GITHUB_OUTPUT"
echo "previous_version=$PREVIOUS_VERSION" >> "$GITHUB_OUTPUT"
echo "tag=v$CURRENT_VERSION" >> "$GITHUB_OUTPUT"
echo "version_relation=$VERSION_RELATION" >> "$GITHUB_OUTPUT"
if [ "$VERSION_RELATION" = "gt" ]; then
echo "has_version_bump=true" >> "$GITHUB_OUTPUT"
echo "Detected release version bump: $PREVIOUS_VERSION -> $CURRENT_VERSION"
else
echo "has_version_bump=false" >> "$GITHUB_OUTPUT"
fi
if [ "$CURRENT_VERSION" = "0.0.0" ] || [ "$VERSION_RELATION" = "lt" ]; then
echo "can_publish_version=false" >> "$GITHUB_OUTPUT"
echo "No release candidate. Current version: $CURRENT_VERSION Previous version: $PREVIOUS_VERSION"
else
echo "can_publish_version=true" >> "$GITHUB_OUTPUT"
if [ "$VERSION_RELATION" = "eq" ]; then
echo "Version unchanged ($CURRENT_VERSION). CI will publish only if tag is still missing."
fi
fi
- name: Check tag availability
if: steps.freshness.outputs.should_release == 'true' && steps.version.outputs.can_publish_version == 'true'
id: tag
env:
TAG: ${{ steps.version.outputs.tag }}
run: |
git fetch --tags --force
if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
echo "tag_available=false" >> "$GITHUB_OUTPUT"
echo "Tag $TAG already exists; skipping release."
else
echo "tag_available=true" >> "$GITHUB_OUTPUT"
fi
- name: Configure git identity
if: steps.freshness.outputs.should_release == 'true' && steps.version.outputs.can_publish_version == 'true' && steps.tag.outputs.tag_available == 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Determine image repository
if: steps.freshness.outputs.should_release == 'true' && steps.version.outputs.can_publish_version == 'true' && steps.tag.outputs.tag_available == 'true'
id: image
run: |
IMAGE_REPO="ghcr.io/$(echo "$GITHUB_REPOSITORY" | tr '[:upper:]' '[:lower:]')"
echo "repo=$IMAGE_REPO" >> "$GITHUB_OUTPUT"
- name: Set up Docker Buildx
if: steps.freshness.outputs.should_release == 'true' && steps.version.outputs.can_publish_version == 'true' && steps.tag.outputs.tag_available == 'true'
uses: docker/setup-buildx-action@v4
- name: Log in to GHCR
if: steps.freshness.outputs.should_release == 'true' && steps.version.outputs.can_publish_version == 'true' && steps.tag.outputs.tag_available == 'true'
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push app image
if: steps.freshness.outputs.should_release == 'true' && steps.version.outputs.can_publish_version == 'true' && steps.tag.outputs.tag_available == 'true'
uses: docker/build-push-action@v7
with:
context: .
file: ./docker/app.Dockerfile
push: true
tags: |
${{ steps.image.outputs.repo }}:${{ steps.version.outputs.tag }}
${{ steps.image.outputs.repo }}:${{ steps.version.outputs.version }}
${{ steps.image.outputs.repo }}:latest
build-args: |
NEXT_PUBLIC_API_URL=
NEXT_PUBLIC_SHIPMENT_BASE_FEE_CENTS=250
NEXT_PUBLIC_SHIPMENT_FEE_PER_UNIT_CENTS=15
- name: Smoke check release image endpoints
if: steps.freshness.outputs.should_release == 'true' && steps.version.outputs.can_publish_version == 'true' && steps.tag.outputs.tag_available == 'true'
env:
APP_IMAGE_REPO: ${{ steps.image.outputs.repo }}
APP_IMAGE_TAG: ${{ steps.version.outputs.version }}
COMPOSE_NETWORK_NAME: corpsim-release-smoke-${{ github.run_id }}
BETTER_AUTH_SECRET: release-smoke-only-secret-do-not-use
BETTER_AUTH_URL: http://127.0.0.1:4310
run: |
docker compose -f docker-compose.preview.yml down -v --remove-orphans || true
docker compose -f docker-compose.preview.yml up -d frontend
for i in $(seq 1 90); do
if curl -fsS "http://127.0.0.1:4311/meta/version" >/tmp/meta-version.json; then
break
fi
sleep 2
done
curl -fsS "http://127.0.0.1:4311/meta/version" | node -e "let s='';process.stdin.on('data',d=>s+=d);process.stdin.on('end',()=>{const j=JSON.parse(s);if(typeof j.version!=='string'||j.version.trim().length===0){process.exit(1)}})"
curl -fsS "http://127.0.0.1:4311/health/maintenance" | node -e "let s='';process.stdin.on('data',d=>s+=d);process.stdin.on('end',()=>{const j=JSON.parse(s);if(typeof j.enabled!=='boolean'){process.exit(1)}})"
curl -fsS "http://127.0.0.1:4311/v1/world/health" | node -e "let s='';process.stdin.on('data',d=>s+=d);process.stdin.on('end',()=>{const j=JSON.parse(s);if(typeof j.currentTick!=='number'){process.exit(1)}})"
- name: Cleanup smoke test stack
if: always() && steps.freshness.outputs.should_release == 'true' && steps.version.outputs.can_publish_version == 'true' && steps.tag.outputs.tag_available == 'true'
env:
APP_IMAGE_REPO: ${{ steps.image.outputs.repo }}
APP_IMAGE_TAG: ${{ steps.version.outputs.version }}
COMPOSE_NETWORK_NAME: corpsim-release-smoke-${{ github.run_id }}
run: docker compose -f docker-compose.preview.yml down -v --remove-orphans
- name: Create annotated tag
if: steps.freshness.outputs.should_release == 'true' && steps.version.outputs.can_publish_version == 'true' && steps.tag.outputs.tag_available == 'true'
env:
TAG: ${{ steps.version.outputs.tag }}
run: git tag -a "$TAG" -m "Release $TAG"
- name: Push release tag
if: steps.freshness.outputs.should_release == 'true' && steps.version.outputs.can_publish_version == 'true' && steps.tag.outputs.tag_available == 'true'
env:
TAG: ${{ steps.version.outputs.tag }}
run: git push origin "$TAG"
- name: Publish GitHub release
if: steps.freshness.outputs.should_release == 'true' && steps.version.outputs.can_publish_version == 'true' && steps.tag.outputs.tag_available == 'true'
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.version.outputs.tag }}
name: CorpSim ${{ steps.version.outputs.tag }}
generate_release_notes: true