From d1f28762df31588d351887a868ffd58be40336b3 Mon Sep 17 00:00:00 2001 From: BREW <81044362+b-re-w@users.noreply.github.com> Date: Sat, 17 Oct 2026 22:09:47 +0900 Subject: [PATCH] CI: Prepare the native-image notebook E2E job for the fork's GitHub Packages The notebook-e2e fixture reads the fork's modules from a Maven URL as well as mavenLocal (composeExtended.repository, credentials from GITHUB_ACTOR/GITHUB_TOKEN). desktop.yml gets a notebook-e2e-native-image job, skipped until the repository variable COMPOSE_EXTENDED_VERSION names a published version. It uses the secret GH_PACKAGES_TOKEN when it exists and GITHUB_TOKEN otherwise, checks one POM first and fails naming the secret when the packages cannot be read, then records the configuration, builds the image and runs it. Refs #251 --- .github/workflows/desktop.yml | 80 +++++++++++++++++++ .../fixtures/notebook-e2e/build.gradle.kts | 25 +++++- 2 files changed, 102 insertions(+), 3 deletions(-) diff --git a/.github/workflows/desktop.yml b/.github/workflows/desktop.yml index d29288c9..0b43ff3e 100644 --- a/.github/workflows/desktop.yml +++ b/.github/workflows/desktop.yml @@ -150,3 +150,83 @@ jobs: path: | python-multiplatform-compose/fixtures/notebook-e2e/build/reports/tests/desktopTest python-multiplatform-compose/fixtures/notebook-e2e/build/test-results/desktopTest + + # The notebook E2E in a GraalVM native image on macOS (#249, #251). It takes `ui`, `ui-text` and + # `foundation` from compose-multiplatform-core-extended, which a runner reads from the fork's GitHub + # Packages registry; until the repository variable COMPOSE_EXTENDED_VERSION names a published + # development version (`1.11.1-ext.N-dev.`) the job is skipped. + notebook-e2e-native-image: + name: Notebook E2E in a native image (macOS) + if: ${{ vars.COMPOSE_EXTENDED_VERSION != '' }} + runs-on: macos-latest + permissions: + contents: read + packages: read + env: + WHEEL_DIR: ${{ github.workspace }}/.tmp/pythonx-compose-dist + COMPOSE_EXTENDED_VERSION: ${{ vars.COMPOSE_EXTENDED_VERSION }} + COMPOSE_EXTENDED_GROUP_PREFIX: ${{ vars.COMPOSE_EXTENDED_GROUP_PREFIX || 'org.thisisthepy.compose' }} + COMPOSE_EXTENDED_REPOSITORY: ${{ vars.COMPOSE_EXTENDED_REPOSITORY || 'https://maven.pkg.github.com/thisisthepy/compose-multiplatform-core-extended' }} + # GitHub Packages asks for a token even for a public package. A read:packages PAT in the secret + # GH_PACKAGES_TOKEN is used when it exists; otherwise this repository's own token. + GITHUB_TOKEN: ${{ secrets.GH_PACKAGES_TOKEN || secrets.GITHUB_TOKEN }} + TOKEN_SOURCE: ${{ secrets.GH_PACKAGES_TOKEN != '' && 'GH_PACKAGES_TOKEN' || 'GITHUB_TOKEN' }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Checkout pythonx-compose develop + uses: actions/checkout@v4 + with: + repository: thisisthepy/pythonx-compose + ref: develop + path: .tmp/pythonx-compose + fetch-depth: 1 + + # Before any build: can this token read the fork's packages? One known POM, so a refusal is + # reported as what it is rather than as a dependency resolution error minutes later. + - name: Check access to the fork's packages + run: | + group_path="${COMPOSE_EXTENDED_GROUP_PREFIX//.//}/ui" + pom="$COMPOSE_EXTENDED_REPOSITORY/$group_path/ui-desktop/$COMPOSE_EXTENDED_VERSION/ui-desktop-$COMPOSE_EXTENDED_VERSION.pom" + status=$(curl -s -o /dev/null -w '%{http_code}' -u "${GITHUB_ACTOR}:${GITHUB_TOKEN}" "$pom") + echo "reading $pom with $TOKEN_SOURCE: HTTP $status" + if [ "$status" != "200" ]; then + echo "::error::The fork's packages could not be read with $TOKEN_SOURCE (HTTP $status at $pom). Add a personal access token (classic) with read:packages as the repository secret GH_PACKAGES_TOKEN, or check COMPOSE_EXTENDED_VERSION / COMPOSE_EXTENDED_REPOSITORY." + exit 1 + fi + + - name: Setup uv + uses: astral-sh/setup-uv@v6 + + - name: Build the pythonx-compose wheel + run: uv build --wheel --directory .tmp/pythonx-compose --out-dir "$WHEEL_DIR" + + - name: Setup Java 21 (Gradle) + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'zulu' + cache: 'gradle' + + # The JDK 25 toolchain native-image comes from, found by Gradle's toolchain resolution. + - name: Setup Liberica NIK 25 + uses: graalvm/setup-graalvm@v1 + with: + java-version: '25' + distribution: 'liberica' + set-java-home: 'false' + github-token: ${{ secrets.GITHUB_TOKEN }} + + # Records Compose and skiko's configuration against the fork version this run uses, then builds + # the image and runs it with JAVA_HOME and PYTHONHOME removed: 20 of 20 and no libjvm. + - name: Build and run the native image + run: > + ./gradlew :python-multiplatform-compose:fixtures:notebook-e2e:notebookE2eNativeAgent + :python-multiplatform-compose:fixtures:notebook-e2e:notebookE2eNativeRun + -PpythonxComposeWheel="$WHEEL_DIR" + -PcomposeExtended.version="$COMPOSE_EXTENDED_VERSION" + -PcomposeExtended.groupPrefix="$COMPOSE_EXTENDED_GROUP_PREFIX" + -PcomposeExtended.mavenLocal=false + -PcomposeExtended.repository="$COMPOSE_EXTENDED_REPOSITORY" + --console=plain diff --git a/python-multiplatform-compose/fixtures/notebook-e2e/build.gradle.kts b/python-multiplatform-compose/fixtures/notebook-e2e/build.gradle.kts index bd12b773..9dc84176 100644 --- a/python-multiplatform-compose/fixtures/notebook-e2e/build.gradle.kts +++ b/python-multiplatform-compose/fixtures/notebook-e2e/build.gradle.kts @@ -210,11 +210,30 @@ val composeExtendedVersion = providers.gradleProperty("composeExtended.version") val composeExtendedGroupPrefix = providers.gradleProperty("composeExtended.groupPrefix").orNull ?: "org.jetbrains.compose" val composeExtendedModules = providers.gradleProperty("composeExtended.modules").orNull ?.split(',')?.map { it.trim() }?.filter { it.isNotEmpty() }?.toSet().orEmpty() -if (providers.gradleProperty("composeExtended.mavenLocal").orNull?.toBoolean() == true && composeExtendedVersion != null) { +// Where the fork's modules come from: this machine's mavenLocal (`composeExtended.mavenLocal`), or a +// Maven repository by URL (`composeExtended.repository`, e.g. the fork's GitHub Packages registry, +// #251), read with `GITHUB_ACTOR`/`GITHUB_TOKEN` when they are set (GitHub Packages asks for a token +// even for a public package). Either way only the fork's group at the fork's version is taken there. +val composeExtendedMavenLocal = providers.gradleProperty("composeExtended.mavenLocal").orNull?.toBoolean() == true +val composeExtendedRepository = providers.gradleProperty("composeExtended.repository").orNull?.takeIf { it.isNotBlank() } +if (composeExtendedVersion != null && (composeExtendedMavenLocal || composeExtendedRepository != null)) { + val forkOnly: MavenRepositoryContentDescriptor.() -> Unit = { + includeVersionByRegex(Regex.escape(composeExtendedGroupPrefix) + ".*", ".*", Regex.escape(composeExtendedVersion)) + } // A project's own repositories replace the settings' ones, so those are repeated here. repositories { - mavenLocal { - mavenContent { includeVersionByRegex(Regex.escape(composeExtendedGroupPrefix) + ".*", ".*", Regex.escape(composeExtendedVersion)) } + if (composeExtendedMavenLocal) mavenLocal { mavenContent(forkOnly) } + if (composeExtendedRepository != null) { + maven { + url = uri(composeExtendedRepository) + System.getenv("GITHUB_TOKEN")?.let { token -> + credentials { + username = System.getenv("GITHUB_ACTOR") ?: "token" + password = token + } + } + mavenContent(forkOnly) + } } google { mavenContent {