Compare commits

...

7 Commits

Author SHA1 Message Date
joejulian a6340f5c9e Merge pull request 'Fix CI APK JDK selection' (#8) from bugfix/ci-apk-java-selection into main
ci / lint-test (push) Successful in 3m41s
ci / build (push) Successful in 6m11s
2026-04-20 04:30:31 +00:00
Joe Julian 0adf1b8826 Run CI for pull requests
ci / lint-test (pull_request) Successful in 5m56s
ci / build (pull_request) Successful in 5m39s
2026-04-19 21:17:49 -07:00
Joe Julian c517794182 Provision Java 25 directly in CI 2026-04-19 20:37:46 -07:00
Joe Julian b511ab4dc0 Fix CI APK JDK selection 2026-04-19 20:27:14 -07:00
joejulian 7b06388712 Merge pull request 'Add Android autofill chooser and learned app binding' (#7) from feature/android-autofill-chooser into main
ci / lint-test (push) Successful in 3m14s
ci / build (push) Failing after 3m4s
2026-04-20 00:02:55 +00:00
Joe Julian fea1a75cdf Keep release signing secrets out of APK build logs 2026-04-18 22:16:25 -07:00
Joe Julian 0dfaeef7bf Require dedicated release signing for APK builds 2026-04-18 22:00:56 -07:00
7 changed files with 111 additions and 15 deletions
+9 -3
View File
@@ -45,8 +45,8 @@ Use this skill together with the installed `android-emulator-debug` skill. That
## Build Workflow ## Build Workflow
1. Verify the JDK/SDK paths match the known working environment. 1. Verify the JDK/SDK paths match the known working environment.
2. Build with `make apk`. 2. Build with `make apk` for debug validation, or `make apk-release` when validating production signing behavior.
3. If `make apk` fails, inspect the effective `JAVA_HOME`, `ANDROID_SDK_ROOT`, and `ANDROID_NDK_ROOT` before changing code. 3. If the build fails, inspect the effective `JAVA_HOME`, `ANDROID_SDK_ROOT`, and `ANDROID_NDK_ROOT` before changing code.
4. If the problem is Android-only, avoid desktop-only conclusions from `go test ./...`. 4. If the problem is Android-only, avoid desktop-only conclusions from `go test ./...`.
Typical local build: Typical local build:
@@ -55,6 +55,12 @@ Typical local build:
JAVA_HOME=/usr/lib/jvm/java-25-openjdk make apk JAVA_HOME=/usr/lib/jvm/java-25-openjdk make apk
``` ```
Typical local release build:
```sh
JAVA_HOME=/usr/lib/jvm/java-25-openjdk make apk-release
```
## Emulator Workflow ## Emulator Workflow
1. Reuse an existing emulator session if one is already running. 1. Reuse an existing emulator session if one is already running.
@@ -79,7 +85,7 @@ adb shell dumpsys window | rg 'mCurrentFocus|mFocusedApp'
## Validation Checklist ## Validation Checklist
- APK builds successfully with `make apk`. - APK builds successfully with the intended target: `make apk` for debug validation or `make apk-release` for release-signing validation.
- App launches to `org.julianfamily.keepassgo/org.gioui.GioActivity`. - App launches to `org.julianfamily.keepassgo/org.gioui.GioActivity`.
- Screenshot shows the expected screen, not just a black frame. - Screenshot shows the expected screen, not just a black frame.
- `logcat` shows no app crash or Android runtime fatal error. - `logcat` shows no app crash or Android runtime fatal error.
+7 -1
View File
@@ -52,11 +52,17 @@ The installed package version must correspond to the committed source, not a dir
Use the repo's known-good local JDK unless the environment already proves otherwise: Use the repo's known-good local JDK unless the environment already proves otherwise:
```sh ```sh
JAVA_HOME=/usr/lib/jvm/java-25-openjdk make apk JAVA_HOME=/usr/lib/jvm/java-25-openjdk make apk-release
``` ```
If that JDK is unavailable on the current host, use the working replacement already established for the machine and say so in the closeout. If that JDK is unavailable on the current host, use the working replacement already established for the machine and say so in the closeout.
- `ship it` must use the dedicated release keystore flow, not Gio's implicit debug or temporary signing path.
- The default local release-signing paths are:
`~/.config/keepassgo/android-release.keystore`
`~/.config/keepassgo/android-release.pass`
- If those files are unavailable, stop and fix signing instead of shipping a differently signed APK.
### 4. Zip The APK ### 4. Zip The APK
- Create the ZIP under the globally required temporary secret-safe directory. - Create the ZIP under the globally required temporary secret-safe directory.
+21 -4
View File
@@ -8,6 +8,9 @@ on:
- "v*" - "v*"
- "release-*" - "release-*"
- "[0-9]+.[0-9]+.[0-9]+*" - "[0-9]+.[0-9]+.[0-9]+*"
pull_request:
branches:
- main
permissions: permissions:
contents: write contents: write
@@ -16,7 +19,6 @@ env:
GO_VERSION: "1.26.1" GO_VERSION: "1.26.1"
ANDROID_SDK_ROOT: /opt/android-sdk ANDROID_SDK_ROOT: /opt/android-sdk
ANDROID_NDK_ROOT: /opt/android-sdk/ndk ANDROID_NDK_ROOT: /opt/android-sdk/ndk
JAVA_HOME: /usr/lib/jvm/java-21-openjdk-amd64
DIST_DIR: dist DIST_DIR: dist
jobs: jobs:
@@ -31,6 +33,12 @@ jobs:
with: with:
go-version: ${{ env.GO_VERSION }} go-version: ${{ env.GO_VERSION }}
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "25"
- name: Install native build dependencies - name: Install native build dependencies
shell: bash shell: bash
run: | run: |
@@ -78,6 +86,12 @@ jobs:
with: with:
go-version: ${{ env.GO_VERSION }} go-version: ${{ env.GO_VERSION }}
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "25"
- name: Install native build dependencies - name: Install native build dependencies
shell: bash shell: bash
run: | run: |
@@ -135,11 +149,14 @@ jobs:
shell: bash shell: bash
run: | run: |
set -euo pipefail set -euo pipefail
signkey_path="$(mktemp)" mkdir -p build/ci-signing
trap 'rm -f -- "$signkey_path"' EXIT signkey_path="$(pwd)/build/ci-signing/android-release.keystore"
signpass_path="$(pwd)/build/ci-signing/android-release.pass"
trap 'rm -f -- "$signkey_path" "$signpass_path"' EXIT
printf '%s' '${{ secrets.APK_SIGNKEY_B64 }}' | base64 -d > "$signkey_path" printf '%s' '${{ secrets.APK_SIGNKEY_B64 }}' | base64 -d > "$signkey_path"
printf '%s' '${{ secrets.APK_SIGNPASS }}' > "$signpass_path"
export APP_VERSION="$(git describe --tags --always --dirty)" export APP_VERSION="$(git describe --tags --always --dirty)"
make apk SIGNKEY="$signkey_path" SIGNPASS='${{ secrets.APK_SIGNPASS }}' make apk-release RELEASE_SIGNKEY="$signkey_path" RELEASE_SIGNPASS_FILE="$signpass_path"
cp build/keepassgo.apk "${DIST_DIR}/keepassgo.apk" cp build/keepassgo.apk "${DIST_DIR}/keepassgo.apk"
- name: Upload CI artifacts - name: Upload CI artifacts
+1 -1
View File
@@ -177,7 +177,7 @@ These features are product requirements, not “nice to have” ideas.
local `ANDROID_NDK_ROOT=/opt/android-ndk`, local `ANDROID_NDK_ROOT=/opt/android-ndk`,
CI `ANDROID_NDK_ROOT=/opt/android-sdk/ndk`, CI `ANDROID_NDK_ROOT=/opt/android-sdk/ndk`,
local `JAVA_HOME=/usr/lib/jvm/java-25-openjdk`, local `JAVA_HOME=/usr/lib/jvm/java-25-openjdk`,
CI `JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64`. CI `JAVA_HOME` provided by `actions/setup-java` with Temurin 25.
- Remember the known Android runtime regression: - Remember the known Android runtime regression:
`gioui.org v0.9.0` produced a black screen on the `KeepassGoAPI35` emulator, while `gioui.org v0.8.0` rendered correctly. Treat Gio upgrades on Android as regression-sensitive and verify them on-device or in the emulator. `gioui.org v0.9.0` produced a black screen on the `KeepassGoAPI35` emulator, while `gioui.org v0.8.0` rendered correctly. Treat Gio upgrades on Android as regression-sensitive and verify them on-device or in the emulator.
- When validating an APK in the emulator, prefer the known KeePassGO setup: - When validating an APK in the emulator, prefer the known KeePassGO setup:
+26 -2
View File
@@ -6,10 +6,22 @@ Build the APK with:
make apk make apk
``` ```
Build the release-signed APK with:
```sh
make apk-release
```
`make apk` uses a local Java 25 install when `JAVA_HOME` points to one. `make apk` uses a local Java 25 install when `JAVA_HOME` points to one.
If the host does not have a working Java 25 install, it falls back to the If the host does not have a working Java 25 install, it falls back to the
repo-managed Docker image in `packaging/docker/android-apk/`, which also builds repo-managed Docker image in `packaging/docker/android-apk/`, which also builds
with Java 25. with Java 25. CI provisions Java 25 directly in the build job so release builds
use that same local path instead of nested Docker.
`make apk` remains a developer build path and may use Gio's default debug or
ephemeral signing behavior if no explicit signing key is provided.
`make apk-release` is the production-signing path and fails unless a dedicated
release keystore and password file are present.
Environment: Environment:
@@ -23,6 +35,14 @@ Environment:
- `APK_VERSION` overrides the packaged app version. - `APK_VERSION` overrides the packaged app version.
- `ANDROID_MIN_SDK` overrides the minimum supported Android SDK. - `ANDROID_MIN_SDK` overrides the minimum supported Android SDK.
- `ANDROID_TARGET_SDK` overrides the target Android SDK. - `ANDROID_TARGET_SDK` overrides the target Android SDK.
- `SIGNPASS_FILE` provides the signing password by file instead of a command-line argument.
- `RELEASE_SIGNKEY` overrides the release keystore path used by `make apk-release`.
- `RELEASE_SIGNPASS_FILE` overrides the password file path used by `make apk-release`.
Default release-signing paths:
- `~/.config/keepassgo/android-release.keystore`
- `~/.config/keepassgo/android-release.pass`
Installed machine prerequisites expected by this repo: Installed machine prerequisites expected by this repo:
@@ -38,13 +58,17 @@ The repo tracks `gogio` as a Go tool, and the local build runs through:
go tool gogio -target android ./cmd/keepassgo ... go tool gogio -target android ./cmd/keepassgo ...
``` ```
The release target wraps `make apk` and injects explicit signing credentials so
local release builds and CI use the same stable key without echoing the release
password in build logs.
The Android build uses the branded icon asset at: The Android build uses the branded icon asset at:
- `internal/assets/keepassgo-icon.png` - `internal/assets/keepassgo-icon.png`
Note: Note:
- KeePassGO's documented Android build uses Java 25 locally. - KeePassGO's documented Android build uses Java 25 locally and in CI.
- If that host setup is unavailable, `make apk` falls back to the Docker image - If that host setup is unavailable, `make apk` falls back to the Docker image
so the build still runs under Java 25 instead of encoding a newer host JDK as so the build still runs under Java 25 instead of encoding a newer host JDK as
a requirement. a requirement.
+33 -2
View File
@@ -12,6 +12,9 @@ ANDROID_MIN_SDK ?= 28
ANDROID_TARGET_SDK ?= 35 ANDROID_TARGET_SDK ?= 35
SIGNKEY ?= SIGNKEY ?=
SIGNPASS ?= SIGNPASS ?=
SIGNPASS_FILE ?=
RELEASE_SIGNKEY ?= $(HOME)/.config/keepassgo/android-release.keystore
RELEASE_SIGNPASS_FILE ?= $(HOME)/.config/keepassgo/android-release.pass
ARCH_PKG_DIR ?= packaging/archlinux/keepassgo-git ARCH_PKG_DIR ?= packaging/archlinux/keepassgo-git
ARCH_PKG_TMPL ?= $(ARCH_PKG_DIR)/PKGBUILD.tmpl ARCH_PKG_TMPL ?= $(ARCH_PKG_DIR)/PKGBUILD.tmpl
ARCH_PKGBUILD ?= $(ARCH_PKG_DIR)/PKGBUILD ARCH_PKGBUILD ?= $(ARCH_PKG_DIR)/PKGBUILD
@@ -26,7 +29,22 @@ ifneq ($(strip $(SIGNPASS)),)
GOGIO_SIGN_FLAGS += -signpass $(SIGNPASS) GOGIO_SIGN_FLAGS += -signpass $(SIGNPASS)
endif endif
.PHONY: apk apk-local apk-container apk-container-image archlinux-pkgbuild browser-bridge browser-extension-validate CONTAINER_SIGNKEY_MOUNT :=
CONTAINER_SIGNPASSFILE_MOUNT :=
CONTAINER_SIGN_ARGS :=
ifneq ($(strip $(SIGNKEY)),)
CONTAINER_SIGNKEY_MOUNT += -v "$(dir $(abspath $(SIGNKEY))):$(dir $(abspath $(SIGNKEY))):ro"
CONTAINER_SIGN_ARGS += SIGNKEY="$(abspath $(SIGNKEY))"
endif
ifneq ($(strip $(SIGNPASS)),)
CONTAINER_SIGN_ARGS += SIGNPASS="$(SIGNPASS)"
endif
ifneq ($(strip $(SIGNPASS_FILE)),)
CONTAINER_SIGNPASSFILE_MOUNT += -v "$(dir $(abspath $(SIGNPASS_FILE))):$(dir $(abspath $(SIGNPASS_FILE))):ro"
CONTAINER_SIGN_ARGS += SIGNPASS_FILE="$(abspath $(SIGNPASS_FILE))"
endif
.PHONY: apk apk-local apk-release apk-container apk-container-image archlinux-pkgbuild browser-bridge browser-extension-validate
apk: apk:
@if [ -x "$(JAVA_HOME)/bin/java" ] && "$(JAVA_HOME)/bin/java" -version 2>&1 | grep -q 'version "25'; then \ @if [ -x "$(JAVA_HOME)/bin/java" ] && "$(JAVA_HOME)/bin/java" -version 2>&1 | grep -q 'version "25'; then \
$(MAKE) apk-local JAVA_HOME="$(JAVA_HOME)"; \ $(MAKE) apk-local JAVA_HOME="$(JAVA_HOME)"; \
@@ -43,6 +61,12 @@ apk-local: android/keepassgo-android.jar
@test -d "$(ANDROID_SDK_ROOT)/platforms/android-$(ANDROID_TARGET_SDK)" || { echo "Android platform android-$(ANDROID_TARGET_SDK) is missing"; exit 1; } @test -d "$(ANDROID_SDK_ROOT)/platforms/android-$(ANDROID_TARGET_SDK)" || { echo "Android platform android-$(ANDROID_TARGET_SDK) is missing"; exit 1; }
@test -d "$(ANDROID_SDK_ROOT)/build-tools" || { echo "Android build-tools are missing"; exit 1; } @test -d "$(ANDROID_SDK_ROOT)/build-tools" || { echo "Android build-tools are missing"; exit 1; }
@mkdir -p "$(dir $(APK_OUT))" @mkdir -p "$(dir $(APK_OUT))"
@set -eu; \
if [ -n "$(SIGNPASS_FILE)" ]; then \
test -f "$(SIGNPASS_FILE)" || { echo "SIGNPASS_FILE does not exist: $(SIGNPASS_FILE)"; exit 1; }; \
export GOGIO_SIGNPASS="$$(tr -d '\r\n' < "$(SIGNPASS_FILE)")"; \
test -n "$$GOGIO_SIGNPASS" || { echo "SIGNPASS_FILE is empty: $(SIGNPASS_FILE)"; exit 1; }; \
fi; \
ANDROID_HOME="$(ANDROID_SDK_ROOT)" \ ANDROID_HOME="$(ANDROID_SDK_ROOT)" \
ANDROID_SDK_ROOT="$(ANDROID_SDK_ROOT)" \ ANDROID_SDK_ROOT="$(ANDROID_SDK_ROOT)" \
ANDROID_NDK_ROOT="$(ANDROID_NDK_ROOT)" \ ANDROID_NDK_ROOT="$(ANDROID_NDK_ROOT)" \
@@ -59,6 +83,11 @@ apk-local: android/keepassgo-android.jar
-icon internal/assets/keepassgo-icon.png \ -icon internal/assets/keepassgo-icon.png \
./cmd/keepassgo ./cmd/keepassgo
apk-release:
@test -f "$(RELEASE_SIGNKEY)" || { echo "Release signing key not found at $(RELEASE_SIGNKEY)"; exit 1; }
@test -f "$(RELEASE_SIGNPASS_FILE)" || { echo "Release signing password file not found at $(RELEASE_SIGNPASS_FILE)"; exit 1; }
@$(MAKE) apk SIGNKEY="$(abspath $(RELEASE_SIGNKEY))" SIGNPASS_FILE="$(abspath $(RELEASE_SIGNPASS_FILE))" JAVA_HOME="$(JAVA_HOME)"
apk-container: apk-container-image apk-container: apk-container-image
@command -v docker >/dev/null 2>&1 || { echo "docker is required for apk-container"; exit 1; } @command -v docker >/dev/null 2>&1 || { echo "docker is required for apk-container"; exit 1; }
@test -d "$(ANDROID_SDK_ROOT)" || { echo "ANDROID_SDK_ROOT must point to an Android SDK install"; exit 1; } @test -d "$(ANDROID_SDK_ROOT)" || { echo "ANDROID_SDK_ROOT must point to an Android SDK install"; exit 1; }
@@ -69,11 +98,13 @@ apk-container: apk-container-image
-w "$(CURDIR)" \ -w "$(CURDIR)" \
-v "$(ANDROID_SDK_ROOT):$(ANDROID_SDK_ROOT)" \ -v "$(ANDROID_SDK_ROOT):$(ANDROID_SDK_ROOT)" \
-v "$(ANDROID_NDK_ROOT):$(ANDROID_NDK_ROOT)" \ -v "$(ANDROID_NDK_ROOT):$(ANDROID_NDK_ROOT)" \
$(CONTAINER_SIGNKEY_MOUNT) \
$(CONTAINER_SIGNPASSFILE_MOUNT) \
-e ANDROID_SDK_ROOT="$(ANDROID_SDK_ROOT)" \ -e ANDROID_SDK_ROOT="$(ANDROID_SDK_ROOT)" \
-e ANDROID_NDK_ROOT="$(ANDROID_NDK_ROOT)" \ -e ANDROID_NDK_ROOT="$(ANDROID_NDK_ROOT)" \
-e JAVA_HOME=/opt/java/openjdk \ -e JAVA_HOME=/opt/java/openjdk \
$(APK_BUILD_IMAGE) \ $(APK_BUILD_IMAGE) \
make apk-local JAVA_HOME=/opt/java/openjdk make apk-local JAVA_HOME=/opt/java/openjdk $(CONTAINER_SIGN_ARGS)
apk-container-image: apk-container-image:
@command -v docker >/dev/null 2>&1 || { echo "docker is required for apk-container-image"; exit 1; } @command -v docker >/dev/null 2>&1 || { echo "docker is required for apk-container-image"; exit 1; }
+14 -2
View File
@@ -95,8 +95,20 @@ make apk
`make apk` prefers a local Java 25 install at `JAVA_HOME`. If that is not `make apk` prefers a local Java 25 install at `JAVA_HOME`. If that is not
available, it falls back to the repo-managed Docker build image, which also available, it falls back to the repo-managed Docker build image, which also
uses Java 25. You still need the Android SDK and NDK installed and configured uses Java 25. CI provisions Java 25 directly in the build job so release
for real device or release packaging. packaging follows that same local path. You still need the Android SDK and NDK
installed and configured for real device or release packaging.
Release package:
```bash
make apk-release
```
`make apk-release` is the production-signing path. It requires a dedicated
release keystore at `~/.config/keepassgo/android-release.keystore` and a
password file at `~/.config/keepassgo/android-release.pass`, unless you
override `RELEASE_SIGNKEY` and `RELEASE_SIGNPASS_FILE`.
## Automation ## Automation