ci(android): containerized APK build and CI job (Issue #45)
- scripts/setup-android-sdk.sh installs fixed versions of the Android command-line tools (checksum verified), platform 34, build-tools 34.0.0 and NDK 28.2 - packaging/android/Containerfile + scripts/build-apk-container.sh build the APK in a container (podman or docker) with JDK 17 and Rust 1.99 - New CI job "Android APK (experimental)": builds the APK and keeps it as a workflow artifact, signed with the key from the secrets ANDROID_KEYSTORE_B64/ANDROID_KEYSTORE_PASS/ANDROID_KEY_ALIAS if set. Not part of the release yet; its failure doesn't block the release. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0be321837c
commit
7584827c04
6 files changed
+172
-2
No files matched your search
@@ -79,3 +79,60 @@ jobs:
|
||||
GITEA_SERVER: ${{ github.server_url }}
|
||||
REPO_OWNER: ${{ github.repository_owner }}
|
||||
REPO_NAME: ${{ github.event.repository.name }}
|
||||
|
||||
android:
|
||||
# Experimental (Issue #45): builds the APK and keeps it as a workflow artifact. It is not
|
||||
# part of the release yet, and a failure here does not stop the release above.
|
||||
name: Android APK (experimental)
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4.2.2
|
||||
|
||||
- name: Install JDK and Tools
|
||||
run: |
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
SUDO=""
|
||||
if [ "$(id -u)" -ne 0 ] && command -v sudo >/dev/null 2>&1; then
|
||||
SUDO="sudo"
|
||||
fi
|
||||
$SUDO apt-get update -y
|
||||
$SUDO apt-get install -y --no-install-recommends \
|
||||
openjdk-17-jdk-headless build-essential pkg-config curl unzip zip
|
||||
|
||||
- name: Setup Rust Toolchain
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||
with:
|
||||
toolchain: stable
|
||||
target: aarch64-linux-android
|
||||
cache: true
|
||||
rustflags: ""
|
||||
|
||||
- name: Cache Android SDK
|
||||
uses: actions/cache@v4.2.3
|
||||
with:
|
||||
path: ~/android-sdk
|
||||
key: android-sdk-${{ hashFiles('scripts/setup-android-sdk.sh') }}
|
||||
|
||||
- name: Install Android SDK and NDK
|
||||
run: ./scripts/setup-android-sdk.sh
|
||||
|
||||
- name: Build APK
|
||||
run: |
|
||||
if [ -n "${ANDROID_KEYSTORE_B64}" ]; then
|
||||
KEYSTORE="${RUNNER_TEMP:-/tmp}/release.keystore"
|
||||
printf '%s' "${ANDROID_KEYSTORE_B64}" | base64 -d > "${KEYSTORE}"
|
||||
export ANDROID_KEYSTORE="${KEYSTORE}"
|
||||
fi
|
||||
./scripts/package-android.sh
|
||||
env:
|
||||
ANDROID_KEYSTORE_B64: ${{ secrets.ANDROID_KEYSTORE_B64 }}
|
||||
ANDROID_KEYSTORE_PASS: ${{ secrets.ANDROID_KEYSTORE_PASS }}
|
||||
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
|
||||
|
||||
- name: Keep APK as Artifact
|
||||
uses: actions/upload-artifact@v4.6.2
|
||||
with:
|
||||
name: ink-notes-android-apk
|
||||
path: target/ink-notes_*_arm64.apk
|
||||
+1
-1
@@ -156,7 +156,7 @@ Die CI prüft zusätzlich `cargo fmt --check` und `cargo clippy --all-targets --
|
||||
|
||||
Andere Systeme:
|
||||
- **Windows** wird unter Linux übersetzt: `scripts/package-windows.sh` (braucht `gcc-mingw-w64-x86-64`); die Tests laufen mit `wine` als Runner (`CARGO_TARGET_X86_64_PC_WINDOWS_GNU_RUNNER=wine cargo test --target x86_64-pc-windows-gnu`).
|
||||
- **Android** (experimentell, Issue #42): `scripts/package-android.sh` baut `target/ink-notes_<version>_arm64.apk` ohne Gradle. Nötig sind das Android-SDK (build-tools, Plattform 34; `ANDROID_HOME`, Standard `~/android-sdk`) und das NDK; der Signierschlüssel kommt aus `ANDROID_KEYSTORE`, `ANDROID_KEYSTORE_PASS` und `ANDROID_KEY_ALIAS`, sonst wird der Debug-Schlüssel verwendet. Installieren mit `adb install -r target/ink-notes_<version>_arm64.apk`, Log-Ausgaben mit `adb logcat -s ink-notes`. Der Stift wird von Fingern unterschieden (Stifttasten, Radierer-Ende, Handballen-Erkennung), Textfelder öffnen die Bildschirmtastatur, Import und „Bild einfügen“ nutzen den System-Dateidialog, und Exporte landen in `Download/ink-notes` (ab Android 10). Tastaturtext und Dateidialog kommen von der kleinen Java-Activity `packaging/android/java/…/InkActivity.java` (zum Bauen ist ein JDK nötig).
|
||||
- **Android** (experimentell, Issue #42): `scripts/package-android.sh` baut `target/ink-notes_<version>_arm64.apk` ohne Gradle. Nötig sind das Android-SDK (build-tools, Plattform 34; `ANDROID_HOME`, Standard `~/android-sdk`) und das NDK; der Signierschlüssel kommt aus `ANDROID_KEYSTORE`, `ANDROID_KEYSTORE_PASS` und `ANDROID_KEY_ALIAS`, sonst wird der Debug-Schlüssel verwendet. Ohne SDK auf dem Rechner baut `scripts/build-apk-container.sh` sie in einem Container (podman oder docker, `packaging/android/Containerfile`); `scripts/setup-android-sdk.sh` installiert die festen SDK-/NDK-Versionen. Die CI baut die APK als Workflow-Artefakt (noch nicht im Release); ein Release-Schlüssel gehört in die Secrets `ANDROID_KEYSTORE_B64` (`base64 -w0 release.keystore`), `ANDROID_KEYSTORE_PASS` und `ANDROID_KEY_ALIAS`. Installieren mit `adb install -r target/ink-notes_<version>_arm64.apk`, Log-Ausgaben mit `adb logcat -s ink-notes`. Der Stift wird von Fingern unterschieden (Stifttasten, Radierer-Ende, Handballen-Erkennung), Textfelder öffnen die Bildschirmtastatur, Import und „Bild einfügen“ nutzen den System-Dateidialog, und Exporte landen in `Download/ink-notes` (ab Android 10). Tastaturtext und Dateidialog kommen von der kleinen Java-Activity `packaging/android/java/…/InkActivity.java` (zum Bauen ist ein JDK nötig).
|
||||
|
||||
### Projektstruktur
|
||||
|
||||
|
||||
@@ -156,7 +156,7 @@ CI additionally checks `cargo fmt --check` and `cargo clippy --all-targets -- -D
|
||||
|
||||
Other systems:
|
||||
- **Windows** is cross-compiled on Linux: `scripts/package-windows.sh` (needs `gcc-mingw-w64-x86-64`); the tests run with `wine` as runner (`CARGO_TARGET_X86_64_PC_WINDOWS_GNU_RUNNER=wine cargo test --target x86_64-pc-windows-gnu`).
|
||||
- **Android** (experimental, Issue #42): `scripts/package-android.sh` builds `target/ink-notes_<version>_arm64.apk` without Gradle. It needs the Android SDK (build-tools, platform 34; `ANDROID_HOME`, default `~/android-sdk`) and the NDK; the signing key comes from `ANDROID_KEYSTORE`, `ANDROID_KEYSTORE_PASS` and `ANDROID_KEY_ALIAS`, otherwise the debug key is used. Install with `adb install -r target/ink-notes_<version>_arm64.apk`, log output with `adb logcat -s ink-notes`. The pen is told apart from fingers (pen buttons, eraser end, palm rejection), text fields open the soft keyboard, import and inserting images use the system file dialog, and exports are saved to `Download/ink-notes` (Android 10 and newer). The small Java activity `packaging/android/java/…/InkActivity.java` (needs a JDK to build) provides keyboard text and the file dialog.
|
||||
- **Android** (experimental, Issue #42): `scripts/package-android.sh` builds `target/ink-notes_<version>_arm64.apk` without Gradle. It needs the Android SDK (build-tools, platform 34; `ANDROID_HOME`, default `~/android-sdk`) and the NDK; the signing key comes from `ANDROID_KEYSTORE`, `ANDROID_KEYSTORE_PASS` and `ANDROID_KEY_ALIAS`, otherwise the debug key is used. Without SDK on the host, `scripts/build-apk-container.sh` builds it in a container (podman or docker, `packaging/android/Containerfile`); `scripts/setup-android-sdk.sh` installs the fixed SDK/NDK versions. The CI builds the APK as a workflow artifact (not yet part of the release); a release key goes into the secrets `ANDROID_KEYSTORE_B64` (`base64 -w0 release.keystore`), `ANDROID_KEYSTORE_PASS` and `ANDROID_KEY_ALIAS`. Install with `adb install -r target/ink-notes_<version>_arm64.apk`, log output with `adb logcat -s ink-notes`. The pen is told apart from fingers (pen buttons, eraser end, palm rejection), text fields open the soft keyboard, import and inserting images use the system file dialog, and exports are saved to `Download/ink-notes` (Android 10 and newer). The small Java activity `packaging/android/java/…/InkActivity.java` (needs a JDK to build) provides keyboard text and the file dialog.
|
||||
|
||||
New code comments and commit messages are written in English; older code and history still contain German.
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# Build environment for the Android APK (Issue #45): fixed versions of the JDK, Android SDK,
|
||||
# NDK and Rust, so that the APK can be built the same way anywhere.
|
||||
# Used by scripts/build-apk-container.sh (podman or docker).
|
||||
FROM docker.io/library/ubuntu:24.04
|
||||
|
||||
ARG RUST_VERSION=1.99.0
|
||||
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
||||
ca-certificates curl unzip zip openjdk-17-jdk-headless build-essential pkg-config \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV ANDROID_HOME=/opt/android-sdk \
|
||||
RUSTUP_HOME=/opt/rustup \
|
||||
CARGO_HOME=/opt/cargo \
|
||||
PATH=/opt/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
|
||||
COPY scripts/setup-android-sdk.sh /tmp/setup-android-sdk.sh
|
||||
RUN /tmp/setup-android-sdk.sh && rm /tmp/setup-android-sdk.sh
|
||||
|
||||
RUN curl -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal \
|
||||
--default-toolchain "${RUST_VERSION}" --target aarch64-linux-android \
|
||||
&& chmod -R a+rwX /opt/cargo /opt/rustup
|
||||
|
||||
WORKDIR /src
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
# ==============================================================================
|
||||
# Builds the APK inside the container from packaging/android/Containerfile (Issue #45),
|
||||
# with podman (preferred) or docker. Needs nothing else on the host.
|
||||
# Result: target/ink-notes_<version>_arm64.apk
|
||||
#
|
||||
# Signing key as in scripts/package-android.sh: ANDROID_KEYSTORE (path on the host),
|
||||
# ANDROID_KEYSTORE_PASS, ANDROID_KEY_ALIAS; otherwise a debug key (kept below
|
||||
# target/android-container/home, so that test builds can update each other).
|
||||
# ==============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
cd "${ROOT_DIR}"
|
||||
|
||||
if command -v podman >/dev/null 2>&1; then
|
||||
ENGINE=podman
|
||||
USER_ARGS=(--userns=keep-id)
|
||||
elif command -v docker >/dev/null 2>&1; then
|
||||
ENGINE=docker
|
||||
USER_ARGS=(--user "$(id -u):$(id -g)")
|
||||
else
|
||||
echo "FEHLER: weder podman noch docker gefunden."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
IMAGE="ink-notes-android"
|
||||
echo "=== Baue Container-Image ${IMAGE} ==="
|
||||
"${ENGINE}" build -t "${IMAGE}" -f packaging/android/Containerfile .
|
||||
|
||||
mkdir -p target/android-container/home
|
||||
ARGS=(
|
||||
--rm "${USER_ARGS[@]}"
|
||||
-v "${ROOT_DIR}:/src"
|
||||
-e HOME=/src/target/android-container/home
|
||||
-e CARGO_TARGET_DIR=/src/target/android-container
|
||||
# Downloaded crates stay between runs
|
||||
-e CARGO_HOME=/src/target/android-container/cargo
|
||||
)
|
||||
if [ -n "${ANDROID_KEYSTORE:-}" ]; then
|
||||
ARGS+=(
|
||||
-v "$(realpath "${ANDROID_KEYSTORE}"):/keystore:ro"
|
||||
-e ANDROID_KEYSTORE=/keystore
|
||||
-e ANDROID_KEYSTORE_PASS -e ANDROID_KEY_ALIAS
|
||||
)
|
||||
fi
|
||||
"${ENGINE}" run "${ARGS[@]}" "${IMAGE}" scripts/package-android.sh
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env bash
|
||||
# ==============================================================================
|
||||
# Installs the Android SDK parts that scripts/package-android.sh needs, in fixed versions
|
||||
# (Issue #45): command-line tools, platform 34, build-tools 34.0.0 and NDK 28.2.
|
||||
# Used by the CI and packaging/android/Containerfile; works for a local SDK as well.
|
||||
#
|
||||
# ANDROID_HOME target folder (default: ~/android-sdk)
|
||||
#
|
||||
# Needs curl, unzip and a JDK 17 or newer (sdkmanager is a Java program).
|
||||
# ==============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
ANDROID_HOME="${ANDROID_HOME:-${HOME}/android-sdk}"
|
||||
CMDLINE_TOOLS_ZIP="commandlinetools-linux-11076708_latest.zip"
|
||||
CMDLINE_TOOLS_SHA256="2d2d50857e4eb553af5a6dc3ad507a17adf43d115264b1afc116f95c92e5e258"
|
||||
PACKAGES=(
|
||||
"platforms;android-34"
|
||||
"build-tools;34.0.0"
|
||||
"ndk;28.2.13676358"
|
||||
)
|
||||
|
||||
SDKMANAGER="${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager"
|
||||
if [ ! -x "${SDKMANAGER}" ]; then
|
||||
echo "=== Lade Android Command-line Tools ==="
|
||||
TMP="$(mktemp -d)"
|
||||
trap 'rm -rf "${TMP}"' EXIT
|
||||
curl -sSfL -o "${TMP}/${CMDLINE_TOOLS_ZIP}" \
|
||||
"https://dl.google.com/android/repository/${CMDLINE_TOOLS_ZIP}"
|
||||
echo "${CMDLINE_TOOLS_SHA256} ${TMP}/${CMDLINE_TOOLS_ZIP}" | sha256sum -c -
|
||||
unzip -q "${TMP}/${CMDLINE_TOOLS_ZIP}" -d "${TMP}"
|
||||
mkdir -p "${ANDROID_HOME}/cmdline-tools"
|
||||
rm -rf "${ANDROID_HOME}/cmdline-tools/latest"
|
||||
mv "${TMP}/cmdline-tools" "${ANDROID_HOME}/cmdline-tools/latest"
|
||||
fi
|
||||
|
||||
echo "=== Installiere ${PACKAGES[*]} nach ${ANDROID_HOME} ==="
|
||||
# sdkmanager ends with an error when `yes` is cut off by a closed pipe; that's fine
|
||||
(yes || true) | "${SDKMANAGER}" --sdk_root="${ANDROID_HOME}" --licenses >/dev/null
|
||||
"${SDKMANAGER}" --sdk_root="${ANDROID_HOME}" --install "${PACKAGES[@]}" >/dev/null
|
||||
echo "Android-SDK bereit: ${ANDROID_HOME}"
|
||||
Reference in new issue
Block a user