Adding support for Obtainium with fixed Signing Keys and apk Release publishing
This commit is contained in:
@@ -39,7 +39,7 @@ jobs:
|
||||
- name: Install the download tools
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends ca-certificates curl xz-utils
|
||||
apt-get install -y --no-install-recommends ca-certificates curl xz-utils jq
|
||||
|
||||
- name: Set up Java
|
||||
uses: https://github.com/actions/setup-java@v4
|
||||
@@ -74,21 +74,110 @@ jobs:
|
||||
- name: Run tests
|
||||
run: flutter test
|
||||
|
||||
# Android accepts an update only when it is signed with the same key as
|
||||
# the installed app, so every release has to carry one key that never
|
||||
# changes. The debug key cannot be used: the runner generates a fresh one
|
||||
# on every run, which would make each apk a different app to android and
|
||||
# break every update through Obtainium.
|
||||
#
|
||||
# `android/app/build.gradle.kts` picks the key up from
|
||||
# `android/key.properties`, which is written here from the repository
|
||||
# secrets (Settings -> Actions -> Secrets). See CLAUDE.md for the
|
||||
# `keytool` command that creates the keystore.
|
||||
- name: Write the release signing key
|
||||
env:
|
||||
KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
|
||||
KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
|
||||
KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
|
||||
KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# Failing here is on purpose: a silently debug signed apk would be
|
||||
# installable once and never updatable afterwards.
|
||||
if [ -z "${KEYSTORE_BASE64:-}" ]; then
|
||||
echo "::error::The secret ANDROID_KEYSTORE_BASE64 is empty. Add the keystore secrets (see CLAUDE.md) before publishing a release."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '%s' "$KEYSTORE_BASE64" | base64 -d > android/app/release-keystore.jks
|
||||
|
||||
# storeFile is relative to `android/app`. The file is gitignored.
|
||||
cat > android/key.properties <<PROPERTIES
|
||||
storeFile=release-keystore.jks
|
||||
storePassword=$KEYSTORE_PASSWORD
|
||||
keyAlias=$KEY_ALIAS
|
||||
keyPassword=$KEY_PASSWORD
|
||||
PROPERTIES
|
||||
|
||||
# One apk for all abis. `--split-per-abi` would produce three smaller
|
||||
# ones instead, at the cost of having to pick the right one manually.
|
||||
#
|
||||
# The release build is signed with a debug key, because
|
||||
# `android/app/build.gradle.kts` has no release signing config yet. To
|
||||
# ship the apk, add a keystore as a secret, write it into
|
||||
# `android/key.properties` here and reference it from the gradle file.
|
||||
# The version in the pubspec (`1.0.0+1`) never moves, so it is replaced
|
||||
# here: the version name becomes the tag of the release and the version
|
||||
# code the run number, which only ever grows. Android refuses to install
|
||||
# an apk whose version code is lower than the installed one, so the code
|
||||
# must never go backwards.
|
||||
- name: Build the apk
|
||||
run: flutter build apk --release
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
version="${{ github.ref_name }}"
|
||||
version="${version#v}"
|
||||
|
||||
# `--build-name` only accepts a dotted number, so an unusual tag
|
||||
# ("nightly") keeps the version of the pubspec instead of failing
|
||||
# the build.
|
||||
case "$version" in
|
||||
*[!0-9.]* | '' ) buildName='' ;;
|
||||
* ) buildName="$version" ;;
|
||||
esac
|
||||
|
||||
if [ -n "$buildName" ]; then
|
||||
flutter build apk --release \
|
||||
--build-name "$buildName" \
|
||||
--build-number "${{ github.run_number }}"
|
||||
else
|
||||
echo "::warning::'${{ github.ref_name }}' is not a dotted version, keeping the version of the pubspec."
|
||||
flutter build apk --release --build-number "${{ github.run_number }}"
|
||||
fi
|
||||
|
||||
# Gradle always writes the same file name, so the release the apk belongs
|
||||
# to would not be visible after downloading it.
|
||||
- name: Name the apk after the release
|
||||
run: mv build/app/outputs/flutter-apk/app-release.apk ac_web-${{ github.ref_name }}.apk
|
||||
|
||||
# Obtainium reads `<host>/api/v1/repos/<owner>/<repo>/releases` and
|
||||
# downloads the *assets* of a release, so the apk has to hang off the
|
||||
# release itself. A workflow artifact is invisible to it: it lives under
|
||||
# the run, is zipped and needs a login.
|
||||
- name: Attach the apk to the release
|
||||
env:
|
||||
# The token gitea creates for the run. If the instance hands out a
|
||||
# read only token, put a personal access token with repository write
|
||||
# access in a secret and use that instead.
|
||||
TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
api="${{ github.server_url }}/api/v1/repos/${{ github.repository }}"
|
||||
asset="ac_web-${{ github.ref_name }}.apk"
|
||||
|
||||
# The release event carries the id; the lookup by tag is only there
|
||||
# for a manual re-run of the job.
|
||||
releaseId="${{ github.event.release.id }}"
|
||||
if [ -z "$releaseId" ] || [ "$releaseId" = "null" ]; then
|
||||
releaseId=$(curl -fsSL -H "Authorization: token $TOKEN" \
|
||||
"$api/releases/tags/${{ github.ref_name }}" | jq -r '.id')
|
||||
fi
|
||||
|
||||
curl -fsSL -X POST \
|
||||
-H "Authorization: token $TOKEN" \
|
||||
-F "attachment=@$asset" \
|
||||
"$api/releases/$releaseId/assets?name=$asset" > /dev/null
|
||||
|
||||
echo "Attached $asset to release $releaseId."
|
||||
|
||||
# @v3 on purpose: `upload-artifact@v4` bundles `@actions/artifact` v2,
|
||||
# which refuses to run as soon as `GITHUB_SERVER_URL` is not github.com
|
||||
# ("GHESNotSupportedError") -- and on this instance it is the gitea host.
|
||||
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
apt-get install -y --no-install-recommends \
|
||||
clang cmake ninja-build pkg-config \
|
||||
libgtk-3-dev liblzma-dev libstdc++-12-dev \
|
||||
curl unzip xz-utils
|
||||
curl unzip xz-utils jq
|
||||
|
||||
# `subosito/flutter-action` cannot be used: it caches the sdk through
|
||||
# `actions/cache@v5`, and the act-runner of this gitea instance only
|
||||
@@ -72,6 +72,36 @@ jobs:
|
||||
- name: Pack the bundle
|
||||
run: tar -czf ac_web-linux-x64-${{ github.ref_name }}.tar.gz -C build/linux/x64/release bundle
|
||||
|
||||
# The tarball hangs off the release itself, next to the apk of the
|
||||
# android job, so it can be downloaded without a login and stays after
|
||||
# the artifacts of the run have been pruned.
|
||||
- name: Attach the bundle to the release
|
||||
env:
|
||||
# The token gitea creates for the run. If the instance hands out a
|
||||
# read only token, put a personal access token with repository write
|
||||
# access in a secret and use that instead.
|
||||
TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
api="${{ github.server_url }}/api/v1/repos/${{ github.repository }}"
|
||||
asset="ac_web-linux-x64-${{ github.ref_name }}.tar.gz"
|
||||
|
||||
# The release event carries the id; the lookup by tag is only there
|
||||
# for a manual re-run of the job.
|
||||
releaseId="${{ github.event.release.id }}"
|
||||
if [ -z "$releaseId" ] || [ "$releaseId" = "null" ]; then
|
||||
releaseId=$(curl -fsSL -H "Authorization: token $TOKEN" \
|
||||
"$api/releases/tags/${{ github.ref_name }}" | jq -r '.id')
|
||||
fi
|
||||
|
||||
curl -fsSL -X POST \
|
||||
-H "Authorization: token $TOKEN" \
|
||||
-F "attachment=@$asset" \
|
||||
"$api/releases/$releaseId/assets?name=$asset" > /dev/null
|
||||
|
||||
echo "Attached $asset to release $releaseId."
|
||||
|
||||
# @v3 on purpose: `upload-artifact@v4` bundles `@actions/artifact` v2,
|
||||
# which refuses to run as soon as `GITHUB_SERVER_URL` is not github.com
|
||||
# ("GHESNotSupportedError") -- and on this instance it is the gitea host.
|
||||
|
||||
Reference in New Issue
Block a user