-
Notifications
You must be signed in to change notification settings - Fork 0
252 lines (228 loc) · 10.5 KB
/
Copy pathci.yml
File metadata and controls
252 lines (228 loc) · 10.5 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
name: CI
# Pushing a v* tag is what publishes a release. Everything else just builds and verifies.
on:
push:
branches: [main]
tags: ['v*']
pull_request:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
JAVA_VERSION: '21'
jobs:
# The platform natives are ordinary Maven dependencies, so one Linux runner can build the
# jar for every platform. Doing all four in one job keeps the Maven cache warm across them.
build:
name: Build all platforms
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: ${{ env.JAVA_VERSION }}
cache: maven
# The tag is the single source of truth for a release version, so it has to be a valid one.
# Rejecting it here costs seconds; a bad version discovered later is baked into lx.package,
# into every published filename, and into whatever people already downloaded.
#
# Semver, minus build metadata: a '+' is legal in semver but makes a mess of a download URL
# and carries no precedence meaning, so there is nothing to gain by allowing it.
- name: Check the tag is semver
if: startsWith(github.ref, 'refs/tags/v')
shell: bash
run: |
version="${GITHUB_REF_NAME#v}"
semver='^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$'
if [[ ! $version =~ $semver ]]; then
echo "::error::Tag '$GITHUB_REF_NAME' is not semver. Expected vMAJOR.MINOR.PATCH, optionally -prerelease (v1.2.3, v0.2.0-rc.1)."
exit 1
fi
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "Releasing $version"
id: version
# A released jar should report its real version in Chromatik's package list, so take it
# from the tag rather than leaving the pom's -SNAPSHOT in lx.package.
- name: Set version from tag
if: startsWith(github.ref, 'refs/tags/v')
shell: bash
run: mvn -B versions:set -DnewVersion="${GITHUB_REF_NAME#v}" -DgenerateBackupPoms=false
# Clean once up front, never between profiles: each profile writes a differently named
# jar into the same target/, and cleaning in the loop would delete the previous one.
#
# The profiles belong to chromatik-core, the only module that bundles a native. The loop
# runs at the root anyway, so the two plugin jars are rebuilt on each pass and overwrite
# themselves with identical content, which is wasteful but keeps this a single command.
- name: Build
run: |
mvn -B clean
for profile in dist-macos dist-windows dist-linux-x86_64 dist-linux-arm64; do
echo "::group::$profile"
mvn -B package "-P$profile"
echo "::endgroup::"
done
# Shade leaves the pre-shade thin jar beside the real one as original-*.jar. Those are
# build leftovers, not artifacts, and attaching them to a release would be confusing.
- name: Collect jars
run: |
mkdir -p dist
find packages -path '*/target/*.jar' ! -name 'original-*' -exec cp {} dist/ \;
ls -l dist
# A plugin whose dependency on chromatik-core slipped from provided to compile scope still
# builds, still passes the release gate, and still works. The only symptom is that shade
# inlined the whole decode stack, so the jar is a thousand times bigger and every class it
# duplicates becomes an error in Chromatik's log once both packages are installed. Size is
# the cheapest thing that notices.
- name: Plugin jars carry no decode stack
run: |
for jar in dist/chromatik-video-*.jar dist/chromatik-screen-*.jar; do
size=$(stat -c%s "$jar")
echo "$jar is $size bytes"
if [ "$size" -gt 1048576 ]; then
echo "::error::$jar is over 1 MB, so it has bundled the decode stack. Its chromatik-core dependency should be provided scope."
exit 1
fi
done
# The shader plugin has a budget of its own because it legitimately carries something: the
# OpenGL bindings Chromatik does not ship, which are about a megabyte of classes plus a small
# native per platform. The ceiling is set just above that, so re-bundling the decode stack or
# LWJGL core still shows up here as well as in the per-jar check during verify.
- name: Shader jar carries its GL bindings and nothing else
run: |
for jar in dist/chromatik-shader-*.jar; do
size=$(stat -c%s "$jar")
echo "$jar is $size bytes"
if [ "$size" -gt 4194304 ]; then
echo "::error::$jar is over 4 MB. It should carry lwjgl-opengl and its native, and nothing else."
exit 1
fi
done
# chromatik-mcp ships no native, so nothing about it varies by platform and it has no place
# in the verify matrix. What it does need is a check that the wire format still matches both
# protocol eras, and that the jar is still only its own classes: gson is provided because
# Chromatik already has it, and a scope slip would push someone else's classes into the class
# loader every installed package shares. The gate needs the provided dependencies supplied,
# exactly as Chromatik supplies them at runtime.
- name: MCP jar shape and protocol
run: |
mvn -B -q -pl :chromatik-mcp dependency:build-classpath \
-Dmdep.outputFile=target/provided.txt -Dmdep.includeScope=provided
jar=$(ls dist/chromatik-mcp-*.jar)
java -cp "$jar:$(cat packages/chromatik-mcp/target/provided.txt)" \
ci/McpProtocolCheck.java "$jar"
- uses: actions/upload-artifact@v7
with:
name: jars
path: dist/
if-no-files-found: error
# Build machine and target machine are different, so every jar is loaded on real hardware of
# the platform it claims to support. Only chromatik-core carries a native, so only it varies by
# platform; the plugin jars are the same everywhere and get checked on every leg for free. The
# Mac jar runs twice because it carries both architectures, and only an Intel Mac can prove the
# Intel half works.
verify:
name: Verify ${{ matrix.name }}
needs: build
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- name: macOS arm64
runner: macos-15
jar: 'chromatik-core-*-macos.jar'
shader: 'chromatik-shader-*-macos.jar'
- name: macOS x86_64
runner: macos-15-intel
jar: 'chromatik-core-*-macos.jar'
shader: 'chromatik-shader-*-macos.jar'
- name: Windows x86_64
runner: windows-2025
jar: 'chromatik-core-*-windows.jar'
shader: 'chromatik-shader-*-windows.jar'
- name: Linux x86_64
runner: ubuntu-24.04
jar: 'chromatik-core-*-linux-x86_64.jar'
shader: 'chromatik-shader-*-linux-x86_64.jar'
- name: Linux arm64
runner: ubuntu-24.04-arm
jar: 'chromatik-core-*-linux-arm64.jar'
shader: 'chromatik-shader-*-linux-arm64.jar'
steps:
- uses: actions/checkout@v7
- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: ${{ env.JAVA_VERSION }}
- uses: actions/download-artifact@v8
with:
name: jars
path: dist
# Only the core jar goes on the classpath, because it is the one carrying the natives. The
# plugin jars are arguments rather than classpath entries: the check has to open each one
# individually to say anything true about it, and passing them as arguments also sidesteps
# the classpath separator being ';' on Windows even under bash.
- name: Load natives and decode
shell: bash
run: |
core=$(ls dist/${{ matrix.jar }})
echo "checking $core against the plugin jars"
java -cp "$core" ci/NativeLoadCheck.java \
dist/chromatik-video-*.jar \
dist/chromatik-screen-*.jar \
dist/${{ matrix.shader }}
# Whether the shader plugin can reach a GPU is a separate question from whether its jar is
# put together correctly, and only the first of those is worth asserting here. A hosted
# runner has no display: Linux has no X server for GLX to talk to, and Windows has only the
# software rasteriser, which stops well short of the 3.3 core profile this needs. So the jar
# is checked for shape above and its rendering is not checked at all, on any platform. macOS
# is the only one where that has been done, by hand, on real hardware.
- name: Note what the shader plugin was and was not checked for
shell: bash
run: |
echo "::notice::${{ matrix.name }}: shader jar shape verified. Rendering is not verified on any CI runner; only macOS has been confirmed on hardware."
release:
name: Publish release
needs: verify
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-24.04
permissions:
contents: write
steps:
- uses: actions/checkout@v7
- uses: actions/download-artifact@v8
with:
name: jars
path: dist
- name: Checksums
working-directory: dist
run: sha256sum *.jar | tee SHA256SUMS
# A semver prerelease is the hyphen suffix, so v0.2.0-rc.1 is flagged on GitHub as one and
# stays out of "latest release", which is what the README links to.
- name: Release notes
shell: bash
env:
TAG: ${{ github.ref_name }}
run: |
version="${TAG#v}"
sed "s/{{VERSION}}/${version}/g" .github/release-notes.md > dist/notes.md
if [[ $version == *-* ]]; then
echo "prerelease=--prerelease" >> "$GITHUB_ENV"
else
echo "prerelease=" >> "$GITHUB_ENV"
fi
# gh is preinstalled on the runner, so the release needs no third-party action.
- name: Create release
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release create "${{ github.ref_name }}" \
dist/*.jar dist/SHA256SUMS \
--title "${{ github.ref_name }}" \
--notes-file dist/notes.md \
--verify-tag \
$prerelease