mirror of
https://github.com/bitsycore/compose-desktop-native.git
synced 2026-10-05 10:47:26 +00:00
309 lines
15 KiB
YAML
309 lines
15 KiB
YAML
name: Publish
|
|
|
|
# Only runs when a version tag is pushed (e.g. `git tag v1.2.3 && git push --tags`).
|
|
# `github.ref_name` is passed to Gradle as PUBLISH_VERSION; the root build strips
|
|
# the leading `v` before setting each subproject's `version`.
|
|
on:
|
|
push:
|
|
tags:
|
|
- 'v*'
|
|
# Manual fallback: run with --ref <tag> so github.ref_name is the tag itself
|
|
# (PUBLISH_VERSION is derived from it).
|
|
workflow_dispatch:
|
|
|
|
# Never overlap two publish runs for the same tag.
|
|
concurrency:
|
|
group: publish-${{ github.ref }}
|
|
cancel-in-progress: false
|
|
|
|
jobs:
|
|
publish:
|
|
strategy:
|
|
fail-fast: false
|
|
# Each host runs only the publish tasks Gradle actually generated for it.
|
|
# WINDOWS also publishes the shared kotlinMultiplatform metadata modules
|
|
# (see its matrix entry) + the jvm jar + the bridge plugin.
|
|
# `target` is the Gradle K/N target token (Capitalised) - one per host,
|
|
# used for :demo / :apidemo linkReleaseExecutable<Target>. linuxArm64 is
|
|
# skipped for the app build because SDL3 static libs on the ubuntu runner
|
|
# are host-arch only; cross-compile would fail at link time.
|
|
matrix:
|
|
include:
|
|
- runner: macos-14
|
|
host: macos
|
|
target: MacosArm64
|
|
exe_ext: kexe
|
|
tasks: >-
|
|
publishMacosArm64PublicationToGitHubPackagesRepository
|
|
- runner: ubuntu-22.04
|
|
host: linux
|
|
target: LinuxX64
|
|
exe_ext: kexe
|
|
tasks: >-
|
|
publishLinuxX64PublicationToGitHubPackagesRepository
|
|
publishLinuxArm64PublicationToGitHubPackagesRepository
|
|
- runner: windows-2022
|
|
host: windows
|
|
target: MingwX64
|
|
exe_ext: exe
|
|
# WINDOWS owns the root KotlinMultiplatform metadata modules: it is
|
|
# the ONLY host that declares every target (vHostSupportsMingw is
|
|
# host-gated), so only its generated .module files carry the full
|
|
# variant table - the macOS-published roots of v0.1.15 had NO
|
|
# mingwX64 variants and Windows consumers could not resolve them.
|
|
# The host-independent jvm jar (:material-symbols) and the bridge
|
|
# plugin ride along on the same job.
|
|
tasks: >-
|
|
publishMingwX64PublicationToGitHubPackagesRepository
|
|
publishKotlinMultiplatformPublicationToGitHubPackagesRepository
|
|
publishJvmPublicationToGitHubPackagesRepository
|
|
:compose-desktop-native-bridge:publishAllPublicationsToGitHubPackagesRepository
|
|
|
|
runs-on: ${{ matrix.runner }}
|
|
|
|
permissions:
|
|
contents: write # attach release binaries to the tag's GitHub Release
|
|
packages: write # publish Maven artifacts to GitHub Packages
|
|
|
|
env:
|
|
PUBLISH_VERSION: ${{ github.ref_name }}
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Set up JDK 21
|
|
uses: actions/setup-java@v4
|
|
with:
|
|
distribution: temurin
|
|
java-version: '21'
|
|
|
|
- name: Set up Gradle
|
|
uses: gradle/actions/setup-gradle@v4
|
|
|
|
# SDL3's Linux static build needs the host graphics / audio / input dev
|
|
# headers so its CMake configure can pick up wayland / X11 / ALSA / etc.
|
|
# libxtst-dev is required whenever X11 is enabled (SDL3 gates XTEST behind
|
|
# a hard-error unless -DSDL_X11_XTEST=OFF is passed).
|
|
- name: Install Linux SDL build deps
|
|
if: matrix.host == 'linux'
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y --no-install-recommends \
|
|
build-essential cmake pkg-config \
|
|
libx11-dev libxext-dev libxrandr-dev libxcursor-dev libxi-dev \
|
|
libxinerama-dev libxss-dev libxkbcommon-dev libxkbcommon-x11-dev \
|
|
libxtst-dev \
|
|
libwayland-dev wayland-protocols \
|
|
libgl1-mesa-dev libegl1-mesa-dev \
|
|
libasound2-dev libpulse-dev libudev-dev \
|
|
libdbus-1-dev libibus-1.0-dev \
|
|
libfontconfig-dev
|
|
|
|
# windows-2022 runners pre-install Chocolatey's mingw-w64 at C:\mingw64.
|
|
# SDL3 >= 3.4 needs a C++ compiler for its GameInput backend (K/N's
|
|
# bundled mingw is C-only) - this pre-install covers that. The setjmp
|
|
# intrinsic K/N's LLD can't resolve is aliased to msvcrt's _setjmpex at
|
|
# link time via --defsym in sdl3.def.
|
|
|
|
# Build the static SDL3 lib (scripts/build-sdl/build-sdl.properties). Every
|
|
# target renders through Skia now, so all platforms need only base SDL3 for
|
|
# the main loop (windowing / input / audio / filesystem) - the from-scratch
|
|
# SDL renderer and its SDL3_ttf / SDL3_image / FreeType deps were removed.
|
|
# Cached against the manifest + scripts so version bumps invalidate cleanly;
|
|
# bump the `v3` key when the toolchain (runner OS, compiler flavour, K/N
|
|
# sysroot) changes without a scripts/build-sdl/ edit.
|
|
- name: Cache libs/
|
|
id: cache-libs
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: libs
|
|
key: sdl-libs-v3-${{ matrix.host }}-${{ hashFiles('scripts/build-sdl/**') }}
|
|
|
|
- name: Build static native deps
|
|
if: steps.cache-libs.outputs.cache-hit != 'true'
|
|
shell: bash
|
|
run: python3 scripts/build-sdl/build-all.py
|
|
|
|
# src/vendor/ trees are gitignored - a fresh checkout has zero vendored
|
|
# androidx.compose.* files. Sync copies them in from the upstream refs
|
|
# pinned in scripts/compose-fork/compose.properties. Needs python3 + git;
|
|
# both are pre-installed on every runner.
|
|
- name: Sync vendored Compose code
|
|
shell: bash
|
|
run: bash scripts/compose-fork/sync.sh
|
|
|
|
# GitHub Packages FORBIDS overwriting a file that already exists in a
|
|
# version, and maven-publish uploads each publication's files
|
|
# SEQUENTIALLY from the first one. So when an upload dies mid-publication
|
|
# (the v0.1.13 macOS run lost DNS mid-publish), a blind retry hits 409
|
|
# Conflict on the first already-uploaded file and aborts BEFORE reaching
|
|
# the missing tail files - the version is poisoned and can never
|
|
# complete. A 409 is therefore only benign when the publication had
|
|
# already completed; you can't tell from the 409 alone. The fix: between
|
|
# attempts, DELETE this host's package versions for the current release
|
|
# and re-upload from a clean slate. Each host owns DISJOINT package
|
|
# names (roots / -mingwx64 / -jvm / bridge on Windows, -macosarm64 on
|
|
# macOS, -linuxx64/-linuxarm64 on Linux), so parallel jobs never touch
|
|
# each other's uploads. Uses the run's GITHUB_TOKEN (packages: write);
|
|
# a 403 on delete just degrades to the old blind-retry behaviour.
|
|
- name: Publish to GitHub Packages
|
|
shell: bash
|
|
run: |
|
|
set -e
|
|
VER="${PUBLISH_VERSION#v}"
|
|
OWNER="${GITHUB_REPOSITORY%%/*}"
|
|
# Keep in sync with the module list in settings.gradle.kts.
|
|
MODULES="sdl-core ui ui-graphics ui-text ui-util ui-geometry ui-unit ui-backhandler ui-tooling-preview animation animation-core animation-graphics foundation foundation-layout material3 material-ripple desktop-native-window material-symbols navigation3-ui components-resources"
|
|
case "${{ matrix.host }}" in
|
|
macos) SUFFIXES="-macosarm64" ;;
|
|
linux) SUFFIXES="-linuxx64 -linuxarm64" ;;
|
|
windows) SUFFIXES="_ROOT_ -mingwx64" ;;
|
|
esac
|
|
# Published group per module - mirrors the per-area com.bitsycore groups
|
|
# in root build.gradle.kts. Project-only modules (sdl-core, material-symbols)
|
|
# fall through to com.bitsycore.compose.sdl; the GitHub Packages Maven
|
|
# package name is "<group>.<artifact>".
|
|
group_of() {
|
|
case "$1" in
|
|
ui|ui-graphics|ui-text|ui-util|ui-geometry|ui-unit|ui-backhandler|ui-tooling-preview)
|
|
echo "com.bitsycore.compose.ui" ;;
|
|
foundation|foundation-layout) echo "com.bitsycore.compose.foundation" ;;
|
|
animation|animation-core|animation-graphics) echo "com.bitsycore.compose.animation" ;;
|
|
material3) echo "com.bitsycore.compose.material3" ;;
|
|
material-ripple) echo "com.bitsycore.compose.material" ;;
|
|
components-resources) echo "com.bitsycore.compose.components" ;;
|
|
navigation3-ui) echo "com.bitsycore.navigation3" ;;
|
|
desktop-native-window) echo "com.bitsycore.compose" ;;
|
|
*) echo "com.bitsycore.compose.sdl" ;;
|
|
esac
|
|
}
|
|
host_packages() {
|
|
for m in $MODULES; do
|
|
g=$(group_of "$m")
|
|
for s in $SUFFIXES; do
|
|
if [ "$s" = "_ROOT_" ]; then echo "$g.$m"; else echo "$g.$m$s"; fi
|
|
done
|
|
done
|
|
if [ "${{ matrix.host }}" = "windows" ]; then
|
|
echo "com.bitsycore.compose.sdl.material-symbols-jvm"
|
|
echo "com.bitsycore.compose.sdl.compose-desktop-native-bridge"
|
|
echo "com.bitsycore.compose-desktop-native.bridge.com.bitsycore.compose-desktop-native.bridge.gradle.plugin"
|
|
fi
|
|
}
|
|
cleanup_host_versions() {
|
|
echo "Deleting this host's partially-published $VER package versions"
|
|
host_packages | while read -r pkg; do
|
|
vid=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \
|
|
"https://api.github.com/users/$OWNER/packages/maven/$pkg/versions?per_page=100" \
|
|
| jq -r --arg v "$VER" 'if type=="array" then ([.[] | select(.name==$v) | .id] | first // empty) else empty end')
|
|
[ -z "$vid" ] && continue
|
|
code=$(curl -s -o /dev/null -w "%{http_code}" -X DELETE \
|
|
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
|
"https://api.github.com/users/$OWNER/packages/maven/$pkg/versions/$vid")
|
|
if [ "$code" != "204" ]; then
|
|
# Deleting the LAST version of a package is refused (400) -
|
|
# a package new in this release: drop the whole package.
|
|
code=$(curl -s -o /dev/null -w "%{http_code}" -X DELETE \
|
|
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
|
"https://api.github.com/users/$OWNER/packages/maven/$pkg")
|
|
fi
|
|
echo " $pkg@$VER -> HTTP $code"
|
|
done
|
|
}
|
|
for attempt in 1 2 3; do
|
|
echo "::group::Publish attempt $attempt"
|
|
if ./gradlew ${{ matrix.tasks }} --continue --no-daemon --stacktrace; then
|
|
echo "::endgroup::"
|
|
exit 0
|
|
fi
|
|
echo "::endgroup::"
|
|
if [ $attempt -lt 3 ]; then
|
|
echo "::warning::Publish attempt $attempt failed - cleaning this host's $VER packages and retrying"
|
|
cleanup_host_versions || true
|
|
sleep 30
|
|
fi
|
|
done
|
|
echo "::error::Publish failed after 3 attempts"
|
|
exit 1
|
|
|
|
# Second target: the self-hosted Reposilite instance. Same publications,
|
|
# task names derived from the matrix by swapping the repository name.
|
|
# The releases repo has redeployment enabled, so a plain retry after a
|
|
# mid-upload failure is safe (no GitHub-Packages-style 409 poisoning).
|
|
- name: Publish to maven.bitsycore.com
|
|
shell: bash
|
|
env:
|
|
BITSYCORE_MAVEN_USER: ${{ secrets.BITSYCORE_MAVEN_USER }}
|
|
BITSYCORE_MAVEN_TOKEN: ${{ secrets.BITSYCORE_MAVEN_TOKEN }}
|
|
run: |
|
|
set -e
|
|
BITSY_TASKS=$(echo "${{ matrix.tasks }}" | sed 's/GitHubPackagesRepository/BitsycoreRepository/g')
|
|
for attempt in 1 2 3; do
|
|
echo "::group::Bitsycore publish attempt $attempt"
|
|
if ./gradlew $BITSY_TASKS --continue --no-daemon --stacktrace; then
|
|
echo "::endgroup::"
|
|
exit 0
|
|
fi
|
|
echo "::endgroup::"
|
|
if [ $attempt -lt 3 ]; then
|
|
echo "::warning::Bitsycore publish attempt $attempt failed - retrying"
|
|
sleep 30
|
|
fi
|
|
done
|
|
echo "::error::Bitsycore publish failed after 3 attempts"
|
|
exit 1
|
|
|
|
# Release binaries for the host target - the binary plus the data.kres
|
|
# (bundled composeResources + fonts + subset icon-font). Each app's
|
|
# linkReleaseExecutable<Target> auto-runs its data.kres zip task -
|
|
# :demo's own copy<Variant>ComposeResources<Target>, :apidemo's
|
|
# bridge-plugin package<Variant>ComposeResources<Target> - so data.kres
|
|
# is produced next to the .kexe / .exe. On Linux only linuxX64 is built -
|
|
# linuxArm64 is skipped since the SDL3 static libs on the ubuntu runner
|
|
# are host-arch (x64).
|
|
- name: Build demo + apidemo release binaries
|
|
shell: bash
|
|
run: |
|
|
./gradlew \
|
|
":demo:linkReleaseExecutable${{ matrix.target }}" \
|
|
":apidemo:linkReleaseExecutable${{ matrix.target }}" \
|
|
--no-daemon --stacktrace
|
|
|
|
- name: Package demo + apidemo into zips
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
# Lower-case first letter - gradle target tokens are Capitalised but
|
|
# the build/bin dir uses the plain target name (macosArm64 etc).
|
|
target_dir=$(echo "${{ matrix.target }}" | awk '{print tolower(substr($0,1,1)) substr($0,2)}')
|
|
version="${GITHUB_REF_NAME#v}"
|
|
mkdir -p dist
|
|
for app in demo apidemo; do
|
|
src="$app/build/bin/$target_dir/releaseExecutable"
|
|
zip_path="$(pwd)/dist/$app-$version-$target_dir.zip"
|
|
files=("$src/$app.${{ matrix.exe_ext }}" "$src/data.kres")
|
|
# Windows Skia leg ships two runtime sidecars next to the exe - the
|
|
# skiko fork DLL and Skia's ICU data (icudtl.dat) - provisioned by the
|
|
# bridge plugin after linkReleaseExecutableMingwX64. The exe cannot run
|
|
# without them, so include them (and fail loudly if a provision regressed).
|
|
if [ "${{ matrix.exe_ext }}" = "exe" ]; then
|
|
for sidecar in skiko-windows-x64.dll icudtl.dat; do
|
|
if [ ! -f "$src/$sidecar" ]; then
|
|
echo "::error::Missing Windows runtime sidecar: $src/$sidecar"; exit 1
|
|
fi
|
|
files+=("$src/$sidecar")
|
|
done
|
|
fi
|
|
python3 -c 'import sys,os,zipfile; z=zipfile.ZipFile(sys.argv[1],"w",zipfile.ZIP_DEFLATED,compresslevel=9); [z.write(f,os.path.basename(f)) for f in sys.argv[2:]]; z.close()' \
|
|
"$zip_path" "${files[@]}"
|
|
done
|
|
ls -la dist/
|
|
|
|
- name: Attach demo + apidemo zips to GitHub Release
|
|
uses: softprops/action-gh-release@v2
|
|
with:
|
|
tag_name: ${{ github.ref_name }}
|
|
files: dist/*.zip
|
|
fail_on_unmatched_files: true
|