Skip to content

Commit bf92951

Browse files
authored
Merge pull request #277: CI: Separate builds with native runners
2 parents 1ee8914 + 52477eb commit bf92951

File tree

3 files changed

+140
-109
lines changed

3 files changed

+140
-109
lines changed

.github/workflows/ci.yml

Lines changed: 102 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,9 @@ jobs:
1717
- uses: actions/checkout@v6
1818
- uses: nextstrain/.github/actions/shellcheck@master
1919

20-
# Build multi-platform builder and final images with caching from Docker Hub
21-
# and GitHub Container Registry; push to GitHub Container Registry.
22-
build:
20+
vars:
2321
runs-on: ubuntu-latest
2422
steps:
25-
26-
- uses: actions/checkout@v6
27-
28-
- uses: actions/setup-python@v6
29-
with:
30-
python-version: '>=3.8'
31-
3223
- name: Set $CACHE_DATE
3324
run: echo "CACHE_DATE=$(date --utc +%Y%m%dT%H%M%SZ)" | tee -a "$GITHUB_ENV"
3425

@@ -42,7 +33,33 @@ jobs:
4233
# and hyphens.
4334
run: echo "TAG=branch-${GITHUB_REF_NAME//[^A-Za-z0-9._-]/-}" | tee -a "$GITHUB_ENV"
4435

45-
- uses: docker/setup-qemu-action@v3
36+
outputs:
37+
cache-date: ${{ env.CACHE_DATE }}
38+
tag: ${{ env.TAG }}
39+
40+
# Build platform-specific builder and final images with caching from Docker
41+
# Hub and GitHub Container Registry; push to GitHub Container Registry.
42+
build:
43+
name: build (${{ matrix.platform }})
44+
needs: vars
45+
runs-on: ${{ matrix.runner }}
46+
strategy:
47+
matrix:
48+
include:
49+
- platform: linux/amd64
50+
arch: amd64
51+
runner: ubuntu-latest
52+
53+
- platform: linux/arm64
54+
arch: arm64
55+
runner: ubuntu-24.04-arm
56+
steps:
57+
58+
- uses: actions/checkout@v6
59+
60+
- uses: actions/setup-python@v6
61+
with:
62+
python-version: '>=3.8'
4663

4764
# GITHUB_TOKEN is unreliable¹ so use a token from nextstrain-bot.
4865
# ¹ https://github.com/docker/build-push-action/issues/463#issuecomment-939394233
@@ -52,21 +69,29 @@ jobs:
5269
username: nextstrain-bot
5370
password: ${{ secrets.GH_TOKEN_NEXTSTRAIN_BOT_MANAGE_PACKAGES }}
5471

55-
- run: ./devel/build -p linux/amd64,linux/arm64 -r ghcr.io -t "$TAG" -l logs/
72+
- run: |
73+
./devel/build \
74+
-p ${{ matrix.platform }} \
75+
-r ghcr.io \
76+
-t "$TAG-${{ matrix.arch }}" \
77+
-l logs-${{ matrix.arch }}/
78+
env:
79+
TAG: ${{ needs.vars.outputs.tag }}
80+
CACHE_DATE: ${{ needs.vars.outputs.cache-date }}
5681
5782
- if: always()
5883
name: Upload build logs as artifacts
5984
uses: actions/upload-artifact@v6
6085
with:
61-
name: build-logs
62-
path: logs/
86+
name: build-logs-${{ matrix.arch }}
87+
path: logs-${{ matrix.arch }}/
6388

6489
- if: always()
6590
name: Summarize build logs for the GitHub Actions run summary page
6691
run: |
67-
for log in logs/*; do
92+
for log in logs-${{ matrix.arch }}/*; do
6893
{
69-
echo "## $(basename "$log")"
94+
echo "## [${{ matrix.platform }}] $(basename "$log")"
7095
echo ''
7196
echo '```'
7297
./devel/summarize-buildkit-output "$log" 2>&1
@@ -75,19 +100,55 @@ jobs:
75100
} >> "$GITHUB_STEP_SUMMARY"
76101
done
77102
78-
outputs:
79-
tag: ${{ env.TAG }}
103+
# Merge platform-specific images into multi-platform images
104+
merge-builds:
105+
needs: [vars, build]
106+
runs-on: ubuntu-latest
107+
env:
108+
TAG: ${{ needs.vars.outputs.tag }}
109+
steps:
110+
- uses: docker/login-action@v3
111+
with:
112+
registry: ghcr.io
113+
username: nextstrain-bot
114+
password: ${{ secrets.GH_TOKEN_NEXTSTRAIN_BOT_MANAGE_PACKAGES }}
115+
116+
- name: Create base-builder-build-platform image
117+
run: |
118+
docker buildx imagetools create \
119+
-t "ghcr.io/nextstrain/base-builder-build-platform:$TAG" \
120+
"ghcr.io/nextstrain/base-builder-build-platform:$TAG-amd64" \
121+
"ghcr.io/nextstrain/base-builder-build-platform:$TAG-arm64"
122+
123+
- name: Create base-builder-target-platform image
124+
run: |
125+
docker buildx imagetools create \
126+
-t "ghcr.io/nextstrain/base-builder-target-platform:$TAG" \
127+
"ghcr.io/nextstrain/base-builder-target-platform:$TAG-amd64" \
128+
"ghcr.io/nextstrain/base-builder-target-platform:$TAG-arm64"
129+
130+
- name: Create base image
131+
run: |
132+
docker buildx imagetools create \
133+
-t "ghcr.io/nextstrain/base:$TAG" \
134+
"ghcr.io/nextstrain/base:$TAG-amd64" \
135+
"ghcr.io/nextstrain/base:$TAG-arm64"
80136
81137
# Run tests with the final image from GitHub Container Registry.
82138
test:
83139
name: test (${{ matrix.platform }})
84-
needs: build
85-
runs-on: ubuntu-latest
140+
needs: [vars, merge-builds]
141+
runs-on: ${{ matrix.runner }}
86142
strategy:
87143
matrix:
88-
platform:
89-
- linux/amd64
90-
- linux/arm64
144+
include:
145+
- platform: linux/amd64
146+
runner: ubuntu-latest
147+
- platform: linux/arm64
148+
# TODO: use ubuntu-24.04-arm and remove emulation when Nextstrain
149+
# CLI can be installed natively on aarch64.¹
150+
# ¹ <https://github.com/nextstrain/cli/pull/490>
151+
runner: ubuntu-latest
91152
permissions:
92153
contents: read
93154
id-token: write
@@ -108,9 +169,7 @@ jobs:
108169
with:
109170
python-version: ~3
110171

111-
# The ubuntu-latest runner is linux/amd64 so anything else will
112-
# run with emulation, which is still better than nothing.
113-
- if: matrix.platform != 'linux/amd64'
172+
- if: matrix.platform == 'linux/arm64'
114173
uses: docker/setup-qemu-action@v3
115174

116175
- uses: actions/checkout@v6
@@ -119,21 +178,21 @@ jobs:
119178

120179
- run: make test
121180
env:
122-
IMAGE: ghcr.io/nextstrain/base:${{ needs.build.outputs.tag }}
181+
IMAGE: ghcr.io/nextstrain/base:${{ needs.vars.outputs.tag }}
123182
DOCKER_DEFAULT_PLATFORM: ${{ matrix.platform }}
124183

125184
- uses: nextstrain/.github/actions/setup-nextstrain-cli@master
126185

127186
- name: Run zika-tutorial
128187
run: |
129188
git clone https://github.com/nextstrain/zika-tutorial
130-
nextstrain build --image ghcr.io/nextstrain/base:${{ needs.build.outputs.tag }} zika-tutorial -F
189+
nextstrain build --image ghcr.io/nextstrain/base:${{ needs.vars.outputs.tag }} zika-tutorial -F
131190
env:
132191
DOCKER_DEFAULT_PLATFORM: ${{ matrix.platform }}
133192

134193
validate-platforms:
135194
name: Validate platforms
136-
needs: build
195+
needs: [vars, merge-builds]
137196
runs-on: ubuntu-latest
138197
steps:
139198
- uses: actions/checkout@v6
@@ -147,13 +206,13 @@ jobs:
147206
password: ${{ secrets.GH_TOKEN_NEXTSTRAIN_BOT_MANAGE_PACKAGES }}
148207

149208
- name: Validate final images
150-
run: ./devel/validate-platforms -r ghcr.io -t ${{ needs.build.outputs.tag }}
209+
run: ./devel/validate-platforms -r ghcr.io -t ${{ needs.vars.outputs.tag }}
151210

152211
# "Push" (copy) the builder and final images from GitHub Container Registry to
153212
# Docker Hub, where they will persist. Do this regardless of test results.
154213
push-branch:
155-
if: startsWith(needs.build.outputs.tag, 'branch-') && github.event_name != 'pull_request'
156-
needs: build
214+
if: startsWith(needs.vars.outputs.tag, 'branch-') && github.event_name != 'pull_request'
215+
needs: [vars, merge-builds]
157216
runs-on: ubuntu-latest
158217
steps:
159218
- uses: actions/checkout@v6
@@ -171,13 +230,13 @@ jobs:
171230
password: ${{ secrets.DOCKER_TOKEN }}
172231

173232
- name: Copy $TAG images to Docker Hub
174-
run: ./devel/copy-images -i ghcr.io -o docker.io -t ${{ needs.build.outputs.tag }}
233+
run: ./devel/copy-images -i ghcr.io -o docker.io -t ${{ needs.vars.outputs.tag }}
175234

176235
# "Push" (copy) the builder and final images from GitHub Container Registry to
177236
# Docker Hub, where they will persist. Only do this if tests pass.
178237
push-build:
179-
if: startsWith(needs.build.outputs.tag, 'build-')
180-
needs: [build, test, validate-platforms]
238+
if: startsWith(needs.vars.outputs.tag, 'build-')
239+
needs: [vars, merge-builds, test, validate-platforms]
181240
runs-on: ubuntu-latest
182241
steps:
183242
- uses: actions/checkout@v6
@@ -196,7 +255,7 @@ jobs:
196255

197256
- name: Copy $TAG + latest images to Docker Hub
198257
run: |
199-
./devel/copy-images -i ghcr.io -o docker.io -t ${{ needs.build.outputs.tag }} -l
258+
./devel/copy-images -i ghcr.io -o docker.io -t ${{ needs.vars.outputs.tag }} -l
200259
201260
# Run pathogen repo CI builds with the final image
202261
# This is running pathogen-repo-ci@v0 for pathogen repos that do not conform
@@ -206,7 +265,7 @@ jobs:
206265
test-pathogen-repo-ci-v0:
207266
# Only one of push-{branch,build} runs for any given workflow run, and
208267
# we're ok with either of them.
209-
needs: [build, push-branch, push-build]
268+
needs: [vars, push-branch, push-build]
210269
if: |2
211270
success()
212271
|| needs.push-branch.result == 'success'
@@ -227,7 +286,7 @@ jobs:
227286
runtimes: |
228287
- docker
229288
env: |
230-
NEXTSTRAIN_DOCKER_IMAGE: nextstrain/base:${{ needs.build.outputs.tag }}
289+
NEXTSTRAIN_DOCKER_IMAGE: nextstrain/base:${{ needs.vars.outputs.tag }}
231290
artifact-name: ${{ matrix.pathogen }}-outputs
232291
continue-on-error: true
233292
secrets: inherit
@@ -239,7 +298,7 @@ jobs:
239298
test-pathogen-repo-ci:
240299
# Only one of push-{branch,build} runs for any given workflow run, and
241300
# we're ok with either of them.
242-
needs: [build, push-branch, push-build]
301+
needs: [vars, push-branch, push-build]
243302
if: |2
244303
success()
245304
|| needs.push-branch.result == 'success'
@@ -267,15 +326,15 @@ jobs:
267326
runtimes: |
268327
- docker
269328
env: |
270-
NEXTSTRAIN_DOCKER_IMAGE: nextstrain/base:${{ needs.build.outputs.tag }}
329+
NEXTSTRAIN_DOCKER_IMAGE: nextstrain/base:${{ needs.vars.outputs.tag }}
271330
artifact-name: ${{ matrix.pathogen }}-outputs
272331
continue-on-error: true
273332
secrets: inherit
274333

275334
# Delete the builder and final images from GitHub Container Registry.
276335
cleanup-registry:
277336
if: always()
278-
needs: [build, test, validate-platforms, push-branch, push-build]
337+
needs: [vars, merge-builds, test, validate-platforms, push-branch, push-build]
279338
runs-on: ubuntu-latest
280339
steps:
281340
- uses: actions/checkout@v6
@@ -285,6 +344,5 @@ jobs:
285344
github-token: ${{ secrets.GH_TOKEN_NEXTSTRAIN_BOT_MANAGE_PACKAGES }}
286345
script: |
287346
const script = require('./devel/delete-from-ghcr.js');
288-
const tag = "${{ needs.build.outputs.tag }}";
289-
const token = "${{ secrets.GH_TOKEN_NEXTSTRAIN_BOT_MANAGE_PACKAGES }}";
290-
script({fetch, octokit: github, tag, token});
347+
const tag = "${{ needs.vars.outputs.tag }}";
348+
script({octokit: github, tag});

0 commit comments

Comments
 (0)