From 894f132e34a53c6019fa8cb0f0ecfcaffc927fea Mon Sep 17 00:00:00 2001 From: "Timur Kh." Date: Thu, 22 Oct 2026 21:58:57 +0300 Subject: [PATCH] fix ots --- .gitea/workflows/android-release.yml | 28 ++++++++++++++++++++++++++-- android/app/build.gradle.kts | 17 +++++++++++++---- docs/production-builds.md | 22 ++++++++++++++++++++-- 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/.gitea/workflows/android-release.yml b/.gitea/workflows/android-release.yml index e28e9a4..9fdbffb 100644 --- a/.gitea/workflows/android-release.yml +++ b/.gitea/workflows/android-release.yml @@ -74,6 +74,30 @@ jobs: printf '%s' "$KEYSTORE_BASE64" | base64 -d > "$RUNNER_TEMP/kadence.keystore" echo "signed=true" >> "$GITHUB_OUTPUT" + # A keystore without its password and alias cannot sign anything. Gradle + # would eventually fail in `packageRelease`, after the build and R8 have + # run; checking here says which secret is missing in a few seconds. + - name: Check the signing secrets + if: steps.key.outputs.signed == 'true' + env: + STORE_PASSWORD: ${{ secrets.KADENCE_KEYSTORE_PASSWORD }} + KEY_ALIAS: ${{ secrets.KADENCE_KEY_ALIAS }} + KEY_PASSWORD: ${{ secrets.KADENCE_KEY_PASSWORD }} + run: | + missing=() + [ -n "${STORE_PASSWORD:-}" ] || missing+=(KADENCE_KEYSTORE_PASSWORD) + [ -n "${KEY_ALIAS:-}" ] || missing+=(KADENCE_KEY_ALIAS) + [ -n "${KEY_PASSWORD:-}" ] || missing+=(KADENCE_KEY_PASSWORD) + if [ ${#missing[@]} -gt 0 ]; then + echo "::error::Missing signing secret(s): ${missing[*]}" + exit 1 + fi + if ! keytool -list -keystore "$RUNNER_TEMP/kadence.keystore" \ + -storepass "$STORE_PASSWORD" -alias "$KEY_ALIAS" >/dev/null 2>&1; then + echo "::error::KADENCE_KEYSTORE_PASSWORD or KADENCE_KEY_ALIAS does not open the keystore." + exit 1 + fi + - name: Resolve the version name run: | if [[ "$GITHUB_REF" == refs/tags/v* ]]; then @@ -137,13 +161,13 @@ jobs: "$SIGNER_SHA256" > "$META_DIR/update.json" cat "$META_DIR/update.json" - - uses: actions/upload-artifact@v3 + - uses: https://gitea.com/actions/gitea-upload-artifact@v4 with: name: kadence-release-apk path: android/app/build/outputs/apk/release/app-release.apk if-no-files-found: error - - uses: actions/upload-artifact@v3 + - uses: https://gitea.com/actions/gitea-upload-artifact@v4 with: name: kadence-update-metadata path: android/app/build/outputs/update/update.json diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index b147a72..f00e5f9 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -26,7 +26,8 @@ val hasGlyphSdk = glyphAar.isFile * `~/.gradle/gradle.properties`; neither should have to care which the build reads. */ fun setting(name: String): String? = - (project.findProperty(name) as String?) ?: System.getenv(name) + (project.findProperty(name) as String?)?.takeIf { it.isNotBlank() } + ?: System.getenv(name)?.takeIf { it.isNotBlank() } /** * Successive builds have to install over each other, so the code has to climb. @@ -74,10 +75,18 @@ android { create("release") { val storePath = setting("KADENCE_KEYSTORE") if (storePath != null && file(storePath).isFile) { + val storePassword = setting("KADENCE_KEYSTORE_PASSWORD") + ?: throw GradleException( + "KADENCE_KEYSTORE is set but KADENCE_KEYSTORE_PASSWORD is " + + "missing or blank.", + ) + val keyPassword = setting("KADENCE_KEY_PASSWORD") ?: storePassword + val alias = setting("KADENCE_KEY_ALIAS") ?: "kadence" + storeFile = file(storePath) - storePassword = setting("KADENCE_KEYSTORE_PASSWORD") - keyAlias = setting("KADENCE_KEY_ALIAS") ?: "kadence" - keyPassword = setting("KADENCE_KEY_PASSWORD") + this.storePassword = storePassword + keyAlias = alias + this.keyPassword = keyPassword } else if (setting("KADENCE_REQUIRE_SIGNING") == "1") { // Without this the fallback below quietly produces a *debug-signed* // "release" APK: installable, apparently fine, and impossible to ship diff --git a/docs/production-builds.md b/docs/production-builds.md index b815a7f..bb64f5e 100644 --- a/docs/production-builds.md +++ b/docs/production-builds.md @@ -62,6 +62,20 @@ All secrets live under **Repository → Settings → Actions → Secrets**. | `KADENCE_KEY_ALIAS` | `android-release.yml` | The key alias, normally `kadence` | | `KADENCE_KEY_PASSWORD` | `android-release.yml` | The key password | +All four `KADENCE_KEY*` secrets are needed for a release. Before uploading the +base64 blob, check that the alias and passwords actually open the file: + +```bash +keytool -list \ + -keystore your-release.keystore \ + -storepass "$KADENCE_KEYSTORE_PASSWORD" \ + -alias "$KADENCE_KEY_ALIAS" +``` + +Then produce the secret with `base64 -w0 your-release.keystore`. The workflow +fails early and names any missing signing secret, instead of waiting for +`packageRelease` to report a keytool error. + The workflow uses Gitea's built-in `GITEA_TOKEN` to attach the APK to a tagged release. That token is supplied by Actions; it is not one of the secrets above. @@ -392,8 +406,10 @@ version code and rebuild. The run page in Gitea is the simplest route: **Actions → run → Artifacts**. For a tagged release, the attached asset is under **Releases**. -With a personal access token, the API works too. Listing the artifacts of the -latest successful `android` run: +With a personal access token, the API works too. Gitea's artifacts API only +lists artifacts uploaded through the v4 artifact protocol, so the workflow uses +`https://gitea.com/actions/gitea-upload-artifact@v4`. Listing the artifacts of +the latest successful `android` run: ```bash TOKEN=... # a Gitea token with repository read access @@ -434,8 +450,10 @@ copy. | `Cannot connect to the Docker daemon` in `docker.yml` | The job container does not have the host socket; check `docker_host` and `valid_volumes` in `deploy/act-runner.yaml` | | The Docker push returns 401 | `REGISTRY_TOKEN` is a job token or a PAT without `package:write`, or the username does not match the token | | Android build fails before Gradle starts | The SDK install failed; check `android/ci/install-sdk.sh` and the cache key. A cold cache can take several minutes | +| `Failed to read key from store ...: keystore password was incorrect` | The alias or a password secret is empty or wrong; the blank before `from` is the alias. All four `KADENCE_KEY*` secrets are required. Verify locally with `keytool -list` before re-encoding the keystore | | The APK is debug-signed despite the keystore secrets | At least one of `KADENCE_KEYSTORE_BASE64`, `KADENCE_KEYSTORE_PASSWORD`, `KADENCE_KEY_ALIAS`, `KADENCE_KEY_PASSWORD` is missing or wrong. With `KADENCE_REQUIRE_SIGNING` the job should have failed; fix the secret rather than shipping it | | `kadence-release-apk` is missing from the run | The build failed before the upload step, or the artifact has expired | +| The artifact API returns `total_count: 0` although the job uploaded the files | The workflow used `actions/upload-artifact@v3`; Gitea 1.24+ only exposes v4-protocol artifacts through `/actions/artifacts`. Use `https://gitea.com/actions/gitea-upload-artifact@v4` and run the build again | | The tag build cannot attach the APK to a release | The tag was pushed but no release exists yet, or the built-in `GITEA_TOKEN` lacks permission. Create the release, or check the repository's Actions token permissions | | A tag push did not start a workflow | The tag does not match `v*`, or it was created locally and never pushed: `git push origin v0.2.0` | | The deployment still runs old code after a pull | `docker compose up` rebuilt the local checkout. Use `--no-build` and pin `KADENCE_IMAGE` |