1 Commits

Author SHA1 Message Date
ml d5f4381169 Add a custom Android CI/release build image and packaging scripts
build / build (push) Failing after 0s
- build-image/: Dockerfile bundling JDK 17, the Android SDK, and
  Node.js (needed by Gitea's JS-based actions) - the app has no
  native code, so no NDK
- build-image.sh/upload-image.sh: extended to also build/push this
  image alongside the existing OCR pipeline tools image
- run-image.sh: runs `make android` inside the image against a live
  checkout, for a CI-equivalent local build
- Makefile: `make android` builds and versions a release APK into
  dist/, from the current git tag
- .gitea/workflows/build.yml: now pulls the custom image and just
  runs `make android`, replacing the ad-hoc Node/SDK install steps
- README.md: describes the pipeline, server, app, and build tooling
- registry.env.example: fix REGISTRY_IMAGE to be fully-qualified
  (an unqualified name was silently resolving pushes to Docker Hub
  instead of the configured registry)
2026-09-29 17:48:02 +02:00
10 changed files with 414 additions and 69 deletions
+16 -37
View File
@@ -1,10 +1,13 @@
name: build
# Builds the Android app (android/) debug APK on every push/PR, plus
# on-demand via the Gitea "Run workflow" button. Runs inside a public,
# prebuilt Android CI image (JDK + Android SDK/cmdline-tools bundled -
# fuelish's app has no native/NDK code, so it doesn't need a custom
# builder image the way a project with native components would).
# on-demand via the Gitea "Run workflow" button. Runs inside build-image
# (see ../../build-image/Dockerfile, built/pushed via ../../build-image.sh
# and ../../upload-image.sh), which bundles the JDK, Android SDK, and
# Node.js (needed by the JS-based actions below - see
# build-image/Dockerfile's own comment) so the runner itself doesn't need
# any of that installed. The Gradle build still needs network access at
# job runtime (Gradle/AGP/androidx dependency resolution).
on:
push:
pull_request:
@@ -21,52 +24,28 @@ jobs:
runs-on: ubuntu-latest
container:
# JDK + Android SDK cmdline-tools, no platform/build-tools installed -
# the "Install SDK components" step below fetches exactly what
# compileSdk in android/app/build.gradle.kts needs. Much smaller than
# kitchen-sink alternatives (~0.7GB vs. several GB), which matters
# since every job pulls it fresh.
image: thyrlian/android-sdk:10.0
image: cr.ladkau.de/fuelish/android-builder:latest
credentials:
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASSWORD }}
steps:
- uses: actions/checkout@v4
with:
# APK naming below uses `git rev-parse --short HEAD` - harmless
# with the default shallow clone, but fetch-depth: 0 keeps this
# consistent if that ever switches to `git describe --tags`.
# `make android`'s version comes from `git describe --tags` -
# needs full history/tags, not actions/checkout's default
# shallow single-commit clone.
fetch-depth: 0
- name: Preflight
run: |
set -euo pipefail
command -v java >/dev/null 2>&1 || { echo "PREFLIGHT FAIL: java not in PATH" >&2; exit 1; }
command -v make >/dev/null 2>&1 || { echo "PREFLIGHT FAIL: make not in PATH" >&2; exit 1; }
[ -n "${ANDROID_HOME:-}" ] || { echo "PREFLIGHT FAIL: ANDROID_HOME not set" >&2; exit 1; }
[ -x android/gradlew ] || { echo "PREFLIGHT FAIL: android/gradlew missing or not executable" >&2; exit 1; }
- name: Install SDK components
run: |
set -euo pipefail
# python3-pil: android/scripts/gen_app_icons.py's Pillow dependency
# (see android/app/build.gradle.kts's generateAppIcons preBuild task)
# - not bundled in this image.
apt-get update -qq
apt-get install -y --no-install-recommends python3-pil
yes | sdkmanager --licenses >/dev/null 2>&1 || true
# Keep in sync with android/app/build.gradle.kts's compileSdk.
sdkmanager "platforms;android-36" "build-tools;36.0.0"
- name: Build
working-directory: android
run: ./gradlew :app:assembleDebug --console=plain
- name: Package
# Renames the debug APK with the commit it was built from, so
# multiple builds in dist/ (and on dl.ladkau.de) don't collide.
run: |
set -euo pipefail
mkdir -p dist
sha="$(git rev-parse --short HEAD)"
cp android/app/build/outputs/apk/debug/app-debug.apk "dist/fuelish-$sha.apk"
run: make android
- name: Upload build artifact
# v4 uses the newer @actions/artifact backend, which this Gitea
+7
View File
@@ -18,6 +18,9 @@
__pycache__/
*.pyc
# Build output (see the root Makefile's `android` target, run-image.sh)
/dist/
# Android app build output/local config and generated resources (launcher
# icon + splash art - see android/app/build.gradle.kts's
# generateAppIcons task and android/scripts/gen_app_icons.py's own
@@ -32,3 +35,7 @@ __pycache__/
/android/app/src/main/res/mipmap-xhdpi
/android/app/src/main/res/mipmap-xxhdpi
/android/app/src/main/res/mipmap-xxxhdpi
# Android Studio project files - per-developer, not shared
/android/.idea/
/android/gradle/gradle-daemon-jvm.properties
+45
View File
@@ -0,0 +1,45 @@
# Builds the Android app (android/) and packages a versioned release APK.
# The OCR pipeline itself (tooling-container/, 0-3_*.sh, test_batch.sh) is
# container-only and has no host build step, so this Makefile only covers
# the Android app.
ANDROID_DIR := android
ANDROID_APK := $(ANDROID_DIR)/app/build/outputs/apk/debug/app-debug.apk
DIST_DIR := dist
.PHONY: all android clean
all: android
# Builds the Android app (`./gradlew assembleDebug` in android/, via its
# own Gradle/AGP toolchain) and copies the resulting debug APK into
# dist/, named with the current git tag, or `make android VERSION=1.2.3`
# to override. Untagged builds (including every CI build, unless a
# release tag is pushed) get a 0.0.0-dev+<sha> placeholder version.
# Ships the auto-generated debug keystore, not a release signing config -
# fine for sideloading; a real release signing setup is a separate,
# not-yet-needed step. Requires the Android SDK (ANDROID_HOME set) - see
# build-image/Dockerfile for the exact version this is built/tested
# against, or run this inside it via ./run-image.sh.
android:
cd $(ANDROID_DIR) && ./gradlew assembleDebug
@mkdir -p $(DIST_DIR)
@V="$(VERSION)"; \
if [ -z "$$V" ]; then \
if TAG=$$(git describe --tags --exact-match --match 'v[0-9]*.[0-9]*.[0-9]*' 2>/dev/null); then \
V=$${TAG#v}; \
else \
V="0.0.0-dev+$$(git rev-parse --short HEAD)"; \
echo "WARNING: HEAD is not on a vX.Y.Z tag - building placeholder version $$V (push a tag to drive a real release version)" >&2; \
fi; \
fi; \
case "$$V" in \
[0-9]*.[0-9]*.[0-9]*) ;; \
*) echo "PREFLIGHT FAIL: VERSION '$$V' is not a semantic version (expected X.Y.Z, optionally with a -pre+meta suffix)" >&2; exit 1;; \
esac; \
APK_NAME="fuelish-$$V.apk"; \
cp $(ANDROID_APK) "$(DIST_DIR)/$$APK_NAME"; \
echo "Wrote $(DIST_DIR)/$$APK_NAME"
clean:
rm -rf $(DIST_DIR) $(ANDROID_DIR)/build $(ANDROID_DIR)/app/build
+137 -1
View File
@@ -1,3 +1,139 @@
<p align="center">
<img src="logo.png" width="280" alt="fuelish logo">
</p>
# fuelish
Extracting and organising fuel data from fuel receipts.
Extracts structured data from photos of German fuel/petrol receipts —
petrol station, transaction number, date, time, price per litre, litres,
discount, and total price — and lets you review, correct, save, and
export it from an Android app.
The extraction pipeline is self-hosted (no cloud AI APIs): Tesseract OCR
plus a small local LLM (`qwen2.5:3b`, served by Ollama), all running in
containers. It's built to eventually run on a Raspberry Pi 4 cluster
under k3s; see [`AGENT.md`](AGENT.md) for the pipeline's architecture,
preprocessing rationale, and current extraction-accuracy notes.
## How it fits together
- **Pipeline** (`tooling-container/`): preprocess → OCR → LLM-structure a
receipt photo into JSON. Runs either as three independently-inspectable
host scripts (for development/testing) or as an HTTP job API (for the
app).
- **Server** (`docker-compose.yml`): the same pipeline, served over HTTP
(`POST /jobs`, `GET /jobs/{id}`) so the Android app can submit a photo
and poll real per-stage progress, backed by an Ollama container.
- **Android app** (`android/`): take a photo, watch it get processed,
correct any misread fields, save it locally, browse everything saved
so far, and export to CSV or `.ods` (matching the layout of the
ground-truth spreadsheets under `testdata/`).
- **CI** (`.gitea/workflows/build.yml`): builds the Android debug APK on
every push and publishes it to dl.ladkau.de, using a custom build image
(`build-image/`) so the runner needs nothing preinstalled.
## Repository layout
```
tooling-container/ Pipeline source (preprocess.py, ocr.py, extract.py,
server.py, compare_ground_truth.py) and the
Dockerfile/VERSION for the fuelish/tools image
both the host scripts and docker-compose.yml run.
0-3_*.sh Numbered host scripts driving one pipeline stage
each, for independent inspection during
development (0_run_ollama.sh starts Ollama).
test_batch.sh Runs the full pipeline over a directory of test
receipts and compares against its ground-truth
.ods - see AGENT.md's "Current state".
docker-compose.yml Runs the pipeline as a long-lived HTTP job API
(api + ollama services) for the Android app.
android/ The Android app: Kotlin + Jetpack Compose, Room
for local storage, OkHttp against the server API,
CSV/.ods export.
build-image/ Dockerfile for the Android CI/release build
environment (JDK + Android SDK, no NDK needed -
the app has no native code), used by CI and by
the build-image.sh/run-image.sh/upload-image.sh
scripts at the repo root (see "Building" below).
.gitea/workflows/ CI: builds the Android APK and publishes it to
dl.ladkau.de.
Makefile Root Makefile - packages a versioned release APK
(`make android`).
testdata/ Real receipt photos + ground-truth .ods files
(gitignored, not checked in - personal data).
```
See [`AGENT.md`](AGENT.md) for the pipeline's architecture, why the
current preprocessing approach was chosen over heavier alternatives, and
the latest batch-accuracy numbers and known failure modes.
## Building
**The pipeline image** (`tooling-container/`), used by the numbered host
scripts, `test_batch.sh`, and `docker-compose.yml`:
```bash
./build-image.sh # builds fuelish/tools and the Android build-image
# (below), and pulls ollama/ollama
```
**The server**, once the tools image is built:
```bash
docker compose up -d # starts ollama + api; api reachable at :8080
```
**The Android app** (`android/`) needs the Android SDK
(`ANDROID_HOME` set). Locally, without installing it yourself:
```bash
cp registry.env.example registry.env # fill in your registry credentials
./build-image.sh # builds the android-builder image (JDK + Android SDK)
./run-image.sh # runs `make android` inside it, against this
# checkout -> dist/fuelish-<version>.apk
```
With the SDK already installed, `make android` (or
`cd android && ./gradlew assembleDebug`) works directly on the host.
**CI** builds the same way — `.gitea/workflows/build.yml` runs
`make android` inside the `build-image/` image (pulled from the
registry, published via `./upload-image.sh` after `./build-image.sh`)
and publishes the APK to dl.ladkau.de on every push to the default
branch.
## Running
**Inspect the pipeline stage by stage**, for development/debugging one
receipt at a time:
```bash
./0_run_ollama.sh # once, starts Ollama
./1_preprocess_image.sh -in photo.jpg -out scratch/
./2_ocr_image.sh -in scratch/03_final.png -out scratch/
./3_extract_data.sh -in scratch/ocr.txt -out scratch/ # -> extracted.json
```
**Batch-test against ground truth**:
```bash
./test_batch.sh -in testdata/<some-set> # needs a *.ods ground-truth
# file in that directory
```
**Serve it for the app**:
```bash
docker compose up -d
```
Then, in the Android app's Settings tab, point it at
`http://<this-host>:8080` (both HTTP-only - see the app's
`network_security_config.xml` - and LAN-only by design, no auth/TLS).
Take a photo, watch the 4-stage progress (upload → preprocess → OCR →
extract), correct anything the model got wrong, and save. The Receipts
tab lists everything saved so far and exports to CSV or `.ods`.
## License
MIT — see [`LICENSE`](LICENSE).
+27 -14
View File
@@ -1,9 +1,11 @@
#!/usr/bin/env bash
# Builds the fuelish tools image (tooling-container/Dockerfile) locally, tagged with
# tooling-container/VERSION and :latest, and pulls the other image the pipeline
# needs (ollama/ollama, used by 0_run_ollama.sh) so all images are ready before
# a pipeline run. Does not push - test it with the numbered pipeline scripts
# first, then publish with ./upload-image.sh.
# Builds every image this repo's pipeline/CI needs: the fuelish tools
# image (tooling-container/Dockerfile) and the Android CI/release build
# image (build-image/Dockerfile), each tagged from its own VERSION file,
# plus pulls ollama/ollama (used by 0_run_ollama.sh). Does not push - test
# the tools image with the numbered pipeline scripts and the
# android-builder image with ./run-image.sh, then publish both with
# ./upload-image.sh.
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")"
@@ -14,26 +16,37 @@ fail() { echo "PREFLIGHT FAIL: $*" >&2; exit 1; }
command -v docker >/dev/null 2>&1 \
|| fail "docker not found in PATH"
IMAGE="${REGISTRY_IMAGE:-fuelish/tools}"
TOOLS_IMAGE="${REGISTRY_IMAGE:-fuelish/tools}"
ANDROID_IMAGE="${REGISTRY_ANDROID_IMAGE:-cr.ladkau.de/fuelish/android-builder}"
if [ -f "$ROOT/registry.env" ]; then
# shellcheck disable=SC1091
source "$ROOT/registry.env"
IMAGE="${REGISTRY_IMAGE:-$IMAGE}"
TOOLS_IMAGE="${REGISTRY_IMAGE:-$TOOLS_IMAGE}"
ANDROID_IMAGE="${REGISTRY_ANDROID_IMAGE:-$ANDROID_IMAGE}"
fi
VERSION="$(<"$ROOT/tooling-container/VERSION")"
[ -n "$VERSION" ] || fail "tooling-container/VERSION is empty"
TOOLS_VERSION="$(<"$ROOT/tooling-container/VERSION")"
[ -n "$TOOLS_VERSION" ] || fail "tooling-container/VERSION is empty"
echo "== Building $IMAGE:$VERSION =="
ANDROID_VERSION="$(<"$ROOT/build-image/VERSION")"
[ -n "$ANDROID_VERSION" ] || fail "build-image/VERSION is empty"
echo "== Building $TOOLS_IMAGE:$TOOLS_VERSION =="
docker build \
-f "$ROOT/tooling-container/Dockerfile" \
-t "$IMAGE:$VERSION" \
-t "$IMAGE:latest" \
-t "$TOOLS_IMAGE:$TOOLS_VERSION" \
-t "$TOOLS_IMAGE:latest" \
"$ROOT/tooling-container"
echo "== Building $ANDROID_IMAGE:$ANDROID_VERSION =="
docker build \
-t "$ANDROID_IMAGE:$ANDROID_VERSION" \
-t "$ANDROID_IMAGE:latest" \
"$ROOT/build-image"
echo "== Pulling ollama/ollama (used by 0_run_ollama.sh) =="
docker pull ollama/ollama
echo "== Done =="
echo "Image: $IMAGE:$VERSION (and :latest)"
echo "Test it with the numbered pipeline scripts, then publish with ./upload-image.sh"
echo "Images: $TOOLS_IMAGE:$TOOLS_VERSION, $ANDROID_IMAGE:$ANDROID_VERSION (both also :latest)"
echo "Test the tools image with the numbered pipeline scripts, the android-builder image with ./run-image.sh, then publish both with ./upload-image.sh"
+73
View File
@@ -0,0 +1,73 @@
# Build environment for the Android app (android/): JDK + Android SDK
# cmdline-tools, pinned to the versions validated on the maintainer's dev
# machine (matching android/app/build.gradle.kts's compileSdk). Used both
# by .gitea/workflows/build.yml's CI container and by ../run-image.sh for
# a local, CI-equivalent build. Rebuild with ../build-image.sh whenever a
# version below changes.
#
# fuelish's Android app has no native/NDK code, so unlike a build image
# that also needs an NDK/CMake toolchain, this is just a JDK + the SDK's
# own cmdline-tools/platform/build-tools.
#
# Does NOT contain any secrets - registry/SFTP credentials are injected
# at job runtime from Gitea Actions secrets, never baked into this image.
FROM ubuntu:24.04
ARG NODE_VERSION=24.16.0
ARG ANDROID_CMDLINE_TOOLS_VERSION=9862592
ARG ANDROID_PLATFORM_VERSION=36
ARG ANDROID_BUILD_TOOLS_VERSION=36.0.0
ENV DEBIAN_FRONTEND=noninteractive
# openjdk-17-jdk-headless: Gradle/AGP's own minimum JDK (see
# android/app/build.gradle.kts's compileOptions).
# make: the root Makefile's `android` target (run by both run-image.sh
# and .gitea/workflows/build.yml).
# python3/python3-pil: android/scripts/gen_app_icons.py's Pillow
# dependency (see android/app/build.gradle.kts's generateAppIcons
# preBuild task) - apt's python3-pil is used instead of pip/venv since
# nothing else in this image needs a Python environment.
# git/curl/ca-certificates/unzip/xz-utils: checkout and SDK/Node tool
# downloads (xz-utils: Node's release tarball below is .tar.xz).
# openssh-client: the build workflow's `sftp` publish to dl.ladkau.de.
RUN apt-get update && apt-get install -y --no-install-recommends \
openjdk-17-jdk-headless make python3 python3-pil \
git curl ca-certificates unzip xz-utils openssh-client \
&& rm -rf /var/lib/apt/lists/*
# Node.js: actions/checkout@v4 and actions/upload-artifact@v3 (used by
# .gitea/workflows/build.yml) are both JS actions - Gitea's act_runner
# execs them with whatever `node` is on this image's PATH rather than
# injecting one itself (https://github.com/nektos/act/issues/107), and
# this base image doesn't bring one.
RUN curl -sSL -o /tmp/node.tar.xz \
"https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz" \
&& tar -xJf /tmp/node.tar.xz -C /usr/local --strip-components=1 \
&& rm /tmp/node.tar.xz
# Android SDK: the cmdline-tools' own sdkmanager installs exactly the
# platform/build-tools version android/app/build.gradle.kts pins -
# resolved here so no `docker run` of this image needs network access to
# install anything before `./gradlew assembleDebug` works. The Gradle
# build itself still needs network access at build time (Gradle/AGP/
# androidx dependency resolution against Google's Maven + Maven Central) -
# android/gradlew resolves its own pinned Gradle version, no Gradle
# binary is baked into this image.
ENV ANDROID_HOME=/opt/android-sdk \
ANDROID_SDK_ROOT=/opt/android-sdk
ENV PATH=${ANDROID_HOME}/cmdline-tools/latest/bin:${ANDROID_HOME}/platform-tools:${PATH}
RUN mkdir -p "${ANDROID_HOME}/cmdline-tools" \
&& curl -sSL -o /tmp/cmdline-tools.zip \
"https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_CMDLINE_TOOLS_VERSION}_latest.zip" \
&& unzip -q /tmp/cmdline-tools.zip -d "${ANDROID_HOME}/cmdline-tools" \
&& mv "${ANDROID_HOME}/cmdline-tools/cmdline-tools" "${ANDROID_HOME}/cmdline-tools/latest" \
&& rm /tmp/cmdline-tools.zip \
&& yes | sdkmanager --licenses >/dev/null \
&& sdkmanager --install \
"platform-tools" \
"platforms;android-${ANDROID_PLATFORM_VERSION}" \
"build-tools;${ANDROID_BUILD_TOOLS_VERSION}"
WORKDIR /workspace
+1
View File
@@ -0,0 +1 @@
0.1.0
+16 -6
View File
@@ -1,9 +1,19 @@
# Copy to registry.env (gitignored, never commit the real values) and fill
# in your registry credentials. Used by build-image.sh, the numbered
# pipeline scripts, and upload-image.sh. Only REGISTRY_IMAGE is needed for
# local builds/runs; REGISTRY/REGISTRY_USER/REGISTRY_PASSWORD are only
# needed to push with upload-image.sh.
REGISTRY=
REGISTRY_IMAGE=fuelish/tools
# in your registry credentials. Used by build-image.sh, upload-image.sh,
# run-image.sh, and the numbered pipeline scripts.
#
# REGISTRY_IMAGE (the OCR pipeline's tools image, tooling-container/) and
# REGISTRY_ANDROID_IMAGE (the Android CI/release build image,
# build-image/) must both be fully-qualified with the registry host -
# upload-image.sh does `docker push "$REGISTRY_IMAGE:..."` directly, so
# an unqualified name (e.g. "fuelish/tools") silently resolves to Docker
# Hub instead of $REGISTRY and fails to push there ("insufficient_scope:
# authorization failed") even though `docker login "$REGISTRY"`
# succeeded. Adjust the host below if your registry isn't cr.ladkau.de.
# REGISTRY/REGISTRY_USER/REGISTRY_PASSWORD are only needed to push with
# upload-image.sh.
REGISTRY=cr.ladkau.de
REGISTRY_IMAGE=cr.ladkau.de/fuelish/tools
REGISTRY_ANDROID_IMAGE=cr.ladkau.de/fuelish/android-builder
REGISTRY_USER=
REGISTRY_PASSWORD=
Executable
+69
View File
@@ -0,0 +1,69 @@
#!/usr/bin/env bash
# Runs `make android` inside the local android-builder image, mirroring
# what the Gitea Actions build workflow does - useful for testing
# build-image/ changes locally before pushing anything to a registry.
#
# Usage: ./run-image.sh [VERSION]
# VERSION is passed through to `make android` (see the Makefile's
# `android` target); omit it for its own git-tag-based default.
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"
IMAGE="${REGISTRY_ANDROID_IMAGE:-cr.ladkau.de/fuelish/android-builder}"
if [ -f "$ROOT/registry.env" ]; then
# shellcheck disable=SC1091
source "$ROOT/registry.env"
IMAGE="${REGISTRY_ANDROID_IMAGE:-$IMAGE}"
fi
IMAGE_TAG="$(<"$ROOT/build-image/VERSION")"
[ -n "$IMAGE_TAG" ] || fail "build-image/VERSION is empty"
IMAGE="$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 output left over from a previous local run
# (dist/, android/build, the generated app icon/splash resources) would
# make this run skip work a real fresh checkout wouldn't, silently hiding
# bugs that only show up in CI. Wipe them first so every run exercises a
# true from-scratch build, same as CI.
echo "== Cleaning generated build artifacts for a fresh build =="
rm -rf \
"$ROOT/dist" "$ROOT/android/build" "$ROOT/android/app/build" \
"$ROOT/android/app/src/main/res/drawable-nodpi" \
"$ROOT/android/app/src/main/res"/mipmap-mdpi "$ROOT/android/app/src/main/res"/mipmap-hdpi \
"$ROOT/android/app/src/main/res"/mipmap-xhdpi "$ROOT/android/app/src/main/res"/mipmap-xxhdpi \
"$ROOT/android/app/src/main/res"/mipmap-xxxhdpi
echo "== Running make android inside $IMAGE =="
# The container runs as root (needed for the SDK/Gradle caches baked into
# the image), so anything it writes into this bind mount - dist/,
# android/build, android/.gradle, 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 the build 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
make android
'
echo "== Done - artifact in dist/ =="
ls -la "$ROOT/dist"
+23 -11
View File
@@ -1,8 +1,10 @@
#!/usr/bin/env bash
# Pushes the fuelish tools image - already built locally with
# ./build-image.sh, and ideally verified with the numbered pipeline
# scripts - to a container registry, so the k3s cluster (or other
# machines) can pull it without rebuilding it.
# Pushes every image ./build-image.sh built locally - the fuelish tools
# image and the Android CI/release build image, each already verified
# (tools image with the numbered pipeline scripts, android-builder image
# with ./run-image.sh) - to a container registry, so the k3s cluster can
# pull the tools image and Gitea Actions can pull the android-builder
# image without rebuilding either.
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")"
@@ -21,21 +23,31 @@ source "$ROOT/registry.env"
: "${REGISTRY:?registry.env must set REGISTRY}"
: "${REGISTRY_IMAGE:?registry.env must set REGISTRY_IMAGE}"
: "${REGISTRY_ANDROID_IMAGE:?registry.env must set REGISTRY_ANDROID_IMAGE}"
: "${REGISTRY_USER:?registry.env must set REGISTRY_USER}"
: "${REGISTRY_PASSWORD:?registry.env must set REGISTRY_PASSWORD}"
VERSION="$(<"$ROOT/tooling-container/VERSION")"
[ -n "$VERSION" ] || fail "tooling-container/VERSION is empty"
TOOLS_VERSION="$(<"$ROOT/tooling-container/VERSION")"
[ -n "$TOOLS_VERSION" ] || fail "tooling-container/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"
ANDROID_VERSION="$(<"$ROOT/build-image/VERSION")"
[ -n "$ANDROID_VERSION" ] || fail "build-image/VERSION is empty"
docker image inspect "$REGISTRY_IMAGE:$TOOLS_VERSION" >/dev/null 2>&1 \
|| fail "$REGISTRY_IMAGE:$TOOLS_VERSION not found locally - run ./build-image.sh first"
docker image inspect "$REGISTRY_ANDROID_IMAGE:$ANDROID_VERSION" >/dev/null 2>&1 \
|| fail "$REGISTRY_ANDROID_IMAGE:$ANDROID_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"
echo "== Pushing $REGISTRY_IMAGE:$TOOLS_VERSION and :latest =="
docker push "$REGISTRY_IMAGE:$TOOLS_VERSION"
docker push "$REGISTRY_IMAGE:latest"
echo "== Pushing $REGISTRY_ANDROID_IMAGE:$ANDROID_VERSION and :latest =="
docker push "$REGISTRY_ANDROID_IMAGE:$ANDROID_VERSION"
docker push "$REGISTRY_ANDROID_IMAGE:latest"
echo "== Done =="
echo "Image: $REGISTRY_IMAGE:$VERSION"
echo "Images: $REGISTRY_IMAGE:$TOOLS_VERSION, $REGISTRY_ANDROID_IMAGE:$ANDROID_VERSION"