This commit is contained in:
26
tools/ci/Dockerfile
Normal file
26
tools/ci/Dockerfile
Normal file
@@ -0,0 +1,26 @@
|
||||
# Build toolchain of CI (.gitea/workflows/release.yaml, docs/development.md «CI»): JDK 21 (Gradle, the desktop app's
|
||||
# toolchain and its jpackage) and the Android SDK the TV app compiles against. dpkg-deb for the .deb comes with the base
|
||||
# image. Gradle itself comes through the wrapper and, like every dependency, lands in /gradle — a BuildKit cache mount of
|
||||
# release.Dockerfile. Bump the platform together with compileSdk in app/build.gradle.kts.
|
||||
FROM eclipse-temurin:21-jdk-noble
|
||||
|
||||
ARG CMDLINE_TOOLS=16111833
|
||||
ARG SDK_PACKAGES="platforms;android-37.0 build-tools;36.0.0"
|
||||
|
||||
ENV ANDROID_HOME=/opt/android-sdk
|
||||
ENV PATH=$ANDROID_HOME/cmdline-tools/latest/bin:$PATH
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends curl unzip \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& curl -fsSL -o /tmp/tools.zip https://dl.google.com/android/repository/commandlinetools-linux-${CMDLINE_TOOLS}_latest.zip \
|
||||
&& mkdir -p $ANDROID_HOME/cmdline-tools \
|
||||
&& unzip -q /tmp/tools.zip -d $ANDROID_HOME/cmdline-tools \
|
||||
&& mv $ANDROID_HOME/cmdline-tools/cmdline-tools $ANDROID_HOME/cmdline-tools/latest \
|
||||
&& rm /tmp/tools.zip \
|
||||
&& (yes | sdkmanager --licenses > /dev/null) \
|
||||
&& sdkmanager $SDK_PACKAGES
|
||||
|
||||
ENV GRADLE_USER_HOME=/gradle \
|
||||
ANDROID_USER_HOME=/gradle/android \
|
||||
GRADLE_OPTS=-Dorg.gradle.daemon=false
|
||||
26
tools/ci/release.Dockerfile
Normal file
26
tools/ci/release.Dockerfile
Normal file
@@ -0,0 +1,26 @@
|
||||
# The release build of CI (.gitea/workflows/release.yaml) on the toolchain of ./Dockerfile. BuildKit does it all, so
|
||||
# the job needs no bind mounts (act_runner's docker resolves those on the host, not in the job): the sources go in as
|
||||
# the build context, the signing key as secrets that never land in a layer, Gradle's caches in a cache mount that
|
||||
# outlives the build, and the result comes out with --output.
|
||||
#
|
||||
# docker build -t sleepyowl-ci tools/ci
|
||||
# docker build --no-cache -f tools/ci/release.Dockerfile \
|
||||
# --secret id=keystore,src=release.jks --secret id=keystore_password,src=password.txt \
|
||||
# --secret id=key_alias,src=alias.txt --build-arg VERSION_NAME=0.1.1 -o out .
|
||||
#
|
||||
# out/ then holds what tools/ci/release.sh lists. --no-cache because secrets are not in the layer cache key: a cached
|
||||
# layer would return an APK signed with whatever key built it; the Gradle cache mount survives it.
|
||||
ARG CI_IMAGE=sleepyowl-ci
|
||||
|
||||
FROM ${CI_IMAGE} AS build
|
||||
ARG VERSION_NAME=
|
||||
WORKDIR /work
|
||||
COPY . .
|
||||
RUN --mount=type=cache,id=sleepyowl-gradle,target=/gradle,sharing=locked \
|
||||
--mount=type=secret,id=keystore \
|
||||
--mount=type=secret,id=keystore_password \
|
||||
--mount=type=secret,id=key_alias \
|
||||
sh tools/ci/release.sh /out
|
||||
|
||||
FROM scratch
|
||||
COPY --from=build /out/ /
|
||||
41
tools/ci/release.sh
Normal file
41
tools/ci/release.sh
Normal file
@@ -0,0 +1,41 @@
|
||||
#!/bin/sh
|
||||
# The release build of CI (release.Dockerfile): the TV's APK on the release key, which BuildKit hands in as secrets,
|
||||
# and the desktop's .deb.
|
||||
# $1/sleepyowl.apk the APK (R8 without obfuscation: no mapping needed for stack traces)
|
||||
# $1/sleepyowl_*_amd64.deb the .deb
|
||||
# $1/cert-sha256.txt the APK's signing certificate, SHA-256 (as apksigner prints it)
|
||||
# $1/version.txt "<versionName> <versionCode>", as the APK has them
|
||||
# VERSION_NAME overrides gradle.properties' sleepyowl.versionName.
|
||||
set -eu
|
||||
|
||||
OUT=$1
|
||||
APK=app/build/outputs/apk/release/app-release.apk
|
||||
|
||||
# Without the key Gradle signs with a debug key made up in the container: such an APK installs over nothing.
|
||||
[ -s /run/secrets/keystore ] || { echo "No keystore secret: the release key is required." >&2; exit 1; }
|
||||
ANDROID_KEYSTORE=/run/secrets/keystore
|
||||
ANDROID_KEYSTORE_PASSWORD=$(cat /run/secrets/keystore_password)
|
||||
ANDROID_KEY_ALIAS=$(cat /run/secrets/key_alias 2>/dev/null || true)
|
||||
export ANDROID_KEYSTORE ANDROID_KEYSTORE_PASSWORD ANDROID_KEY_ALIAS
|
||||
|
||||
./gradlew :app:assembleRelease :desktop:packageDeb \
|
||||
${VERSION_NAME:+"-Psleepyowl.versionName=$VERSION_NAME"}
|
||||
|
||||
buildtools=$(ls -d "$ANDROID_HOME"/build-tools/* | tail -n 1)
|
||||
digest=$("$buildtools/apksigner" verify --print-certs "$APK" | sed -n 's/^Signer #1 certificate SHA-256 digest: //p')
|
||||
[ -n "$digest" ] || { echo "apksigner printed no certificate digest." >&2; exit 1; }
|
||||
line=$("$buildtools/aapt2" dump badging "$APK" | sed -n '1p')
|
||||
name=$(printf '%s' "$line" | sed -n "s/.* versionName='\([^']*\)'.*/\1/p")
|
||||
code=$(printf '%s' "$line" | sed -n "s/.* versionCode='\([0-9]*\)'.*/\1/p")
|
||||
[ -n "$name" ] && [ -n "$code" ] || { echo "No version in the APK's manifest: $line" >&2; exit 1; }
|
||||
|
||||
mkdir -p "$OUT"
|
||||
cp "$APK" "$OUT/sleepyowl.apk"
|
||||
cp desktop/build/deb/sleepyowl_*_amd64.deb "$OUT/"
|
||||
printf '%s\n' "$digest" > "$OUT/cert-sha256.txt"
|
||||
printf '%s %s\n' "$name" "$code" > "$OUT/version.txt"
|
||||
|
||||
# What the build left in /work would land in this RUN's layer, and BuildKit keeps such a layer from every run
|
||||
# (--no-cache): only the sources stay. (.dockerignore keeps every build/ out of the context, so these are all Gradle's.)
|
||||
find . -type d -name build -prune -exec rm -rf {} +
|
||||
rm -rf .gradle .kotlin
|
||||
Reference in New Issue
Block a user