-
Notifications
You must be signed in to change notification settings - Fork 0
326 lines (291 loc) · 12.2 KB
/
Copy pathrelease.yml
File metadata and controls
326 lines (291 loc) · 12.2 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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
name: Release
# Publishes the function package so it is installable straight from a registry,
# without the local-registry dance in scripts/kind-up.sh:
#
# ghcr.io/netclab/function-avd:<version> (always)
# xpkg.upbound.io/netclab/function-avd:<ver> (when UPBOUND_TOKEN is set)
#
# The Upbound push is what puts the function on
# https://marketplace.upbound.io/functions/netclab -- the Marketplace indexes
# that registry, so a GHCR-only release is not listed there.
#
# The published version is pyproject.toml's, not the git tag's: one number to
# bump, and the tag is checked against it rather than trusted.
on:
push:
tags: ['v*']
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
# Read-only by default; only the publishing job widens this.
permissions:
contents: read
jobs:
# Publishing a broken function is worse than publishing late: the offline
# suite is ~seconds, so gate the release on it rather than trusting the tag.
test:
name: offline suite
runs-on: ubuntu-latest
timeout-minutes: 15
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@v7
with:
# The fidelity tests diff against the golden structured configs in the
# submodule; without it they collect nothing and pass vacuously.
submodules: true
fetch-depth: 1
# Full version, not @v9: setup-uv publishes floating major aliases only up
# to v7, so @v9 does not resolve. Renovate tracks the exact tag instead.
- uses: astral-sh/setup-uv@v9.0.0
with:
enable-cache: true
- run: uv sync --locked
- name: Resolve the version to publish
id: version
run: |
set -euo pipefail
# pyproject.toml is the single source of truth; kind-up.sh reads the
# same number, so the local image and the release cannot drift apart.
version="v$(uv version --short)"
echo "version=${version}" >> "$GITHUB_OUTPUT"
echo "publishing ${version}"
if ! [[ "${version}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then
echo "::error::'${version}' is not a semver; xpkg push would reject it."
exit 1
fi
# A tag that disagrees with pyproject.toml means the version bump was
# forgotten, so publishing under either number would be a lie.
if [[ "${{ github.ref_type }}" == "tag" && "${{ github.ref_name }}" != "${version}" ]]; then
echo "::error::tag ${{ github.ref_name }} != pyproject.toml version ${version}." \
"Bump [project].version, or move the tag."
exit 1
fi
- run: uv run pytest -q
# One leg per architecture. The docker exporter cannot write a manifest list,
# so a single build with two --platform values is not an option anyway; the
# matrix at least runs the emulated arm64 build alongside the native one.
build:
name: xpkg (${{ matrix.arch }})
needs: test
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
fail-fast: true
matrix:
arch: [amd64, arm64]
steps:
- uses: actions/checkout@v7
with:
# No submodules: .dockerignore excludes avd/ from the runtime image,
# and it is ~a GB of examples the package never carries.
fetch-depth: 1
# arm64 is emulated, which is what makes the layer cache below worth it.
- uses: docker/setup-qemu-action@v4
- uses: docker/setup-buildx-action@v4
# Cached through GitHub Actions' native backend, per
# https://docs.docker.com/build/cache/backends/gha/ -- worth it because the
# image's expensive layer is `uv sync`, and arm64 runs it under emulation.
- name: Build the runtime image
uses: docker/build-push-action@v7
with:
context: .
platforms: linux/${{ matrix.arch }}
tags: function-avd-runtime:${{ needs.test.outputs.version }}
# A docker-archive tarball, which is what --embed-runtime-image-tarball
# reads. Loading into the daemon instead would be a dead end: one tag
# cannot hold both architectures.
outputs: type=docker,dest=runtime-${{ matrix.arch }}.tar
# scope defaults to `buildkit` -- shared by both matrix legs, which
# would then evict each other's cache on every run. Split it per arch.
cache-from: type=gha,scope=${{ matrix.arch }}
cache-to: type=gha,mode=max,scope=${{ matrix.arch }}
# The docker exporter cannot carry attestations, and xpkg build wants a
# plain image; kind-up.sh passes --provenance=false for the same reason.
provenance: false
- name: Install crossplane CLI
run: |
set -euo pipefail
# renovate: datasource=github-releases depName=crossplane/crossplane
curl -sfL "https://cli.crossplane.io/install.sh" | XP_VERSION=v2.4.0 sh
sudo mv ./crossplane /usr/local/bin/crossplane
crossplane version --client
# --examples-root is stated even though `package/` is the package root,
# because it is NOT optional: it defaults to ./examples, so leaving it out
# does not mean "no examples" -- it means "every YAML under examples/,
# whatever ends up there". That default broke the v0.1.4 release, when
# examples/lab/topology.yaml (helm values, no `kind`) landed beside the
# fabric examples and this build rejected it with "Object 'Kind' is
# missing". --ignore is no help: it does not reach --examples-root.
#
# examples/fabric is what both packages ship, and it holds Fabric XRs and
# nothing else. examples/lab is lab scaffolding, not a demonstration of
# the API.
- name: Build the package
run: |
set -euo pipefail
crossplane xpkg build \
--package-root=package \
--examples-root=examples/fabric \
--embed-runtime-image-tarball="runtime-${{ matrix.arch }}.tar" \
-o "function-avd-${{ matrix.arch }}.xpkg"
- uses: actions/upload-artifact@v7
with:
name: xpkg-${{ matrix.arch }}
path: function-avd-${{ matrix.arch }}.xpkg
if-no-files-found: error
retention-days: 7
# The API this function serves, published as its own package. A Function
# package cannot carry XRDs or Compositions -- `xpkg build` rejects both with
# "object is not a CRD" -- so the only way to ship the avd.netclab.dev API is
# a Configuration, and it is released from here, beside the function it
# depends on and under the same version.
#
# Deliberately NOT an `xpkg-*` artifact: the publish job below globs every
# .xpkg it downloads into one multi-platform index, and this package must not
# be folded into the function's.
configuration:
name: configuration package
needs: test
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 1
- name: Install crossplane CLI
run: |
set -euo pipefail
# renovate: datasource=github-releases depName=crossplane/crossplane
curl -sfL "https://cli.crossplane.io/install.sh" | XP_VERSION=v2.4.0 sh
sudo mv ./crossplane /usr/local/bin/crossplane
- name: Build the Configuration package
run: |
set -euo pipefail
crossplane xpkg build \
--package-root=apis \
--examples-root=examples/fabric \
-o "configuration-avd-${{ needs.test.outputs.version }}.xpkg"
- uses: actions/upload-artifact@v7
with:
name: configuration-xpkg
path: ./*.xpkg
if-no-files-found: error
publish-configuration:
name: push configuration to GHCR + Upbound
needs: [test, configuration]
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: read
packages: write
env:
VERSION: ${{ needs.test.outputs.version }}
UP_TOKEN: ${{ secrets.UPBOUND_TOKEN }}
steps:
- uses: actions/download-artifact@v8
with:
name: configuration-xpkg
- name: Install crossplane CLI
run: |
set -euo pipefail
# renovate: datasource=github-releases depName=crossplane/crossplane
curl -sfL "https://cli.crossplane.io/install.sh" | XP_VERSION=v2.4.0 sh
sudo mv ./crossplane /usr/local/bin/crossplane
- name: Login to GHCR
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push to GHCR
run: |
set -euo pipefail
# A Configuration is YAML with no runtime image, so there is nothing
# to build per architecture and this is a single-file push.
file="$(ls ./configuration-avd-*.xpkg)"
crossplane xpkg push -f "${file}" \
"ghcr.io/netclab/configuration-avd:${VERSION}"
- name: Install up CLI
if: env.UP_TOKEN != ''
run: |
set -euo pipefail
# renovate: datasource=github-releases depName=upbound/up
curl -sL "https://cli.upbound.io" | VERSION=v0.51.1 sh
sudo mv ./up /usr/local/bin/up
- name: Push to the Upbound Marketplace
if: env.UP_TOKEN != ''
run: |
set -euo pipefail
up login --token="${UP_TOKEN}"
file="$(ls ./configuration-avd-*.xpkg)"
up xpkg push -f "${file}" --create \
"xpkg.upbound.io/netclab/configuration-avd:${VERSION}"
- name: Note a skipped Upbound push
if: env.UP_TOKEN == ''
run: |
echo "::warning::UPBOUND_TOKEN is not set -- configuration pushed to" \
"GHCR only."
publish:
name: push to GHCR + Upbound
needs: [test, build]
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
packages: write
env:
VERSION: ${{ needs.test.outputs.version }}
# secrets are not available to job-level `if:`, so hoist the token into
# env and let the Upbound steps skip themselves when it is unset.
UP_TOKEN: ${{ secrets.UPBOUND_TOKEN }}
steps:
# No checkout: pushing packages needs the .xpkg files and nothing else.
- uses: actions/download-artifact@v8
with:
pattern: xpkg-*
merge-multiple: true
- name: Install crossplane CLI
run: |
set -euo pipefail
# renovate: datasource=github-releases depName=crossplane/crossplane
curl -sfL "https://cli.crossplane.io/install.sh" | XP_VERSION=v2.4.0 sh
sudo mv ./crossplane /usr/local/bin/crossplane
- name: Login to GHCR
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push to GHCR
run: |
set -euo pipefail
# One multi-platform index over the per-arch packages, so a cluster on
# either architecture resolves the same tag.
files="$(ls ./*.xpkg | paste -sd, -)"
repo="$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')"
crossplane xpkg push -f "${files}" "ghcr.io/${repo}:${VERSION}"
- name: Install up CLI
if: env.UP_TOKEN != ''
run: |
set -euo pipefail
# renovate: datasource=github-releases depName=upbound/up
curl -sL "https://cli.upbound.io" | VERSION=v0.51.1 sh
sudo mv ./up /usr/local/bin/up
- name: Push to the Upbound Marketplace
if: env.UP_TOKEN != ''
run: |
set -euo pipefail
up login --token="${UP_TOKEN}"
files="$(ls ./*.xpkg | paste -sd, -)"
# --create so the first release does not need the repository to have
# been made by hand in the Upbound console.
up xpkg push -f "${files}" --create \
"xpkg.upbound.io/netclab/function-avd:${VERSION}"
- name: Note a skipped Upbound push
if: env.UP_TOKEN == ''
run: |
echo "::warning::UPBOUND_TOKEN is not set -- pushed to GHCR only." \
"The Marketplace listing will not move until the token is added."