Compare commits
5 Commits
2117043e31
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| f65b9be1df | |||
| 5ac145bff6 | |||
| e07258c8bd | |||
| 8881241fd8 | |||
| ad887c8bc8 |
@@ -0,0 +1,32 @@
|
|||||||
|
# Build context for build-image/Dockerfile. The context is the repo root
|
||||||
|
# (see build-image.sh) so the Gradle-cache-warming stage can COPY in the
|
||||||
|
# companion app's real project files — everything else is excluded to keep
|
||||||
|
# the context small and to make sure secrets never reach the Docker daemon.
|
||||||
|
.git
|
||||||
|
dist
|
||||||
|
docs
|
||||||
|
versions
|
||||||
|
SchwertUndMagieOnPebbleWatchApp
|
||||||
|
|
||||||
|
# Companion app: only the Gradle project files are needed (see the
|
||||||
|
# gradle-cache-warm stage) — not generated build output or the VICE/nibtools
|
||||||
|
# source tarballs (buildVice/buildNibtools are excluded from that stage's
|
||||||
|
# gradle invocation, so they're never unpacked there).
|
||||||
|
SchwertUndMagieOnPebbleCompanionApp/app/build
|
||||||
|
SchwertUndMagieOnPebbleCompanionApp/app/.cxx
|
||||||
|
SchwertUndMagieOnPebbleCompanionApp/app/src/main/jni/vice-src
|
||||||
|
SchwertUndMagieOnPebbleCompanionApp/app/src/main/jni/vice-libs
|
||||||
|
SchwertUndMagieOnPebbleCompanionApp/app/src/main/jni/nibtools-src
|
||||||
|
SchwertUndMagieOnPebbleCompanionApp/app/src/main/jni/nibtools-libs
|
||||||
|
SchwertUndMagieOnPebbleCompanionApp/res/*.tar.gz
|
||||||
|
SchwertUndMagieOnPebbleCompanionApp/.gradle
|
||||||
|
SchwertUndMagieOnPebbleCompanionApp/.idea
|
||||||
|
SchwertUndMagieOnPebbleCompanionApp/local.properties
|
||||||
|
SchwertUndMagieOnPebbleCompanionApp/build
|
||||||
|
SchwertUndMagieOnPebbleCompanionApp/captures
|
||||||
|
|
||||||
|
# Secrets — must never reach the Docker daemon, even unused.
|
||||||
|
registry.env
|
||||||
|
**/keystore.properties
|
||||||
|
**/*.keystore
|
||||||
|
**/*.jks
|
||||||
@@ -56,13 +56,46 @@ jobs:
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
API="${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}"
|
API="${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}"
|
||||||
AUTH="Authorization: token ${{ secrets.GITEA_TOKEN }}"
|
AUTH="Authorization: token ${{ secrets.GITEA_TOKEN }}"
|
||||||
|
TAG="${{ gitea.ref_name }}"
|
||||||
|
|
||||||
RELEASE_ID="$(curl -sf -X POST "$API/releases" \
|
# Reuse an existing release for this tag instead of failing outright
|
||||||
-H "$AUTH" -H "Content-Type: application/json" \
|
# (curl -f exit 22) if a prior run already created it — e.g. a retry
|
||||||
-d "{\"tag_name\": \"${{ gitea.ref_name }}\", \"name\": \"${{ gitea.ref_name }}\"}" \
|
# after a later step failed. (No -f here: a 404 for "no release yet"
|
||||||
| jq -r .id)"
|
# is expected, not an error — it just leaves .id empty below.)
|
||||||
|
RELEASE_ID="$(curl -s "$API/releases/tags/$TAG" -H "$AUTH" | jq -r '.id // empty')"
|
||||||
|
if [ -z "$RELEASE_ID" ]; then
|
||||||
|
RELEASE_ID="$(curl -sf -X POST "$API/releases" \
|
||||||
|
-H "$AUTH" -H "Content-Type: application/json" \
|
||||||
|
-d "{\"tag_name\": \"$TAG\", \"name\": \"$TAG\"}" \
|
||||||
|
| jq -r .id)"
|
||||||
|
fi
|
||||||
|
|
||||||
for f in dist/*; do
|
for f in dist/*; do
|
||||||
curl -sf -X POST "$API/releases/$RELEASE_ID/assets?name=$(basename "$f")" \
|
NAME="$(basename "$f")"
|
||||||
|
# Same idempotency concern for assets: a retry re-uploading a name
|
||||||
|
# that's already attached would 409, so replace it instead.
|
||||||
|
EXISTING_ID="$(curl -sf "$API/releases/$RELEASE_ID/assets" -H "$AUTH" \
|
||||||
|
| jq -r --arg n "$NAME" '.[] | select(.name == $n) | .id')"
|
||||||
|
if [ -n "$EXISTING_ID" ]; then
|
||||||
|
curl -sf -X DELETE "$API/releases/$RELEASE_ID/assets/$EXISTING_ID" -H "$AUTH"
|
||||||
|
fi
|
||||||
|
curl -sf -X POST "$API/releases/$RELEASE_ID/assets?name=$NAME" \
|
||||||
-H "$AUTH" -F "attachment=@$f"
|
-H "$AUTH" -F "attachment=@$f"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
- name: Publish to dl.ladkau.de
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
echo "${{ secrets.DL_SFTP_KEY }}" > ~/.ssh/dl_sftp_key
|
||||||
|
chmod 600 ~/.ssh/dl_sftp_key
|
||||||
|
BATCH="$(mktemp)"
|
||||||
|
{
|
||||||
|
echo "-mkdir files/schwert-und-magie"
|
||||||
|
for f in dist/*; do
|
||||||
|
echo "put $f files/schwert-und-magie/$(basename "$f")"
|
||||||
|
done
|
||||||
|
} > "$BATCH"
|
||||||
|
sftp -i ~/.ssh/dl_sftp_key -P 2223 \
|
||||||
|
-o StrictHostKeyChecking=accept-new \
|
||||||
|
-b "$BATCH" uploader@dl.ladkau.de
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@
|
|||||||
.cxx
|
.cxx
|
||||||
local.properties
|
local.properties
|
||||||
.lock*
|
.lock*
|
||||||
|
.idea
|
||||||
/SchwertUndMagieOnPebbleCompanionApp/local.properties
|
/SchwertUndMagieOnPebbleCompanionApp/local.properties
|
||||||
/SchwertUndMagieOnPebbleCompanionApp/.idea/caches
|
/SchwertUndMagieOnPebbleCompanionApp/.idea/caches
|
||||||
/SchwertUndMagieOnPebbleCompanionApp/.idea/libraries
|
/SchwertUndMagieOnPebbleCompanionApp/.idea/libraries
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Matthias Ladkau
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -150,8 +150,10 @@ Pushing a tag `vX.Y.Z` builds both apps in a containerized runner and
|
|||||||
publishes a Gitea Release with the versioned artifacts attached — see
|
publishes a Gitea Release with the versioned artifacts attached — see
|
||||||
`docs/publish.md` §4. One-time setup:
|
`docs/publish.md` §4. One-time setup:
|
||||||
|
|
||||||
1. Build and push the build environment image (`./build-image.sh`, needs
|
1. Build the build environment image locally and push it (`./build-image.sh`
|
||||||
`registry.env` — copy from `registry.env.example`).
|
then `./upload-image.sh`, needs `registry.env` — copy from
|
||||||
|
`registry.env.example`). Use `./run-image.sh` in between to sanity-check
|
||||||
|
the image before pushing.
|
||||||
2. Register a self-hosted `act_runner` with a Docker executor.
|
2. Register a self-hosted `act_runner` with a Docker executor.
|
||||||
3. Add repo secrets under **Settings → Actions → Secrets**:
|
3. Add repo secrets under **Settings → Actions → Secrets**:
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ echo " TOOLCHAIN : ${TOOLCHAIN} [OK]"
|
|||||||
|
|
||||||
# ---- Ensure required host tools are present ---------------------------------
|
# ---- Ensure required host tools are present ---------------------------------
|
||||||
MISSING=()
|
MISSING=()
|
||||||
for tool in dos2unix autoconf automake pkg-config xa; do
|
for tool in dos2unix autoconf automake pkg-config xa flex; do
|
||||||
command -v "$tool" &>/dev/null || MISSING+=("$tool")
|
command -v "$tool" &>/dev/null || MISSING+=("$tool")
|
||||||
done
|
done
|
||||||
if [ ${#MISSING[@]} -gt 0 ]; then
|
if [ ${#MISSING[@]} -gt 0 ]; then
|
||||||
|
|||||||
@@ -807,7 +807,11 @@ JNI_FN(jboolean, getDriveLed)(JNIEnv *env, jobject obj) {
|
|||||||
|
|
||||||
JNI_FN(jint, getFrameCount)(JNIEnv *env, jobject obj) {
|
JNI_FN(jint, getFrameCount)(JNIEnv *env, jobject obj) {
|
||||||
(void)env; (void)obj;
|
(void)env; (void)obj;
|
||||||
|
#ifdef HAVE_VICE_SRC
|
||||||
return (jint)g_frame_count;
|
return (jint)g_frame_count;
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Schwert und Magie uploads a custom character set that redefines a handful of
|
/* Schwert und Magie uploads a custom character set that redefines a handful of
|
||||||
@@ -895,7 +899,11 @@ JNI_FN(void, injectKey)(JNIEnv *env, jobject obj, jint keyCode, jboolean pressed
|
|||||||
|
|
||||||
JNI_FN(void, setSoundEnabled)(JNIEnv *env, jobject obj, jboolean enabled) {
|
JNI_FN(void, setSoundEnabled)(JNIEnv *env, jobject obj, jboolean enabled) {
|
||||||
(void)env; (void)obj;
|
(void)env; (void)obj;
|
||||||
|
#ifdef HAVE_VICE_SRC
|
||||||
g_sound_enabled = enabled ? 1 : 0;
|
g_sound_enabled = enabled ? 1 : 0;
|
||||||
|
#else
|
||||||
|
(void)enabled;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
JNI_FN(jboolean, saveState)(JNIEnv *env, jobject obj, jstring jpath) {
|
JNI_FN(jboolean, saveState)(JNIEnv *env, jobject obj, jstring jpath) {
|
||||||
|
|||||||
+9
-16
@@ -1,8 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Builds and pushes the release build environment image (build-image/Dockerfile)
|
# Builds the release build environment image (build-image/Dockerfile)
|
||||||
# to the container registry. The Gitea Actions release workflow pulls this
|
# locally, tagged with build-image/VERSION and :latest. Does not push —
|
||||||
# image to run dist.sh. Bump build-image/VERSION whenever the Dockerfile
|
# test it with ./run-image.sh first, then publish with ./upload-image.sh.
|
||||||
# changes so the workflow can pin a stable tag.
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
cd "$(dirname "${BASH_SOURCE[0]}")"
|
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||||
@@ -19,27 +18,21 @@ command -v docker >/dev/null 2>&1 \
|
|||||||
# shellcheck disable=SC1091
|
# shellcheck disable=SC1091
|
||||||
source "$ROOT/registry.env"
|
source "$ROOT/registry.env"
|
||||||
|
|
||||||
: "${REGISTRY:?registry.env must set REGISTRY}"
|
|
||||||
: "${REGISTRY_IMAGE:?registry.env must set REGISTRY_IMAGE}"
|
: "${REGISTRY_IMAGE:?registry.env must set REGISTRY_IMAGE}"
|
||||||
: "${REGISTRY_USER:?registry.env must set REGISTRY_USER}"
|
|
||||||
: "${REGISTRY_PASSWORD:?registry.env must set REGISTRY_PASSWORD}"
|
|
||||||
|
|
||||||
VERSION="$(<"$ROOT/build-image/VERSION")"
|
VERSION="$(<"$ROOT/build-image/VERSION")"
|
||||||
[ -n "$VERSION" ] || fail "build-image/VERSION is empty"
|
[ -n "$VERSION" ] || fail "build-image/VERSION is empty"
|
||||||
|
|
||||||
echo "== Logging in to $REGISTRY =="
|
|
||||||
echo "$REGISTRY_PASSWORD" | docker login "$REGISTRY" -u "$REGISTRY_USER" --password-stdin
|
|
||||||
|
|
||||||
echo "== Building $REGISTRY_IMAGE:$VERSION =="
|
echo "== Building $REGISTRY_IMAGE:$VERSION =="
|
||||||
|
# Context is the repo root (not build-image/) so the Dockerfile's
|
||||||
|
# gradle-cache-warm stage can COPY in the companion app's real Gradle
|
||||||
|
# project files — see .dockerignore for what's excluded from that context.
|
||||||
docker build \
|
docker build \
|
||||||
-t "$REGISTRY_IMAGE:$VERSION" \
|
-t "$REGISTRY_IMAGE:$VERSION" \
|
||||||
-t "$REGISTRY_IMAGE:latest" \
|
-t "$REGISTRY_IMAGE:latest" \
|
||||||
-f "$ROOT/build-image/Dockerfile" \
|
-f "$ROOT/build-image/Dockerfile" \
|
||||||
"$ROOT/build-image"
|
"$ROOT"
|
||||||
|
|
||||||
echo "== Pushing $REGISTRY_IMAGE:$VERSION and :latest =="
|
|
||||||
docker push "$REGISTRY_IMAGE:$VERSION"
|
|
||||||
docker push "$REGISTRY_IMAGE:latest"
|
|
||||||
|
|
||||||
echo "== Done =="
|
echo "== Done =="
|
||||||
echo "Image: $REGISTRY_IMAGE:$VERSION"
|
echo "Image: $REGISTRY_IMAGE:$VERSION (and :latest)"
|
||||||
|
echo "Test it with ./run-image.sh, then publish with ./upload-image.sh"
|
||||||
|
|||||||
+54
-10
@@ -1,17 +1,18 @@
|
|||||||
# Build environment for schwert_und_magie_on_pebble release artifacts:
|
# Build environment for schwert_und_magie_on_pebble release artifacts:
|
||||||
# Android SDK/NDK (companion app) + Pebble SDK (watch app), matching the
|
# Android SDK/NDK (companion app) + Pebble SDK (watch app), matching the
|
||||||
# versions pinned in app/build.gradle.kts and validated on the maintainer's
|
# versions pinned in app/build.gradle.kts and validated on the maintainer's
|
||||||
# dev machine. Rebuild and push with ../build-image.sh whenever a version
|
# dev machine. Rebuild with ../build-image.sh whenever a version below, or
|
||||||
# below, or the companion app's ndkVersion/compileSdk, changes.
|
# the companion app's ndkVersion/compileSdk, changes.
|
||||||
#
|
#
|
||||||
# Does NOT contain the release keystore or any secrets — those are injected
|
# Does NOT contain the release keystore or any secrets — those are injected
|
||||||
# at job runtime from Gitea Actions secrets, never baked into this image.
|
# at job runtime from Gitea Actions secrets, never baked into this image.
|
||||||
FROM eclipse-temurin:21-jdk-jammy
|
FROM eclipse-temurin:21-jdk-jammy AS base
|
||||||
|
|
||||||
ARG ANDROID_CMDLINE_TOOLS_VERSION=11076708
|
ARG ANDROID_CMDLINE_TOOLS_VERSION=11076708
|
||||||
ARG ANDROID_PLATFORM=android-36
|
ARG ANDROID_PLATFORM=android-36
|
||||||
ARG ANDROID_BUILD_TOOLS=36.1.0
|
ARG ANDROID_BUILD_TOOLS=36.1.0
|
||||||
ARG ANDROID_NDK=30.0.14904198
|
ARG ANDROID_NDK=30.0.14904198
|
||||||
|
ARG ANDROID_CMAKE=3.22.1
|
||||||
ARG PEBBLE_TOOL_VERSION=5.0.35
|
ARG PEBBLE_TOOL_VERSION=5.0.35
|
||||||
ARG PEBBLE_SDK_CORE_VERSION=4.9.169
|
ARG PEBBLE_SDK_CORE_VERSION=4.9.169
|
||||||
ARG NODE_VERSION=24.16.0
|
ARG NODE_VERSION=24.16.0
|
||||||
@@ -24,17 +25,21 @@ ENV DEBIAN_FRONTEND=noninteractive \
|
|||||||
|
|
||||||
# git/unzip/curl/jq: checkout, SDK downloads, and the release workflow's
|
# git/unzip/curl/jq: checkout, SDK downloads, and the release workflow's
|
||||||
# calls to the Gitea API (create release, upload assets).
|
# calls to the Gitea API (create release, upload assets).
|
||||||
|
# openssh-client: the release workflow's `sftp` upload to dl.ladkau.de.
|
||||||
# python3-venv: pebble-tool's `sdk install` creates a venv per SDK version.
|
# python3-venv: pebble-tool's `sdk install` creates a venv per SDK version.
|
||||||
# dos2unix/autoconf/automake/pkg-config/xa65/build-essential/gettext: host
|
# dos2unix/autoconf/automake/pkg-config/xa65/build-essential/gettext/flex/bison:
|
||||||
# tools required by the companion app's build_vice.sh (see that file's own
|
# host tools required by the companion app's build_vice.sh (see that file's
|
||||||
# preflight check) to cross-compile VICE via autotools before NDK clang
|
# own preflight check, plus flex/bison for VICE's AC_PROG_LEX/AC_PROG_YACC-
|
||||||
# takes over for the actual target compilation.
|
# based configure) to cross-compile VICE via autotools before NDK clang takes
|
||||||
|
# over for the actual target compilation.
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
curl ca-certificates git unzip tar xz-utils python3 python3-venv file jq \
|
curl ca-certificates git unzip tar xz-utils python3 python3-venv file jq openssh-client \
|
||||||
dos2unix autoconf automake pkg-config xa65 build-essential gettext \
|
dos2unix autoconf automake pkg-config xa65 build-essential gettext flex bison \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# --- Android SDK: cmdline-tools, platform, build-tools, NDK ---
|
# --- Android SDK: cmdline-tools, platform, build-tools, NDK, CMake ---
|
||||||
|
# CMake version must match app/build.gradle.kts's externalNativeBuild.cmake.version
|
||||||
|
# — baking it in here avoids AGP installing it on first `docker run` instead.
|
||||||
RUN mkdir -p "$ANDROID_HOME/cmdline-tools" \
|
RUN mkdir -p "$ANDROID_HOME/cmdline-tools" \
|
||||||
&& curl -sSL -o /tmp/cmdline-tools.zip \
|
&& curl -sSL -o /tmp/cmdline-tools.zip \
|
||||||
"https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_CMDLINE_TOOLS_VERSION}_latest.zip" \
|
"https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_CMDLINE_TOOLS_VERSION}_latest.zip" \
|
||||||
@@ -47,6 +52,7 @@ RUN mkdir -p "$ANDROID_HOME/cmdline-tools" \
|
|||||||
"platforms;${ANDROID_PLATFORM}" \
|
"platforms;${ANDROID_PLATFORM}" \
|
||||||
"build-tools;${ANDROID_BUILD_TOOLS}" \
|
"build-tools;${ANDROID_BUILD_TOOLS}" \
|
||||||
"ndk;${ANDROID_NDK}" \
|
"ndk;${ANDROID_NDK}" \
|
||||||
|
"cmake;${ANDROID_CMAKE}" \
|
||||||
>/dev/null
|
>/dev/null
|
||||||
|
|
||||||
# Node.js: `pebble sdk install` below runs `npm install` for the SDK-core's
|
# Node.js: `pebble sdk install` below runs `npm install` for the SDK-core's
|
||||||
@@ -63,4 +69,42 @@ RUN curl -LsSf https://astral.sh/uv/install.sh | sh \
|
|||||||
&& /root/.local/bin/uv tool install "pebble-tool==${PEBBLE_TOOL_VERSION}" \
|
&& /root/.local/bin/uv tool install "pebble-tool==${PEBBLE_TOOL_VERSION}" \
|
||||||
&& /root/.local/bin/pebble sdk install "${PEBBLE_SDK_CORE_VERSION}"
|
&& /root/.local/bin/pebble sdk install "${PEBBLE_SDK_CORE_VERSION}"
|
||||||
|
|
||||||
|
# --- Warm the Gradle dependency cache ---
|
||||||
|
# The companion app's real Gradle project files (not the generated build/
|
||||||
|
# output, and not the VICE/nibtools source tarballs — see .dockerignore) are
|
||||||
|
# COPYed into a throwaway location and built once here, so every actual
|
||||||
|
# `docker run` of this image (which bind-mounts a fresh checkout over
|
||||||
|
# /workspace) hits a warm ~/.gradle cache instead of re-downloading the same
|
||||||
|
# Maven dependencies from dl.google.com/mavenCentral every single run.
|
||||||
|
#
|
||||||
|
# buildVice/buildNibtools are excluded (their source tarballs aren't in the
|
||||||
|
# build context) — CMake already handles that gracefully, falling back to a
|
||||||
|
# placeholder (see vice_jni.c / build_vice.sh's own header comment) — so this
|
||||||
|
# stage only ever warms the Gradle/Maven dependency cache, never bakes in
|
||||||
|
# compiled VICE/nibtools output.
|
||||||
|
#
|
||||||
|
# packageRelease/signReleaseBundle are also excluded — they're the only tasks
|
||||||
|
# that need the release keystore, which never exists here (secrets are
|
||||||
|
# injected at job runtime, never baked into the image). Excluding them lets
|
||||||
|
# everything upstream (dependency resolution, Kotlin/native compilation,
|
||||||
|
# resource merging, dexing) still run and get cached, without two guaranteed,
|
||||||
|
# noisy "missing storeFile" failures cluttering every image build.
|
||||||
|
#
|
||||||
|
# This is a pure optimization: if the app's dependencies change after this
|
||||||
|
# image was built, Gradle just downloads the delta against the warm cache at
|
||||||
|
# `docker run` time — same as it would without this stage, just slower for
|
||||||
|
# that one run, never broken. `|| true` means a transient network failure
|
||||||
|
# here only costs a slower first `docker run`, never breaks the image build.
|
||||||
|
FROM base AS gradle-cache-warm
|
||||||
|
COPY SchwertUndMagieOnPebbleCompanionApp /tmp/warm/SchwertUndMagieOnPebbleCompanionApp
|
||||||
|
WORKDIR /tmp/warm/SchwertUndMagieOnPebbleCompanionApp
|
||||||
|
RUN chmod +x gradlew \
|
||||||
|
&& (./gradlew bundleRelease assembleRelease \
|
||||||
|
-x buildVice -x buildNibtools \
|
||||||
|
-x packageRelease -x signReleaseBundle \
|
||||||
|
--continue || true)
|
||||||
|
|
||||||
|
FROM base
|
||||||
|
COPY --from=gradle-cache-warm /root/.gradle /root/.gradle
|
||||||
|
|
||||||
WORKDIR /workspace
|
WORKDIR /workspace
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
1
|
4
|
||||||
|
|||||||
@@ -74,6 +74,9 @@ fi
|
|||||||
[[ "$VERSION" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+) ]] \
|
[[ "$VERSION" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+) ]] \
|
||||||
|| fail "VERSION '$VERSION' is not a semantic version (expected X.Y.Z, optionally with a -pre+meta suffix)"
|
|| fail "VERSION '$VERSION' is not a semantic version (expected X.Y.Z, optionally with a -pre+meta suffix)"
|
||||||
VERSION_CODE=$(( ${BASH_REMATCH[1]} * 10000 + ${BASH_REMATCH[2]} * 100 + ${BASH_REMATCH[3]} ))
|
VERSION_CODE=$(( ${BASH_REMATCH[1]} * 10000 + ${BASH_REMATCH[2]} * 100 + ${BASH_REMATCH[3]} ))
|
||||||
|
# Android requires a positive versionCode — the untagged dev placeholder
|
||||||
|
# (0.0.0-dev+<sha>) would otherwise compute to 0 and fail Gradle configuration.
|
||||||
|
[ "$VERSION_CODE" -gt 0 ] || VERSION_CODE=1
|
||||||
echo "Version: $VERSION (Android versionCode $VERSION_CODE)"
|
echo "Version: $VERSION (Android versionCode $VERSION_CODE)"
|
||||||
|
|
||||||
# Patch versions into the tracked source files for this build only, then
|
# Patch versions into the tracked source files for this build only, then
|
||||||
@@ -87,7 +90,11 @@ sed -i \
|
|||||||
-e "s/versionCode = [0-9]\+/versionCode = $VERSION_CODE/" \
|
-e "s/versionCode = [0-9]\+/versionCode = $VERSION_CODE/" \
|
||||||
-e "s/versionName = \"[^\"]*\"/versionName = \"$VERSION\"/" \
|
-e "s/versionName = \"[^\"]*\"/versionName = \"$VERSION\"/" \
|
||||||
"$GRADLE_KTS"
|
"$GRADLE_KTS"
|
||||||
sed -i -e "s/\"version\": \"[^\"]*\"/\"version\": \"$VERSION\"/" "$WATCH_PKG_JSON"
|
# Pebble's own build tooling parses package.json's version strictly as
|
||||||
|
# X.Y.Z integers — it rejects the -pre+meta suffix dist.sh otherwise allows
|
||||||
|
# (including the default "0.0.0-dev+<sha>" placeholder), so strip it here.
|
||||||
|
PEBBLE_VERSION="${BASH_REMATCH[1]}.${BASH_REMATCH[2]}.${BASH_REMATCH[3]}"
|
||||||
|
sed -i -e "s/\"version\": \"[^\"]*\"/\"version\": \"$PEBBLE_VERSION\"/" "$WATCH_PKG_JSON"
|
||||||
|
|
||||||
mkdir -p "$DIST"
|
mkdir -p "$DIST"
|
||||||
|
|
||||||
|
|||||||
+34
-8
@@ -249,11 +249,11 @@ be bumped in source files beforehand (`dist.sh` patches `versionCode`/
|
|||||||
running the same thing locally).
|
running the same thing locally).
|
||||||
|
|
||||||
The whole toolchain (Android SDK/NDK, Pebble SDK) lives in
|
The whole toolchain (Android SDK/NDK, Pebble SDK) lives in
|
||||||
`build-image/Dockerfile`, built and pushed to a container registry by
|
`build-image/Dockerfile`, built locally and pushed to a container registry by
|
||||||
`build-image.sh`. This keeps the setup portable: the runner just needs Docker
|
three separate scripts (below). This keeps the setup portable: the runner
|
||||||
and pulls that image, so it isn't tied to any one machine's local toolchain
|
just needs Docker and pulls that image, so it isn't tied to any one machine's
|
||||||
install and can be moved or re-registered elsewhere without touching this
|
local toolchain install and can be moved or re-registered elsewhere without
|
||||||
repo's build scripts.
|
touching this repo's build scripts.
|
||||||
|
|
||||||
### 4.1 Build environment image
|
### 4.1 Build environment image
|
||||||
|
|
||||||
@@ -262,9 +262,14 @@ pinned in `app/build.gradle.kts`, matching what's validated for local builds.
|
|||||||
It does **not** contain the release keystore — that's injected at job runtime
|
It does **not** contain the release keystore — that's injected at job runtime
|
||||||
from Actions secrets (§4.3), never baked into the image.
|
from Actions secrets (§4.3), never baked into the image.
|
||||||
|
|
||||||
|
Three scripts, kept separate so a Dockerfile change can be built and tested
|
||||||
|
locally before anything is pushed to the registry:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp registry.env.example registry.env # fill in your registry credentials
|
cp registry.env.example registry.env # fill in your registry credentials
|
||||||
./build-image.sh # builds + pushes :latest and the pinned VERSION tag
|
./build-image.sh # builds :latest and the pinned VERSION tag, locally only
|
||||||
|
./run-image.sh # runs dist.sh inside that local image — sanity-check before publishing
|
||||||
|
./upload-image.sh # pushes the already-built :latest and VERSION tag to cr.ladkau.de
|
||||||
```
|
```
|
||||||
|
|
||||||
Rebuild and push whenever `build-image/Dockerfile` changes (e.g. a Pebble SDK
|
Rebuild and push whenever `build-image/Dockerfile` changes (e.g. a Pebble SDK
|
||||||
@@ -273,6 +278,16 @@ meaningful. The workflow (§4.2) pulls `:latest` by default; if you need a
|
|||||||
release to be reproducible against an exact toolchain image, pin the `image:`
|
release to be reproducible against an exact toolchain image, pin the `image:`
|
||||||
line in `.gitea/workflows/release.yml` to the versioned tag instead.
|
line in `.gitea/workflows/release.yml` to the versioned tag instead.
|
||||||
|
|
||||||
|
`run-image.sh` bind-mounts this repo straight into the container, so before
|
||||||
|
each run it wipes generated build artifacts (`vice-src`, `vice-libs`,
|
||||||
|
`nibtools-src`, `nibtools-libs`, `app/build`, `app/.cxx`, the watch app's
|
||||||
|
`build/`) — otherwise leftovers from a previous local run would let
|
||||||
|
`build_vice.sh`/`build_nibtools.sh` skip work a real fresh CI checkout always
|
||||||
|
does, hiding bugs that only show up in CI. The container also runs as root
|
||||||
|
(needed for the baked-in SDK/NDK/Gradle setup), so it chowns the whole repo
|
||||||
|
back to your host user on exit — you shouldn't ever need `sudo` to clean up
|
||||||
|
after it.
|
||||||
|
|
||||||
### 4.2 Runner setup (one-time)
|
### 4.2 Runner setup (one-time)
|
||||||
|
|
||||||
1. Enable Actions for the repo: repo Settings → Actions → enable, if not
|
1. Enable Actions for the repo: repo Settings → Actions → enable, if not
|
||||||
@@ -311,6 +326,7 @@ Settings → Actions → Secrets, add:
|
|||||||
| `RELEASE_KEYSTORE_PROPERTIES` | the full contents of `SchwertUndMagieOnPebbleCompanionApp/keystore.properties` (§2.2) |
|
| `RELEASE_KEYSTORE_PROPERTIES` | the full contents of `SchwertUndMagieOnPebbleCompanionApp/keystore.properties` (§2.2) |
|
||||||
| `REGISTRY_USER` | same as `REGISTRY_USER` in `registry.env` |
|
| `REGISTRY_USER` | same as `REGISTRY_USER` in `registry.env` |
|
||||||
| `REGISTRY_PASSWORD` | same as `REGISTRY_PASSWORD` in `registry.env` |
|
| `REGISTRY_PASSWORD` | same as `REGISTRY_PASSWORD` in `registry.env` |
|
||||||
|
| `DL_SFTP_KEY` | private key (PEM) for the `uploader` SFTP account on dl.ladkau.de |
|
||||||
|
|
||||||
`secrets.GITEA_TOKEN` (used to create the release and upload assets) is
|
`secrets.GITEA_TOKEN` (used to create the release and upload assets) is
|
||||||
Gitea's own auto-generated per-job token — nothing to create or add yourself.
|
Gitea's own auto-generated per-job token — nothing to create or add yourself.
|
||||||
@@ -326,15 +342,25 @@ git push origin v1.2.3
|
|||||||
```
|
```
|
||||||
|
|
||||||
Watch the run under the repo's Actions tab. On success, the release appears
|
Watch the run under the repo's Actions tab. On success, the release appears
|
||||||
under the repo's Releases page with the three versioned artifacts attached.
|
under the repo's Releases page with the three versioned artifacts attached,
|
||||||
|
and the same three files are uploaded over SFTP to
|
||||||
|
`dl.ladkau.de:files/schwert-und-magie/` (using the `DL_SFTP_KEY` secret,
|
||||||
|
§4.3) for direct download outside of Gitea.
|
||||||
|
|
||||||
To build the same versioned artifacts locally without pushing a tag (e.g. to
|
To build the same versioned artifacts locally without pushing a tag (e.g. to
|
||||||
test before releasing):
|
test before releasing), either run `dist.sh` directly with the host toolchain:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
VERSION=1.2.3 ./dist.sh
|
VERSION=1.2.3 ./dist.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
or run it inside the build-image container (same environment the runner
|
||||||
|
uses — see §4.1):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./run-image.sh 1.2.3
|
||||||
|
```
|
||||||
|
|
||||||
## 5. Pebble watch app → Rebble app store / direct distribution
|
## 5. Pebble watch app → Rebble app store / direct distribution
|
||||||
|
|
||||||
The official Pebble app store shut down years ago; the community-run
|
The official Pebble app store shut down years ago; the community-run
|
||||||
|
|||||||
Executable
+73
@@ -0,0 +1,73 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Runs dist.sh inside the local build-image container, mirroring what the
|
||||||
|
# Gitea Actions release workflow does — useful for testing build-image
|
||||||
|
# changes (or dist.sh/build_vice.sh/build_nibtools.sh changes) locally
|
||||||
|
# before pushing anything to cr.ladkau.de.
|
||||||
|
#
|
||||||
|
# Usage: ./run-image.sh [VERSION]
|
||||||
|
# VERSION is passed through to dist.sh; omit it for dist.sh's own
|
||||||
|
# git-tag-based default (see dist.sh's header comment).
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||||
|
ROOT="$(pwd)"
|
||||||
|
|
||||||
|
fail() { echo "PREFLIGHT FAIL: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
command -v docker >/dev/null 2>&1 \
|
||||||
|
|| fail "docker not found in PATH"
|
||||||
|
|
||||||
|
[ -f "$ROOT/registry.env" ] \
|
||||||
|
|| fail "registry.env not found — copy registry.env.example to registry.env and fill in your cr.ladkau.de credentials"
|
||||||
|
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
source "$ROOT/registry.env"
|
||||||
|
|
||||||
|
: "${REGISTRY_IMAGE:?registry.env must set REGISTRY_IMAGE}"
|
||||||
|
|
||||||
|
IMAGE_TAG="$(<"$ROOT/build-image/VERSION")"
|
||||||
|
[ -n "$IMAGE_TAG" ] || fail "build-image/VERSION is empty"
|
||||||
|
IMAGE="$REGISTRY_IMAGE:$IMAGE_TAG"
|
||||||
|
|
||||||
|
docker image inspect "$IMAGE" >/dev/null 2>&1 \
|
||||||
|
|| fail "$IMAGE not found locally — run ./build-image.sh first"
|
||||||
|
|
||||||
|
VERSION="${1:-${VERSION:-}}"
|
||||||
|
|
||||||
|
# CI always starts from a fresh checkout, but this script bind-mounts the live
|
||||||
|
# host repo — so build outputs left over from a previous local run (e.g. a
|
||||||
|
# vice-src/ already configured, or a libvice.a that's already built) would
|
||||||
|
# make build_vice.sh/build_nibtools.sh skip work they'd have to do on a real
|
||||||
|
# fresh checkout, silently hiding bugs (like a missing host build tool) that
|
||||||
|
# only show up in CI. Wipe them first so every run exercises a true from-
|
||||||
|
# scratch build, same as CI.
|
||||||
|
JNI="$ROOT/SchwertUndMagieOnPebbleCompanionApp/app/src/main/jni"
|
||||||
|
echo "== Cleaning generated build artifacts for a fresh build =="
|
||||||
|
rm -rf \
|
||||||
|
"$JNI/vice-src" "$JNI/vice-libs" \
|
||||||
|
"$JNI/nibtools-src" "$JNI/nibtools-libs" \
|
||||||
|
"$ROOT/SchwertUndMagieOnPebbleCompanionApp/app/build" \
|
||||||
|
"$ROOT/SchwertUndMagieOnPebbleCompanionApp/app/.cxx" \
|
||||||
|
"$ROOT/SchwertUndMagieOnPebbleWatchApp/build"
|
||||||
|
|
||||||
|
echo "== Running dist.sh inside $IMAGE =="
|
||||||
|
# The container runs as root (needed for the SDK/NDK/Gradle setup baked into
|
||||||
|
# the image), so anything it writes into this bind mount — dist/, app/build,
|
||||||
|
# .cxx, etc. — would otherwise come back owned by root, leaving the host repo
|
||||||
|
# unusable without sudo. Chown everything back to the host user on exit,
|
||||||
|
# whether dist.sh succeeds or fails.
|
||||||
|
docker run --rm \
|
||||||
|
-v "$ROOT:/workspace" \
|
||||||
|
-w /workspace \
|
||||||
|
-e VERSION="$VERSION" \
|
||||||
|
-e HOST_UID="$(id -u)" \
|
||||||
|
-e HOST_GID="$(id -g)" \
|
||||||
|
"$IMAGE" \
|
||||||
|
bash -c '
|
||||||
|
git config --global --add safe.directory /workspace
|
||||||
|
trap "chown -R \"$HOST_UID:$HOST_GID\" /workspace" EXIT
|
||||||
|
./dist.sh
|
||||||
|
'
|
||||||
|
|
||||||
|
echo "== Done — artifacts in dist/ =="
|
||||||
|
ls -la "$ROOT/dist"
|
||||||
Executable
+41
@@ -0,0 +1,41 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Pushes the build environment image — already built locally with
|
||||||
|
# ./build-image.sh, and ideally verified with ./run-image.sh — to
|
||||||
|
# cr.ladkau.de. The Gitea Actions release workflow pulls this image to
|
||||||
|
# run dist.sh.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "${BASH_SOURCE[0]}")"
|
||||||
|
ROOT="$(pwd)"
|
||||||
|
|
||||||
|
fail() { echo "PREFLIGHT FAIL: $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
command -v docker >/dev/null 2>&1 \
|
||||||
|
|| fail "docker not found in PATH"
|
||||||
|
|
||||||
|
[ -f "$ROOT/registry.env" ] \
|
||||||
|
|| fail "registry.env not found — copy registry.env.example to registry.env and fill in your cr.ladkau.de credentials"
|
||||||
|
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
source "$ROOT/registry.env"
|
||||||
|
|
||||||
|
: "${REGISTRY:?registry.env must set REGISTRY}"
|
||||||
|
: "${REGISTRY_IMAGE:?registry.env must set REGISTRY_IMAGE}"
|
||||||
|
: "${REGISTRY_USER:?registry.env must set REGISTRY_USER}"
|
||||||
|
: "${REGISTRY_PASSWORD:?registry.env must set REGISTRY_PASSWORD}"
|
||||||
|
|
||||||
|
VERSION="$(<"$ROOT/build-image/VERSION")"
|
||||||
|
[ -n "$VERSION" ] || fail "build-image/VERSION is empty"
|
||||||
|
|
||||||
|
docker image inspect "$REGISTRY_IMAGE:$VERSION" >/dev/null 2>&1 \
|
||||||
|
|| fail "$REGISTRY_IMAGE:$VERSION not found locally — run ./build-image.sh first"
|
||||||
|
|
||||||
|
echo "== Logging in to $REGISTRY =="
|
||||||
|
echo "$REGISTRY_PASSWORD" | docker login "$REGISTRY" -u "$REGISTRY_USER" --password-stdin
|
||||||
|
|
||||||
|
echo "== Pushing $REGISTRY_IMAGE:$VERSION and :latest =="
|
||||||
|
docker push "$REGISTRY_IMAGE:$VERSION"
|
||||||
|
docker push "$REGISTRY_IMAGE:latest"
|
||||||
|
|
||||||
|
echo "== Done =="
|
||||||
|
echo "Image: $REGISTRY_IMAGE:$VERSION"
|
||||||
Reference in New Issue
Block a user