W-23599683: CI layered composite actions (foundation + per-artifact) - #150
Merged
Conversation
Design for extracting the shared ~9-step build prefix (Gradle/GraalVM setup, build, distro, Python wheel, Node package + tests) from ci.yml/main.yml/ release.yml into one .github/actions/build-native composite action, with inputs for github-token, native-version, and break-system-packages. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Evolve the monolithic build-native action into build-foundation (shared Gradle/GraalVM setup + build) plus one package-* action per artifact (cli/python/node, extensible to go/rust). Moves the Python-only --break-system-packages flag onto package-python where it belongs. Pure restructuring — rendered commands stay identical to PR #150's green state. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Shape A: each artifact (cli/python/node, later go/rust) is one composite action owning produce + optional tck + optional publish, gated by run-tck and publish (none/artifact/release) inputs, named by artifact. Foundation stays build-foundation. Adding an artifact = 1 new action dir + 1 line per workflow. Records the GHA one-action-per-directory constraint, the verified per-artifact test asymmetry (Python has no tck; CLI/Node do), and defers the raw dwlib upload placement to the plan. Pure restructuring — behavior stays identical to PR #150. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the build-native monolith and six svenstaro upload steps with the composed action sequence: build-foundation → cli → python → node → native-lib. Each artifact action now publishes to release assets via publish: 'release'. Preserves Guess Extension Version step. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
martincousido
approved these changes
Jul 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Restructures CI into layered composite actions so adding a new product
artifact touches the workflows as little as possible and everything about one
artifact lives in one place. Two layers:
.github/actions/build-foundation— Gradle + GraalVM setup and the singlegradlew buildthat compiles both native images (thedwCLI and thedwlibshared library).
cli,python,node,native-lib— eachowning its full lifecycle (produce → optional test/tck → optional publish),
with the phases selected by inputs.
ci.yml,main.yml, andrelease.ymlnow readcheckout → build-foundation → cli → python → node → native-lib: one call perartifact. Follow-up to the runner-migration work (#148).
This evolves the earlier
build-nativemonolith (the first form of this PR)into per-artifact actions —
build-nativeis removed; master never carried it.Why
The roadmap adds Go/Rust bindings. With a single "generic" build action, each new
binding meant editing a shared block that already baked in Node- and
Python-specific setup. With per-artifact actions, adding Go is one new action
directory + one line per workflow that wants it — no change to the foundation
or to any other artifact.
GitHub Actions constraint that decided the shape
A composite action is one directory / one
action.yml/ one callable unit — youcan't define
produce-cli,upload-cli, … as separate sub-actions in one file.A reusable workflow (
workflow_call) runs as its own job on its own runner, soit wouldn't see the native images the foundation compiled without uploading and
re-downloading them between jobs — breaking "build once, package many." So each
artifact is one composite action that performs all its phases, gated by
inputs, named by artifact (
cli, notproduce-cli).How
Every artifact action shares an input convention:
native-version-PnativeVersionvalue; empty omits the flag (Gradle default100.100.100)run-tck'true'runs that artifact's master-only tck phase (cli,nodeonly)publish'none' | 'artifact' | 'release'— selects the publish phaserepo-token/arch/script-name/distro-os/tagbreak-system-packagespythononly — PEP 668--break-system-packagesPer-workflow composition:
native-versionrun-tckpublishbreak-system-packagesci.ymlnonemain.yml100.100.100${{ github.ref == 'refs/heads/master' }}(cli, node)artifacttruerelease.yml${{ env.NATIVE_VERSION }}(tag-derived)releasetrueartifact=actions/upload-artifact@v7.0.1(main's CI retention, incl. the OS-qualifiedstaging/rename each artifact needs);
release=svenstaro/upload-release-action@v2(per-OS asset names, tag). The workflows nolonger carry divergent upload tails.
(
native-cli-integration-tests:test, ×2 suite versions, master-only);Node
nodeTestalways + master-only TCK conformance (×2); Python hasno tck today.
.so/.dll/.dylib+.h) is its ownnative-libaction(
native-lib:stagePythonNativeLib+ the multi-file / per-OS uploads), separatefrom the wheel/tgz that embed it.
secretsor the caller's matrix, so tokens,arch,script-name, anddistro-osare inputs.checkoutstays first;main.yml'sDerive platform tokensandrelease.yml'sGuess Extension Versionrun beforebuild-foundation.Design/plan:
docs/superpowers/specs/2026-07-30-ci-layered-actions-design.md.Verification
Pure restructuring — effective Gradle commands, artifacts produced, their names,
and which tests run per trigger are identical to the pre-refactor state.
build-nativereferenceremains; each workflow composes
build-foundation → cli → python → node → native-lib.--break-system-packages, master-only tck gating, and everyupload-artifact/svenstaroasset name confirmed byte-identical.Build Native CLI(main.yml) PR run is the live cross-OS proof(ubuntu-latest / windows-2022 / macos-latest).
ci.yml(self-hosted) andrelease.yml(tags) don't run on PRs; validated by parse + command inspection.🤖 Generated with Claude Code