Repository navigation
303 lines (296 loc) · 12.6 KB
/
Copy pathci.yaml
File metadata and controls
303 lines (296 loc) · 12.6 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
name: CI
on:
push:
branches:
- "**"
- "!dependabot/**"
tags:
- 'v[0-9]+\.[0-9]+\.[0-9]+-?**'
pull_request: {}
jobs:
# the settings of the repository's Makefile the other jobs need, e.g. whether it builds a cli
settings:
runs-on: ubuntu-latest
outputs:
cli: ${{ steps.settings.outputs.cli }}
steps:
- uses: actions/checkout@v7
- name: Check build
# the files synced from componentized/build are unchanged, see build/README.md
run: build/scripts/check-build.sh
- uses: actions-rust-lang/setup-rust-toolchain@v2
- name: Read settings
id: settings
run: echo "cli=$( make -s print-CLI_BIN )" >> "${GITHUB_OUTPUT}"
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions-rust-lang/setup-rust-toolchain@v2
- name: Install cargo binstall
uses: cargo-bins/cargo-binstall@main
- name: Install tools
# the versions pinned in tools/Cargo.toml, on the path for later steps
run: |
make tools
make -s tools-path >> "${GITHUB_PATH}"
- name: Generate
# the wit dependencies, and anything else the repository generates, see `make generate`
run: make generate
- name: Check for drift in generated files
run: git diff --exit-code .
- name: Check crate version matches the interface version
# the crates inherit the workspace version, bumped with the interface by
# build/scripts/bump-version.sh. A repository without a workspace version has nothing to check.
run: |
interface_version=$( sed -n "s/^package ${GITHUB_REPOSITORY/\//:}@\(.*\);$/\1/p" wit/worlds.wit )
workspace_version=$( sed -n '/^\[workspace.package\]/,/^\[/s/^version = "\(.*\)"$/\1/p' Cargo.toml )
if [ -n "${workspace_version}" ] && [ "${workspace_version}" != "${interface_version}" ] ; then
echo "::error::the workspace version ${workspace_version} in Cargo.toml does not match the interface version ${interface_version}, see build/scripts/bump-version.sh"
exit 1
fi
- name: Check crates can be published
# the release workflow publishes the crates when a release is published, too late to find a
# crate can't be published. A cli is verified against the library being published with it.
run: |
read -ra crates <<< "$( make -s print-PUBLISH_CRATES )"
if [ "${#crates[@]}" -eq 0 ] ; then
echo "no crates to publish, see PUBLISH_CRATES"
exit 0
fi
cargo publish --dry-run --locked "${crates[@]/#/--package=}"
- name: Build components
run: make components
- name: Collect components.tar
run: tar -cvf ../../components.tar .
working-directory: ./target/components
- name: Upload components.tar
uses: actions/upload-artifact@v7
with:
name: components.tar
path: components.tar
retention-days: 7
- name: Test
run: make test
- name: Integration test
# the tests that need more than cargo, e.g. a server in a container, see `make test-integration`
run: make test-integration
- name: Capture WIT
working-directory: ./target/components
run: |
dump_wit() {
echo "::group::$(basename "$1") ($(du -h "$1" | awk '{print $1}' ))"
wasm-tools component wit "$1"
echo "::endgroup::"
}
# print interface.wasm first
if [ -f interface.wasm ] ; then
dump_wit interface.wasm
fi
for component in $(find . -name '*.wasm' -not -name '*.debug.wasm' -not -name 'interface.wasm' | sort) ; do
dump_wit "${component}"
done
# the cli built from the repository, see CLI_BIN, packaged for each platform as cargo binstall
# expects, e.g. constants-aarch64-apple-darwin.tar.gz
cli:
needs:
- settings
if: needs.settings.outputs.cli != ''
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-26.04
target: x86_64-unknown-linux-musl
- os: ubuntu-26.04-arm
target: aarch64-unknown-linux-musl
- os: macos-26
target: aarch64-apple-darwin
- os: macos-26-intel
target: x86_64-apple-darwin
- os: windows-2025
target: x86_64-pc-windows-msvc
- os: windows-11-arm
target: aarch64-pc-windows-msvc
# each runner builds for its own platform, linux builds are static with musl
runs-on: ${{ matrix.os }}
# bash on windows too, where pwsh is the default, the steps read CLI_BIN from the environment. The
# windows packaging steps are pwsh, reading it as `${env:CLI_BIN}`.
defaults:
run:
shell: bash
env:
CLI_BIN: ${{ needs.settings.outputs.cli }}
steps:
- uses: actions/checkout@v7
- uses: actions-rust-lang/setup-rust-toolchain@v2
with:
target: ${{ matrix.target }}
- name: Build cli
run: cargo build --release --locked --bin "${CLI_BIN}" --target ${{ matrix.target }}
- name: Package cli
if: runner.os != 'Windows'
run: |
mkdir -p dist
tar -czvf "dist/${CLI_BIN}-${{ matrix.target }}.tar.gz" -C target/${{ matrix.target }}/release "${CLI_BIN}" -C "${PWD}" LICENSE NOTICE README.md
- name: Package cli
if: runner.os == 'Windows'
shell: pwsh
run: |
New-Item -ItemType Directory -Force dist
Compress-Archive -Path "target/${{ matrix.target }}/release/${env:CLI_BIN}.exe", LICENSE, NOTICE, README.md -DestinationPath "dist/${env:CLI_BIN}-${{ matrix.target }}.zip"
# the smoke test runs the cli from the package, as a user would
- name: Unpack cli
if: runner.os != 'Windows'
run: |
mkdir -p smoke
tar -xzvf "dist/${CLI_BIN}-${{ matrix.target }}.tar.gz" -C smoke
- name: Unpack cli
if: runner.os == 'Windows'
shell: pwsh
run: Expand-Archive -Path "dist/${env:CLI_BIN}-${{ matrix.target }}.zip" -DestinationPath smoke
- name: Install wasmtime
uses: bytecodealliance/actions/wasmtime/setup@v1
- name: Smoke test cli
# the repository's scripts/smoke-test-cli.sh runs the unpacked cli, given its path and a
# directory for its files
run: |
if [ ! -f scripts/smoke-test-cli.sh ] ; then
echo "::warning::no scripts/smoke-test-cli.sh to smoke test ${CLI_BIN}"
exit 0
fi
bash scripts/smoke-test-cli.sh "./smoke/${CLI_BIN}" smoke
- name: Upload cli
uses: actions/upload-artifact@v7
with:
name: cli-${{ matrix.target }}
path: dist/*
if-no-files-found: error
retention-days: 7
# a single check for branch protection to require, rather than each job and matrix entry. It always
# runs, a skipped required check counts as passing, and fails when any job it needs did not succeed.
# The cli is skipped for a repository without one.
ci:
if: always()
needs:
- settings
- build
- cli
runs-on: ubuntu-latest
steps:
- name: Check jobs succeeded
env:
RESULTS: ${{ toJSON(needs.*.result) }}
CLI: ${{ needs.settings.outputs.cli }}
CLI_RESULT: ${{ needs.cli.result }}
run: |
echo "results: ${RESULTS}"
if [ -z "${CLI}" ] && [ "${CLI_RESULT}" = "skipped" ] ; then
RESULTS=$( jq -c 'map(select(. != "skipped"))' <<< "${RESULTS}" )
fi
if jq -e 'any(. != "success")' <<< "${RESULTS}" > /dev/null ; then
echo "::error::a required job did not succeed"
exit 1
fi
publish:
# the ci job's result rather than the default success(), which is false when the cli is skipped,
# even though the ci job succeeded without it
if: ${{ !cancelled() && needs.ci.result == 'success' && github.event_name == 'push' && ( startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' ) }}
needs:
- settings
- ci
permissions:
contents: write
packages: write
id-token: write
runs-on: ubuntu-latest
env:
# the repository variable DRY_RUN=true goes through publishing without publishing anything:
# nothing is pushed or signed, and no release is drafted
DRY_RUN: ${{ vars.DRY_RUN == 'true' }}
steps:
- name: Dry run
if: env.DRY_RUN == 'true'
run: echo "::notice::a dry run, the repository variable DRY_RUN is true, nothing is published"
- uses: actions/checkout@v7
- uses: actions-rust-lang/setup-rust-toolchain@v2
- name: Install cargo binstall
uses: cargo-bins/cargo-binstall@main
- name: Install tools
run: |
make tools
make -s tools-path >> "${GITHUB_PATH}"
- name: Install cosign
uses: sigstore/cosign-installer@v4.1.2
- name: Download components.tar
uses: actions/download-artifact@v8
with:
name: components.tar
- name: Download cli
if: needs.settings.outputs.cli != ''
uses: actions/download-artifact@v8
with:
pattern: cli-*
path: dist
merge-multiple: true
- name: Extract components
run: mkdir -p target/components && tar -xvf components.tar -C target/components
- name: Get interface version
id: interface_version
run: echo "VERSION=$( wasm-tools component wit target/components/interface.wasm --json | jq -r "[.packages[] | select(.name | contains(\"${GITHUB_REPOSITORY/\//:}@\"))][0].name" | cut -d'@' -f2 )" >> $GITHUB_OUTPUT
- name: Get tag version
if: startsWith(github.ref, 'refs/tags/')
id: tag_version
run: echo "VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT
- name: Check tag matches the workspace version
# cargo binstall finds the cli archives at the release tagged with the cli crate's version,
# and publishing the release publishes the crates to crates.io at that version. A repository
# without a workspace version has nothing to check.
if: startsWith(github.ref, 'refs/tags/')
run: |
workspace_version=$( sed -n '/^\[workspace.package\]/,/^\[/s/^version = "\(.*\)"$/\1/p' Cargo.toml )
if [ -n "${workspace_version}" ] && [ "v${workspace_version}" != "${{ steps.tag_version.outputs.VERSION }}" ] ; then
echo "::error::tag ${{ steps.tag_version.outputs.VERSION }} does not match the workspace version ${workspace_version} in Cargo.toml"
exit 1
fi
- name: Login to GitHub Container Registry
if: env.DRY_RUN != 'true'
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Publish components to gchr.io
# only publish tags and pre-releases from main
if: startsWith(github.ref, 'refs/tags/') || (github.ref == 'refs/heads/main' && contains(steps.interface_version.outputs.VERSION, '-'))
run: make publish
env:
PUBLISH_LOG: "${{ runner.temp }}/published.txt"
REPOSITORY: "ghcr.io/${{ github.repository }}"
VERSION: "${{ case(github.ref == 'refs/heads/main', steps.interface_version.outputs.VERSION, steps.tag_version.outputs.VERSION) }}"
- name: Draft release notes
if: startsWith(github.ref, 'refs/tags/') || (github.ref == 'refs/heads/main' && contains(steps.interface_version.outputs.VERSION, '-'))
run: |
{
echo "## Published components"
echo
echo "| File | Size | Image |"
echo "| --- | --: | --- |"
while read -r file bytes image ; do
size=$( numfmt --to=iec-i --suffix=B --format=%.1f "${bytes}" )
echo "| \`${file}\` | ${size} | \`${image}\` |"
done < "${RUNNER_TEMP}/published.txt"
} > "${RUNNER_TEMP}/release-notes.md"
cat "${RUNNER_TEMP}/release-notes.md"
- name: Draft GitHub Release
if: startsWith(github.ref, 'refs/tags/') && ( env.DRY_RUN != 'true' || github.repository == 'componentized/build' )
uses: softprops/action-gh-release@v3
with:
draft: true
body_path: ${{ runner.temp }}/release-notes.md
# the cli archives of a repository with a cli
files: |
components.tar
${{ needs.settings.outputs.cli != '' && 'dist/*' || '' }}
fail_on_unmatched_files: true
token: ${{ secrets.GITHUB_TOKEN }}