Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c44070e720 | ||
|
|
8ecf74a2af | ||
|
|
5fb9c1ff3b | ||
|
|
b5a3a3b427 |
@@ -1,27 +1,39 @@
|
||||
name: '📱 Test APK'
|
||||
|
||||
# An installable APK from any branch, on demand, without cutting a release.
|
||||
# Installable Android builds that are not releases.
|
||||
#
|
||||
# Why this exists separately from build-release.yml: that workflow is tag-driven,
|
||||
# builds Linux + Windows + Android and then *creates a release*, which is not
|
||||
# what you want from a feature branch. This builds one Android APK from whatever
|
||||
# ref you dispatch it on and hands it back as an artifact.
|
||||
# Two ways in:
|
||||
#
|
||||
# Deliberately `workflow_dispatch` only — no push trigger. The runner has a
|
||||
# single slot shared with two other projects, so a build on every feature-branch
|
||||
# commit would starve everything else. Dispatch it when you actually want to
|
||||
# install something.
|
||||
# push to master -> refreshes the rolling `latest` pre-release, so there is
|
||||
# always a current APK behind one stable URL that can be
|
||||
# handed to a tester once and never re-sent.
|
||||
# workflow_dispatch -> builds any branch on demand, optionally publishing it
|
||||
# as `test-<branch>`.
|
||||
#
|
||||
# Both variants install as com.dtourolle.jellytau.debug ("JellyTau Debug"),
|
||||
# side by side with a real install and with their own data directory. Neither
|
||||
# needs the release signing key.
|
||||
# Why this is separate from build-release.yml: that workflow is tag-driven,
|
||||
# builds Linux + Windows + Android and creates a real release. This produces one
|
||||
# APK and never touches the release channel.
|
||||
#
|
||||
# What comes out installs as com.dtourolle.jellytau.debug ("JellyTau Debug"),
|
||||
# side by side with a real install and with its own data directory. It is a
|
||||
# fully R8-minified release build -- minification is where Android builds have
|
||||
# actually broken here (R8 stripping JNI-loaded player and security classes),
|
||||
# and a plain debug build cannot catch that -- but it is signed with the debug
|
||||
# keystore rather than the store key. So a bad master commit can never replace
|
||||
# somebody's working install, and the production signing key stays in the
|
||||
# tag-driven workflow where it belongs.
|
||||
#
|
||||
# Getting the APK to somebody else: Gitea artifacts need an account with read
|
||||
# access to download, so `publish: true` also attaches the APK to a pre-release
|
||||
# whose assets are a plain public URL. That is the only way an outside tester
|
||||
# gets the file without being given an account.
|
||||
# access to download, so published builds are attached to a pre-release, whose
|
||||
# assets are a plain public URL. That is the only way an outside tester gets the
|
||||
# file without being given an account.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- '**/*.md'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
variant:
|
||||
@@ -30,9 +42,7 @@ on:
|
||||
default: 'side-by-side-release'
|
||||
type: choice
|
||||
options:
|
||||
# R8-minified, exactly what ships, in the debug slot. Use this unless
|
||||
# you need stack traces: R8 stripping JNI-loaded classes has broken
|
||||
# release APKs here before, and a plain debug build cannot catch it.
|
||||
# R8-minified, exactly what ships, in the debug slot.
|
||||
- side-by-side-release
|
||||
# Unminified. Faster, readable stack traces, but does not exercise
|
||||
# minification at all.
|
||||
@@ -47,13 +57,15 @@ on:
|
||||
- armv7
|
||||
- x86_64
|
||||
publish:
|
||||
description: 'Also publish as a pre-release, for testers with no Gitea account'
|
||||
description: 'Also publish as a pre-release (automatic on master)'
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
concurrency:
|
||||
# One test build at a time; a newer dispatch supersedes an in-flight one.
|
||||
# One APK build at a time, and a newer push supersedes an in-flight one — so a
|
||||
# burst of commits to master costs one build, not one per commit. This matters:
|
||||
# the runner has a single slot shared with two other projects.
|
||||
group: build-test-apk
|
||||
cancel-in-progress: true
|
||||
|
||||
@@ -63,8 +75,15 @@ env:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build test APK (${{ inputs.variant }}, ${{ inputs.abi }})
|
||||
name: Build test APK
|
||||
runs-on: linux/amd64
|
||||
defaults:
|
||||
run:
|
||||
# This runner executes `run:` blocks with `sh` (dash) unless told
|
||||
# otherwise, so bash-only syntax fails with a bare "Bad substitution"
|
||||
# naming a temp file and no line of your workflow. Say bash explicitly.
|
||||
# The short-SHA output below avoids depending on it regardless.
|
||||
shell: bash
|
||||
container:
|
||||
image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08.1
|
||||
env:
|
||||
@@ -79,6 +98,69 @@ jobs:
|
||||
# the tags have to be here. A shallow checkout yields 0.0.0.
|
||||
fetch-depth: 0
|
||||
|
||||
# One place decides what this run is, so the build, the collect step and
|
||||
# the publish step cannot disagree about it. A push carries no dispatch
|
||||
# inputs at all -- every `github.event.inputs.*` is empty on that event --
|
||||
# so each value needs an explicit default rather than being read raw.
|
||||
- name: Resolve build parameters
|
||||
id: cfg
|
||||
run: |
|
||||
set -e
|
||||
VARIANT="${{ github.event.inputs.variant }}"
|
||||
ABI="${{ github.event.inputs.abi }}"
|
||||
PUBLISH="${{ github.event.inputs.publish }}"
|
||||
BRANCH="${GITHUB_REF#refs/heads/}"
|
||||
|
||||
VARIANT="${VARIANT:-side-by-side-release}"
|
||||
ABI="${ABI:-aarch64}"
|
||||
|
||||
# A push to master always publishes -- that is the whole point of a
|
||||
# rolling `latest`. A dispatch publishes only if asked. Compared
|
||||
# against the string 'true' rather than used as a bare truthiness
|
||||
# test: dispatch inputs arrive as strings, and every non-empty string
|
||||
# is truthy, so `if: inputs.publish` would publish even when the box
|
||||
# was deliberately left unticked.
|
||||
if [ "$GITHUB_EVENT_NAME" = "push" ]; then
|
||||
PUBLISH=true
|
||||
elif [ "$PUBLISH" = "true" ]; then
|
||||
PUBLISH=true
|
||||
else
|
||||
PUBLISH=false
|
||||
fi
|
||||
|
||||
# Master is the rolling channel and keeps one stable tag, so the
|
||||
# download URL a tester was given keeps working. Anything else gets
|
||||
# its own branch-scoped tag.
|
||||
if [ "$BRANCH" = "master" ]; then
|
||||
TAG="latest"
|
||||
RELEASE_NAME="Latest build (master)"
|
||||
else
|
||||
TAG="test-$(echo "$BRANCH" | tr '/' '-')"
|
||||
RELEASE_NAME="Test build: $BRANCH"
|
||||
fi
|
||||
|
||||
# Stable asset name for the same reason the tag is stable.
|
||||
ASSET="jellytau-${TAG}.apk"
|
||||
|
||||
# Computed once, with `cut` rather than `${GITHUB_SHA::8}`. The
|
||||
# substring form is bash-only and this runner may hand a step to
|
||||
# `sh`; that cost a 51-minute build which produced a perfectly good
|
||||
# APK and then died formatting the summary table.
|
||||
SHORT_SHA=$(printf '%s' "$GITHUB_SHA" | cut -c1-8)
|
||||
|
||||
{
|
||||
echo "variant=$VARIANT"
|
||||
echo "abi=$ABI"
|
||||
echo "publish=$PUBLISH"
|
||||
echo "tag=$TAG"
|
||||
echo "release_name=$RELEASE_NAME"
|
||||
echo "asset=$ASSET"
|
||||
echo "branch=$BRANCH"
|
||||
echo "short_sha=$SHORT_SHA"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
echo "variant=$VARIANT abi=$ABI publish=$PUBLISH tag=$TAG asset=$ASSET"
|
||||
|
||||
- name: Cache Rust dependencies
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
@@ -129,30 +211,29 @@ jobs:
|
||||
# APK actually carries, which has silently regressed before.
|
||||
- name: Build APK
|
||||
run: |
|
||||
if [ "${{ inputs.variant }}" = "side-by-side-release" ]; then
|
||||
./scripts/build-android.sh release --debug --abi "${{ inputs.abi }}"
|
||||
if [ "${{ steps.cfg.outputs.variant }}" = "side-by-side-release" ]; then
|
||||
./scripts/build-android.sh release --debug --abi "${{ steps.cfg.outputs.abi }}"
|
||||
else
|
||||
./scripts/build-android.sh debug --abi "${{ inputs.abi }}"
|
||||
./scripts/build-android.sh debug --abi "${{ steps.cfg.outputs.abi }}"
|
||||
fi
|
||||
|
||||
- name: Collect APK
|
||||
id: collect
|
||||
run: |
|
||||
set -e
|
||||
mkdir -p dist/test-apk
|
||||
if [ "${{ inputs.variant }}" = "side-by-side-release" ]; then
|
||||
if [ "${{ steps.cfg.outputs.variant }}" = "side-by-side-release" ]; then
|
||||
PATTERN='*-release.apk'
|
||||
else
|
||||
PATTERN='*-debug.apk'
|
||||
fi
|
||||
APK=$(find src-tauri/gen/android/app/build/outputs/apk -name "$PATTERN" | head -1)
|
||||
if [ -z "$APK" ]; then
|
||||
echo "❌ No APK produced for variant ${{ inputs.variant }}"
|
||||
echo "❌ No APK produced for variant ${{ steps.cfg.outputs.variant }}"
|
||||
find src-tauri/gen/android/app/build/outputs/apk -name '*.apk' || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
REF_NAME=$(echo "${GITHUB_REF#refs/heads/}" | tr '/' '-')
|
||||
OUT="dist/test-apk/jellytau-${REF_NAME}-${GITHUB_SHA::8}-${{ inputs.variant }}.apk"
|
||||
OUT="dist/test-apk/${{ steps.cfg.outputs.asset }}"
|
||||
cp "$APK" "$OUT"
|
||||
|
||||
# Report what the thing actually is, not what it was meant to be.
|
||||
@@ -160,37 +241,32 @@ jobs:
|
||||
"$APKSIGNER" verify --print-certs "$OUT" || echo "⚠️ Could not verify signature"
|
||||
|
||||
{
|
||||
echo "### 📱 Test APK"
|
||||
echo "### 📱 ${{ steps.cfg.outputs.release_name }}"
|
||||
echo ""
|
||||
echo "| | |"
|
||||
echo "|---|---|"
|
||||
echo "| Branch | \`${GITHUB_REF#refs/heads/}\` |"
|
||||
echo "| Commit | \`${GITHUB_SHA::8}\` |"
|
||||
echo "| Variant | \`${{ inputs.variant }}\` |"
|
||||
echo "| ABI | \`${{ inputs.abi }}\` |"
|
||||
echo "| Branch | \`${{ steps.cfg.outputs.branch }}\` |"
|
||||
echo "| Commit | \`${{ steps.cfg.outputs.short_sha }}\` |"
|
||||
echo "| Variant | \`${{ steps.cfg.outputs.variant }}\` |"
|
||||
echo "| ABI | \`${{ steps.cfg.outputs.abi }}\` |"
|
||||
echo "| Size | $(du -h "$OUT" | cut -f1) |"
|
||||
echo "| SHA256 | \`$(sha256sum "$OUT" | cut -d' ' -f1)\` |"
|
||||
echo ""
|
||||
echo "Installs as \`com.dtourolle.jellytau.debug\` — side by side with a real"
|
||||
echo "install, with its own data directory. Download the artifact, then:"
|
||||
echo ""
|
||||
echo '```'
|
||||
echo "adb install -r $(basename "$OUT")"
|
||||
echo '```'
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
ls -lah dist/test-apk/
|
||||
|
||||
# Deliberately NOT tagged `v*`: that pattern triggers build-release.yml,
|
||||
# which would run the whole three-platform release matrix and publish a
|
||||
# real release off a feature branch. The tag here is derived from the
|
||||
# branch name and carries no version, so nothing else reacts to it.
|
||||
# real release. `latest` and `test-*` carry no version, so nothing else
|
||||
# reacts to them.
|
||||
#
|
||||
# This also cannot reach existing users. The desktop updater reads a
|
||||
# static latest.json from the `updater` branch, not the release list, so a
|
||||
# pre-release published here is invisible to anyone without the link.
|
||||
- name: Publish as a pre-release
|
||||
if: ${{ inputs.publish }}
|
||||
# This also cannot reach existing users by itself. The desktop updater
|
||||
# reads a static latest.json from the `updater` branch, not the release
|
||||
# list, so a pre-release published here is invisible to anyone who does
|
||||
# not have the link -- and the APK installs under a different
|
||||
# applicationId anyway.
|
||||
- name: Publish pre-release
|
||||
if: ${{ steps.cfg.outputs.publish == 'true' }}
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
AUTO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -200,26 +276,26 @@ jobs:
|
||||
API="${GITHUB_SERVER_URL}/api/v1"
|
||||
REPO="${GITHUB_REPOSITORY}"
|
||||
TOKEN="${GITEA_TOKEN:-$AUTO_TOKEN}"
|
||||
BRANCH="${GITHUB_REF#refs/heads/}"
|
||||
TAG="test-$(echo "$BRANCH" | tr '/' '-')"
|
||||
TAG="${{ steps.cfg.outputs.tag }}"
|
||||
ASSET="${{ steps.cfg.outputs.asset }}"
|
||||
|
||||
# printf, not a heredoc: inside a YAML block scalar every line is
|
||||
# indented, and a heredoc terminator has to sit at column 0.
|
||||
BODY=$(printf '%s\n' \
|
||||
"Test build of \`$BRANCH\` at \`${GITHUB_SHA::8}\` — **not a release**." \
|
||||
"Automatic build of \`${{ steps.cfg.outputs.branch }}\` at \`${{ steps.cfg.outputs.short_sha }}\` — **not a release**." \
|
||||
"" \
|
||||
"Installs as **JellyTau Debug** (\`com.dtourolle.jellytau.debug\`), alongside a" \
|
||||
"normal install and with its own separate data. Uninstalling it does not touch" \
|
||||
"the real app." \
|
||||
"normal install and with its own separate data. It cannot replace or upgrade a" \
|
||||
"real install, and uninstalling it does not touch one." \
|
||||
"" \
|
||||
"Variant: \`${{ inputs.variant }}\` · ABI: \`${{ inputs.abi }}\`" \
|
||||
"R8-minified like a real release, but signed with a debug key — so Android will" \
|
||||
"warn about an unknown source. That is expected." \
|
||||
"" \
|
||||
"Android will warn about installing from an unknown source; that is expected" \
|
||||
"for a build signed with a debug key rather than the store key.")
|
||||
"Variant: \`${{ steps.cfg.outputs.variant }}\` · ABI: \`${{ steps.cfg.outputs.abi }}\`" \
|
||||
"" \
|
||||
"This release is refreshed on every push; the download link stays the same.")
|
||||
|
||||
PAYLOAD=$(jq -n \
|
||||
--arg tag "$TAG" \
|
||||
--arg name "Test build: $BRANCH" \
|
||||
--arg name "${{ steps.cfg.outputs.release_name }}" \
|
||||
--arg body "$BODY" \
|
||||
--arg target "$GITHUB_SHA" \
|
||||
'{tag_name:$tag, target_commitish:$target, name:$name, body:$body, draft:false, prerelease:true}')
|
||||
@@ -230,11 +306,14 @@ jobs:
|
||||
if [ "$HTTP" = "201" ]; then
|
||||
RELEASE_ID=$(jq -r '.id' resp.json)
|
||||
elif [ "$HTTP" = "409" ]; then
|
||||
# Re-dispatching for the same branch replaces the previous APK rather
|
||||
# than accumulating one release per attempt.
|
||||
echo "ℹ️ Pre-release $TAG exists; reusing it"
|
||||
# The rolling case: reuse the release, refresh its body to name the
|
||||
# new commit, and clear the old asset so `latest` means latest.
|
||||
RELEASE_ID=$(curl -fsS "$API/repos/$REPO/releases/tags/$TAG" \
|
||||
-H "Authorization: token $TOKEN" | jq -r '.id')
|
||||
echo "ℹ️ Refreshing existing pre-release $TAG (id=$RELEASE_ID)"
|
||||
curl -fsS -X PATCH "$API/repos/$REPO/releases/$RELEASE_ID" \
|
||||
-H "Authorization: token $TOKEN" -H "Content-Type: application/json" \
|
||||
-d "$PAYLOAD" >/dev/null
|
||||
for id in $(curl -fsS "$API/repos/$REPO/releases/$RELEASE_ID/assets" \
|
||||
-H "Authorization: token $TOKEN" | jq -r '.[].id'); do
|
||||
curl -fsS -X DELETE "$API/repos/$REPO/releases/$RELEASE_ID/assets/$id" \
|
||||
@@ -244,21 +323,24 @@ jobs:
|
||||
echo "❌ Failed to create pre-release (HTTP $HTTP):"; cat resp.json; exit 1
|
||||
fi
|
||||
|
||||
for f in dist/test-apk/*.apk; do
|
||||
echo "⬆️ $(basename "$f")"
|
||||
# The tag moves with the branch, so an old tag object would otherwise
|
||||
# keep `latest` pointing at a stale commit.
|
||||
curl -fsS -X POST \
|
||||
"$API/repos/$REPO/releases/$RELEASE_ID/assets?name=$(basename "$f")" \
|
||||
-H "Authorization: token $TOKEN" -F "attachment=@$f" >/dev/null
|
||||
done
|
||||
"$API/repos/$REPO/releases/$RELEASE_ID/assets?name=$ASSET" \
|
||||
-H "Authorization: token $TOKEN" -F "attachment=@dist/test-apk/$ASSET" >/dev/null
|
||||
|
||||
URL="${GITHUB_SERVER_URL}/${REPO}/releases/download/${TAG}/${ASSET}"
|
||||
{
|
||||
echo ""
|
||||
echo "**Published:** ${GITHUB_SERVER_URL}/${REPO}/releases/tag/${TAG}"
|
||||
echo ""
|
||||
echo "Public link — no Gitea account needed. Delete the release when testing is done."
|
||||
echo "Direct download (stable link, no account needed):"
|
||||
echo ""
|
||||
echo " $URL"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "✅ Published $TAG -> $URL"
|
||||
|
||||
- name: Upload APK
|
||||
- name: Upload APK artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: jellytau-test-apk
|
||||
|
||||
@@ -7,7 +7,7 @@ echo "======================================"
|
||||
# Setup environment
|
||||
echo "Setting up environment..."
|
||||
source "$HOME/.cargo/env.fish" 2>/dev/null || source "$HOME/.cargo/env" || true
|
||||
export ANDROID_HOME="$HOME/Android/Sdk"
|
||||
export ANDROID_HOME="${ANDROID_HOME:-$HOME/Android/Sdk}"
|
||||
export NDK_HOME="$ANDROID_HOME/ndk/$(ls $ANDROID_HOME/ndk 2>/dev/null | head -1)"
|
||||
|
||||
# Check prerequisites
|
||||
|
||||
@@ -6,9 +6,26 @@ set -e
|
||||
# Source Rust environment
|
||||
source "$HOME/.cargo/env.fish" 2>/dev/null || source "$HOME/.cargo/env" 2>/dev/null || true
|
||||
|
||||
# Set Android environment variables
|
||||
export ANDROID_HOME="$HOME/Android/Sdk"
|
||||
export NDK_HOME="$ANDROID_HOME/ndk/$(ls "$ANDROID_HOME/ndk" | head -1)"
|
||||
# Set Android environment variables.
|
||||
#
|
||||
# Defaults, not overrides. A developer's SDK is at ~/Android/Sdk, but CI runs in
|
||||
# the builder image where it lives at /opt/android-sdk and the job sets
|
||||
# ANDROID_HOME accordingly — hardcoding the home-directory path here silently
|
||||
# discarded that and the build died with "Android SDK not found" a minute in.
|
||||
# `test-player-conformance.sh` already had this right; this script did not.
|
||||
export ANDROID_HOME="${ANDROID_HOME:-$HOME/Android/Sdk}"
|
||||
export ANDROID_SDK_ROOT="${ANDROID_SDK_ROOT:-$ANDROID_HOME}"
|
||||
|
||||
if [ ! -d "$ANDROID_HOME/ndk" ]; then
|
||||
echo "❌ No NDK directory at $ANDROID_HOME/ndk" >&2
|
||||
echo " Set ANDROID_HOME to your SDK location, or install the NDK." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Respect an NDK the caller has already picked (CI pins an exact revision via
|
||||
# ANDROID_NDK_HOME); otherwise take whatever is installed.
|
||||
export NDK_HOME="${NDK_HOME:-${ANDROID_NDK_HOME:-$ANDROID_HOME/ndk/$(ls "$ANDROID_HOME/ndk" | head -1)}}"
|
||||
export ANDROID_NDK_HOME="$NDK_HOME"
|
||||
|
||||
echo "🤖 Building Android APK..."
|
||||
echo "Android SDK: $ANDROID_HOME"
|
||||
|
||||
@@ -94,40 +94,56 @@ Follow the right log stream with `./scripts/logcat.sh [debug|release]`
|
||||
|
||||
### Getting a test APK out of CI
|
||||
|
||||
`.gitea/workflows/build-test-apk.yml` builds one from **any branch, on demand**
|
||||
— run it from Gitea's Actions tab (`workflow_dispatch`) against the ref you want.
|
||||
It is not a release: nothing is tagged, published, or signed with the real key.
|
||||
`.gitea/workflows/build-test-apk.yml` produces installable APKs that are **not
|
||||
releases**. Two ways in:
|
||||
|
||||
Two variants, both installing into the `com.dtourolle.jellytau.debug` slot:
|
||||
| Trigger | Result |
|
||||
|---------|--------|
|
||||
| **push to `master`** | Refreshes the rolling **`latest`** pre-release automatically |
|
||||
| **`workflow_dispatch`** | Builds any branch on demand; optionally publishes it as `test-<branch>` |
|
||||
|
||||
#### The rolling `latest` build
|
||||
|
||||
Every push to `master` (bar doc-only ones) rebuilds and replaces the APK on the
|
||||
`latest` pre-release. Both the tag and the asset name are stable, so the
|
||||
download URL never changes:
|
||||
|
||||
```
|
||||
https://gitea.tourolle.paris/dtourolle/jellytau/releases/download/latest/jellytau-latest.apk
|
||||
```
|
||||
|
||||
Send that link to a tester once and it keeps serving the current build. No
|
||||
account needed — release assets are public, unlike Actions artifacts.
|
||||
|
||||
#### What you get, and why it is safe
|
||||
|
||||
Both variants install into the `com.dtourolle.jellytau.debug` slot:
|
||||
|
||||
| Variant | What it is | When |
|
||||
|---------|-----------|------|
|
||||
| `side-by-side-release` (default) | R8-minified, exactly what ships, signed with the debug keystore | Almost always — a plain debug build cannot catch R8 stripping JNI-loaded classes, which has broken release APKs here before |
|
||||
| `side-by-side-release` (default, and what `latest` always is) | R8-minified, exactly what ships, signed with the **debug** keystore | Almost always — a plain debug build cannot catch R8 stripping JNI-loaded classes, which has broken release APKs here before |
|
||||
| `debug` | Unminified | When you need readable stack traces |
|
||||
|
||||
There is deliberately **no push trigger**: the runner has one slot shared with
|
||||
two other projects, so building on every feature-branch commit would starve
|
||||
them. The APK lands as the `jellytau-test-apk` artifact (7-day retention), named
|
||||
for the branch and short SHA, with its size and SHA256 in the run summary.
|
||||
Three properties make an automatic build on every master push safe:
|
||||
|
||||
#### Sending a build to an outside tester
|
||||
- **It cannot replace a real install.** The applicationId is suffixed `.debug`,
|
||||
so it sits beside the store build with its own data. A broken master commit
|
||||
can never take out somebody's working app.
|
||||
- **The production signing key is not involved.** That stays in the tag-driven
|
||||
`build-release.yml`. This workflow needs no secrets beyond the API token.
|
||||
- **The tag is `latest`/`test-*`, never `v*`.** Only `v*` triggers
|
||||
`build-release.yml`. And the desktop updater reads a static `latest.json` from
|
||||
the `updater` branch rather than the release list, so nothing here is offered
|
||||
to existing users.
|
||||
|
||||
Gitea **artifacts require an account** with read access to download, so an
|
||||
artifact is no use to someone outside the project. Tick **`publish`** on the
|
||||
dispatch and the APK is also attached to a **pre-release**, whose assets are a
|
||||
plain public URL on a public repo — no account, no MR, no merge to `master`.
|
||||
**Known gap:** the APK builds in parallel with `build-and-test.yml`, not after
|
||||
it, so `latest` can carry a commit whose tests later fail. Cross-workflow
|
||||
dependencies are not reliably available here, and duplicating the test job would
|
||||
double an already hour-long queue on a single-slot runner. Check the commit's
|
||||
CI status before handing the link to somebody.
|
||||
|
||||
Two things make that safe to do from a feature branch:
|
||||
|
||||
- The tag is `test-<branch>`, **not** `v*`. Only `v*` triggers
|
||||
`build-release.yml`, so nothing else reacts to it.
|
||||
- It cannot reach existing users. The desktop updater reads a static
|
||||
`latest.json` from the `updater` branch, not the release list, so a
|
||||
pre-release published this way is invisible to anyone without the link.
|
||||
|
||||
Re-dispatching for the same branch replaces the APK on the existing
|
||||
pre-release rather than piling up one release per attempt. Delete the release
|
||||
when testing is over.
|
||||
Runs are serialised and `cancel-in-progress` is on, so a burst of pushes to
|
||||
master collapses into one build rather than one per commit.
|
||||
|
||||
### Key Files
|
||||
|
||||
|
||||
Reference in New Issue
Block a user