Move APK runner to a separate host; openssl-based keystore
- gen-keystore.sh now builds a PKCS12 keystore with openssl (no JDK/keytool), and signing.gradle declares storeType PKCS12. - Runner is meant to run on a roomy server (the app host lacks disk for the Android SDK); added runner/README.md with deployment steps. Removed the runner that was registered on the app host. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
d293e53ee6
commit
994aaa88d1
4 changed files with 68 additions and 17 deletions
11
README.md
11
README.md
|
|
@ -174,17 +174,16 @@ On success the buyer receives the QR email. Re-sends of the same submission are
|
||||||
|
|
||||||
## APK builds (Forgejo CI)
|
## APK builds (Forgejo CI)
|
||||||
|
|
||||||
The runner in `runner/` is registered against `git.mowden.top` and builds a signed APK whenever a `vX.Y.Z` tag is pushed.
|
A Forgejo Actions runner builds a signed APK whenever a `vX.Y.Z` tag is pushed. **Run the runner on the roomy server, not the app host** — the Android SDK needs several GB the app host doesn't have. See [`runner/README.md`](./runner/README.md) for deploying it.
|
||||||
|
|
||||||
**One-time setup:**
|
**One-time setup:**
|
||||||
1. Generate a release keystore and print the secrets: `bash scripts/gen-keystore.sh`
|
1. Generate a release keystore (PKCS12, uses openssl — no JDK needed) and print the secrets: `bash scripts/gen-keystore.sh`
|
||||||
2. In Forgejo → CampgroundTickets → *Settings → Actions → Secrets*, add:
|
2. In Forgejo → CampgroundTickets → *Settings → Actions → Secrets*, add:
|
||||||
`ANDROID_KEYSTORE_B64`, `ANDROID_KEYSTORE_PASSWORD`, `ANDROID_KEY_ALIAS`, `ANDROID_KEY_PASSWORD`.
|
`ANDROID_KEYSTORE_B64`, `ANDROID_KEYSTORE_PASSWORD`, `ANDROID_KEY_ALIAS`, `ANDROID_KEY_PASSWORD`
|
||||||
|
(for PKCS12 the keystore and key passwords are the same value).
|
||||||
3. Keep the keystore file safe forever — Obtainium updates require the same signing key on every release.
|
3. Keep the keystore file safe forever — Obtainium updates require the same signing key on every release.
|
||||||
|
|
||||||
**Release:** `git tag v0.1.0 && git push origin v0.1.0` → the workflow builds `camp-scan-v0.1.0.apk` and attaches it to the Forgejo release. Obtainium picks it up (see INSTALL.md).
|
**Release:** `git tag v0.1.0 && git push origin v0.1.0` → the workflow builds `camp-scan-v0.1.0.apk` and attaches it to the Forgejo release. Obtainium picks it up (see INSTALL.md). The build runs jobs in a `node:22-bookworm` container and installs the Android SDK itself; the first run may need an SDK/Gradle tweak, so shake it out with a throwaway `v0.0.1` tag first.
|
||||||
|
|
||||||
The runner runs jobs in a `node:22-bookworm` container and installs the Android SDK itself. To (re)start it: `cd runner && cp .env.example .env` (set `REGISTRATION_TOKEN`, `DOCKER_GID`) `&& docker compose up -d`.
|
|
||||||
|
|
||||||
## API reference (staff endpoints require `Authorization: Bearer <token>`)
|
## API reference (staff endpoints require `Authorization: Bearer <token>`)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ gradle.projectsLoaded {
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
campscanRelease {
|
campscanRelease {
|
||||||
storeFile file(storeFilePath)
|
storeFile file(storeFilePath)
|
||||||
|
storeType 'PKCS12'
|
||||||
storePassword System.getenv('CAMPSCAN_STORE_PASSWORD')
|
storePassword System.getenv('CAMPSCAN_STORE_PASSWORD')
|
||||||
keyAlias System.getenv('CAMPSCAN_KEY_ALIAS')
|
keyAlias System.getenv('CAMPSCAN_KEY_ALIAS')
|
||||||
keyPassword System.getenv('CAMPSCAN_KEY_PASSWORD')
|
keyPassword System.getenv('CAMPSCAN_KEY_PASSWORD')
|
||||||
|
|
|
||||||
39
runner/README.md
Normal file
39
runner/README.md
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
# Forgejo Actions runner (APK builds)
|
||||||
|
|
||||||
|
Run this **on the roomy server** (where Forgejo lives / where there's disk for
|
||||||
|
the ~3–4 GB Android SDK), **not** on the app host — the Android build needs
|
||||||
|
several GB of scratch space that the small app host doesn't have.
|
||||||
|
|
||||||
|
## Deploy
|
||||||
|
|
||||||
|
1. Copy this `runner/` directory to the roomy server (or clone the repo there).
|
||||||
|
2. In Forgejo → `Beartaria/CampgroundTickets` → **Settings → Actions → Runners → Create new runner**, copy the **registration token**.
|
||||||
|
3. Create `.env` from the example and fill it in:
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
# REGISTRATION_TOKEN=<the token from Forgejo>
|
||||||
|
# DOCKER_GID=<output of: stat -c %g /var/run/docker.sock on THIS server>
|
||||||
|
```
|
||||||
|
4. Start it:
|
||||||
|
```bash
|
||||||
|
mkdir -p data && sudo chown -R 1000:1000 data
|
||||||
|
docker compose up -d
|
||||||
|
docker compose logs -f # expect "declared successfully" then "poller launched"
|
||||||
|
```
|
||||||
|
|
||||||
|
The runner advertises the `docker` label; the build workflow (`.forgejo/workflows/build-apk.yml`) runs jobs in a `node:22-bookworm` container and installs the Android SDK itself.
|
||||||
|
|
||||||
|
## Build an APK
|
||||||
|
|
||||||
|
1. One-time: generate a keystore with `../scripts/gen-keystore.sh` and add the four
|
||||||
|
`ANDROID_*` secrets to the repo (see the main README → *APK builds*).
|
||||||
|
2. Push a tag: `git tag v0.1.0 && git push origin v0.1.0`.
|
||||||
|
3. The runner builds `camp-scan-v0.1.0.apk` and attaches it to a Forgejo release; Obtainium picks it up.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- The runner I initially registered on the app host has been removed. If Forgejo
|
||||||
|
still lists an **offline** `camptickets-runner`, delete it from
|
||||||
|
*Settings → Actions → Runners*.
|
||||||
|
- `DOCKER_GID` must match the roomy server's docker socket group, or the runner
|
||||||
|
can't reach the Docker daemon.
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Generate a release signing keystore for the Android APK (run ONCE), then print
|
# Generate a PKCS12 release signing keystore for the Android APK (run ONCE),
|
||||||
# the base64 + values to paste into Forgejo repo secrets. Keep the keystore file
|
# using openssl (no JDK/keytool required). Then print the base64 + values to
|
||||||
# safe and constant forever — losing it or changing it breaks Obtainium updates
|
# paste into Forgejo repo secrets.
|
||||||
# (a differently-signed APK will not install over the old one).
|
#
|
||||||
|
# Keep the keystore file safe and constant forever — losing it or changing it
|
||||||
|
# breaks Obtainium updates (a differently-signed APK won't install over the old
|
||||||
|
# one). PKCS12 uses ONE password for both the store and the key.
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
KEYSTORE="${1:-campscan-release.keystore}"
|
KEYSTORE="${1:-campscan-release.keystore}"
|
||||||
|
|
@ -12,17 +15,26 @@ if [ -f "$KEYSTORE" ]; then
|
||||||
echo "Refusing to overwrite existing $KEYSTORE" >&2
|
echo "Refusing to overwrite existing $KEYSTORE" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
command -v openssl >/dev/null || { echo "openssl not found" >&2; exit 1; }
|
||||||
|
|
||||||
read -r -s -p "Choose a keystore password: " STOREPASS; echo
|
read -r -s -p "Choose a keystore password: " STOREPASS; echo
|
||||||
read -r -s -p "Confirm keystore password: " STOREPASS2; echo
|
read -r -s -p "Confirm keystore password: " STOREPASS2; echo
|
||||||
[ "$STOREPASS" = "$STOREPASS2" ] || { echo "Passwords do not match" >&2; exit 1; }
|
[ "$STOREPASS" = "$STOREPASS2" ] || { echo "Passwords do not match" >&2; exit 1; }
|
||||||
|
[ ${#STOREPASS} -ge 6 ] || { echo "Use at least 6 characters" >&2; exit 1; }
|
||||||
|
|
||||||
keytool -genkeypair -v \
|
TMP="$(mktemp -d)"
|
||||||
-keystore "$KEYSTORE" \
|
trap 'rm -rf "$TMP"' EXIT
|
||||||
-alias "$ALIAS" \
|
|
||||||
-keyalg RSA -keysize 2048 -validity 10000 \
|
# Self-signed cert + key, valid ~27 years, then bundle into a PKCS12 keystore.
|
||||||
-storepass "$STOREPASS" -keypass "$STOREPASS" \
|
openssl req -x509 -newkey rsa:2048 -sha256 -days 10000 -nodes \
|
||||||
-dname "CN=Beartaria Campgrounds, OU=Gate, O=Beartaria, L=, ST=, C=US"
|
-keyout "$TMP/key.pem" -out "$TMP/cert.pem" \
|
||||||
|
-subj "/CN=Beartaria Campgrounds/OU=Gate/O=Beartaria/C=US" 2>/dev/null
|
||||||
|
|
||||||
|
openssl pkcs12 -export \
|
||||||
|
-inkey "$TMP/key.pem" -in "$TMP/cert.pem" \
|
||||||
|
-name "$ALIAS" \
|
||||||
|
-out "$KEYSTORE" \
|
||||||
|
-passout pass:"$STOREPASS"
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "==================== Forgejo repo secrets ===================="
|
echo "==================== Forgejo repo secrets ===================="
|
||||||
|
|
@ -30,7 +42,7 @@ echo "Set these under: git.mowden.top -> CampgroundTickets -> Settings -> Action
|
||||||
echo
|
echo
|
||||||
echo "ANDROID_KEY_ALIAS = $ALIAS"
|
echo "ANDROID_KEY_ALIAS = $ALIAS"
|
||||||
echo "ANDROID_KEYSTORE_PASSWORD = (the password you just entered)"
|
echo "ANDROID_KEYSTORE_PASSWORD = (the password you just entered)"
|
||||||
echo "ANDROID_KEY_PASSWORD = (the same password)"
|
echo "ANDROID_KEY_PASSWORD = (the SAME password — PKCS12 uses one)"
|
||||||
echo "ANDROID_KEYSTORE_B64 = (paste the block below, single line)"
|
echo "ANDROID_KEYSTORE_B64 = (paste the block below, single line)"
|
||||||
echo
|
echo
|
||||||
base64 -w0 "$KEYSTORE"; echo
|
base64 -w0 "$KEYSTORE"; echo
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue