ci: the core checks run in their own job on ci-tauri, and the APK needs it
CI & Build / Python lint (push) Successful in 2s
Android / Core and FFI clippy and tests (push) Failing after 48s
CI & Build / Build now, or wait for Android? (push) Successful in 3s
Android / Build, or is the channel already serving this? (push) Successful in 3s
Desktop (Tauri) / Build, or is the channel already serving this? (push) Successful in 2s
CI & Build / Web typecheck and unit tests (push) Successful in 8s
CI & Build / Python tests (push) Successful in 11s
Android / Kotlin + Rust (APK) (push) Skipped
CI & Build / integration (push) Successful in 1m18s
CI & Build / Build & push image (push) Skipped
Android / Build the server image (push) Successful in 44s
Desktop (Tauri) / Web tests, clippy, Rust tests and rustfmt (push) Failing after 2m4s
Desktop (Tauri) / Tauri desktop (Linux) (push) Skipped
Desktop (Tauri) / Windows installer (cross-compiled) (push) Skipped
Desktop (Tauri) / Update manifest (push) Skipped

The step added in 42db4cd ran cargo on the Android image, which has OpenSSL
only for the Android targets; the host build died at openssl-sys (run 8663)
before reaching a test. The core's clippy and tests are now a 'rust' job on
ci-tauri, the image desktop's verify runs them on, and the APK job needs it.

The server-image dispatch moves to its own job: it was a step inside the APK
job, and a skipped job runs no steps, so failing core checks would have
silently stopped the server image too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-07 22:42:17 -04:00
co-authored by Claude Opus 5.5
parent a682d6d225
commit d682ae026d
+45 -22
View File
@@ -42,7 +42,7 @@ jobs:
# #
# The guard runs here so it covers the skip path too (§6.3). # The guard runs here so it covers the skip path too (§6.3).
# #
# NOTE THE COUPLING WITH ci.yml: when this lane builds, its last step dispatches # NOTE THE COUPLING WITH ci.yml: when this lane builds, its server-image job dispatches
# ci.yml so the image bakes in the APK just published. When it SKIPS, no dispatch # ci.yml so the image bakes in the APK just published. When it SKIPS, no dispatch
# happens — and that is correct, because ci.yml's `gate` stands down only when the # happens — and that is correct, because ci.yml's `gate` stands down only when the
# push touched Android's files, which is the same condition that makes this build. # push touched Android's files, which is the same condition that makes this build.
@@ -72,9 +72,33 @@ jobs:
sh packaging/guard-forward.sh android "$channel" sh packaging/guard-forward.sh android "$channel"
echo "build=$(sh packaging/should-build.sh android "$channel")" >> $GITHUB_OUTPUT echo "build=$(sh packaging/should-build.sh android "$channel")" >> $GITHUB_OUTPUT
# The core ships inside the APK (through android/ffi), so its checks have to gate
# the APK HERE, in this workflow's graph. desktop.yml runs them too, but a red run
# there cannot stop this lane publishing (rule 177, #5237).
#
# On ci-tauri, not ci-rust-android: these are host-target tests, and on Linux the
# core's native-tls is OpenSSL, whose headers only ci-tauri carries. The Android
# image has OpenSSL vendored for the Android targets alone (run 8663 failed at
# `openssl-sys` trying this there). Same Rust pin, same Cargo.lock.
rust:
name: Core and FFI clippy and tests
needs: [decide]
if: needs.decide.outputs.build == 'true'
runs-on: python-ci
container:
image: git.fabledsword.com/bvandeusen/ci-tauri:1.97
steps:
- uses: actions/checkout@v6
- name: Clippy
run: cargo clippy --locked -p inkwell-core -p inkwell-ffi --all-targets -- -D warnings
- name: Test
run: cargo test --locked -p inkwell-core -p inkwell-ffi
build: build:
name: Kotlin + Rust (APK) name: Kotlin + Rust (APK)
needs: [decide] needs: [decide, rust]
if: needs.decide.outputs.build == 'true' if: needs.decide.outputs.build == 'true'
# runs-on is only a scheduling label (Label Model B). flutter-ci is the # runs-on is only a scheduling label (Label Model B). flutter-ci is the
# proven-working label that can pull our container images. # proven-working label that can pull our container images.
@@ -86,8 +110,6 @@ jobs:
permissions: permissions:
contents: write contents: write
# For the dispatch at the end: this lane starts the server image build.
actions: write
defaults: defaults:
run: run:
@@ -169,16 +191,6 @@ jobs:
- name: Build the native library and bindings - name: Build the native library and bindings
run: ./gradlew generateUniffiBindings -PINKWELL_CARGO_PROFILE=${{ steps.build.outputs.profile }} run: ./gradlew generateUniffiBindings -PINKWELL_CARGO_PROFILE=${{ steps.build.outputs.profile }}
# The core ships inside this APK (through android/ffi), so its checks have to
# gate the APK HERE, in this workflow's graph. desktop.yml runs them too, but a
# red run there cannot stop this lane publishing (rule 177, #5237). Host
# target: the crates' own tests, not the cross-compiled .so.
- name: Rust clippy and tests (core and FFI)
working-directory: .
run: |
cargo clippy --locked -p inkwell-core -p inkwell-ffi --all-targets -- -D warnings
cargo test --locked -p inkwell-core -p inkwell-ffi
# The image's PINNED CLIs, not Gradle plugins. ci-rust-android carries both # The image's PINNED CLIs, not Gradle plugins. ci-rust-android carries both
# (M12 step 3) precisely so this lane needs no second image, and going # (M12 step 3) precisely so this lane needs no second image, and going
# through Gradle plugins would mean a second version of each tool resolved # through Gradle plugins would mean a second version of each tool resolved
@@ -195,8 +207,8 @@ jobs:
- name: Unit tests - name: Unit tests
# Host-JVM tests only. Anything touching the core needs an Android # Host-JVM tests only. Anything touching the core needs an Android
# runtime to load the .so, so those are instrumented tests and belong on # runtime to load the .so, so those are instrumented tests and belong on
# an emulator, not here — the Rust side is covered by the cargo step # an emulator, not here — the Rust side is the `rust` job, which this
# above. # one needs.
# #
# DEBUG regardless of what is being packaged: AGP creates unit-test tasks # DEBUG regardless of what is being packaged: AGP creates unit-test tasks
# only for `testBuildType`, which is debug, so `testReleaseUnitTest` does # only for `testBuildType`, which is debug, so `testReleaseUnitTest` does
@@ -290,21 +302,32 @@ jobs:
path: ${{ steps.build.outputs.apk }} path: ${{ steps.build.outputs.apk }}
if-no-files-found: error if-no-files-found: error
server-image:
name: Build the server image
needs: [decide, rust, build]
if: always() && needs.decide.outputs.build == 'true' && (github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/main')
runs-on: flutter-ci
container:
# The image the dispatch has always run in, so its curl is a known quantity.
image: git.fabledsword.com/bvandeusen/ci-rust-android:1.97
permissions:
actions: write
steps:
# The server image bakes in whatever client the dev release holds, so it has # The server image bakes in whatever client the dev release holds, so it has
# to be built AFTER this lane, not alongside it. `ci.yml` stands down on any # to be built AFTER this lane, not alongside it. `ci.yml` stands down on any
# push that touches the Android app (its `gate` job) and waits to be called # push that touches the Android app (its `gate` job) and waits to be called
# from here — that is the other half of this. # from here — that is the other half of this.
# #
# `always()`: a FAILED Android build must still let the server image through. # `always()`: a FAILED Android build — or failed core checks, which skip the
# There is no new client in that case, so it bakes in the previous one, which # build job entirely — must still let the server image through. There is no
# is exactly right — the alternative is a broken Android lane silently # new client in that case, so it bakes in the previous one, which is exactly
# blocking server delivery. # right — the alternative is a broken Android lane silently blocking server
# delivery. Its own job for that reason: a step inside `build` never runs
# when `build` is skipped.
# #
# Not `if: success()` and not skipped on tags either: every ref that builds an # Not `if: success()` and not skipped on tags either: every ref that builds an
# image needs the call, or nothing builds one at all. # image needs the call, or nothing builds one at all.
- name: Build the server image now the client is published - name: Build the server image now the client is published
if: always() && (github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/main')
working-directory: .
env: env:
GITHUB_TOKEN: ${{ github.token }} GITHUB_TOKEN: ${{ github.token }}
run: | run: |