Skip to content

Add repo imagery: README logo and social preview card #62

Add repo imagery: README logo and social preview card

Add repo imagery: README logo and social preview card #62

Workflow file for this run

# Continuous integration: every pull request and every push to main.
#
# This workflow never cuts a release. That is Release (release.yml), triggered
# by a v* tag or run by hand, and it is deliberately a separate file: a build
# that runs constantly and a build that produces something permanent want
# different triggers, different concurrency and different blast radius. Both
# build through .github/actions/build-apk, so what CI tests is what a release
# ships.
#
# - JDK 17 rather than the Android starter template's 11, which AGP 8.x
# refuses to run on.
# - The debug APK uploaded as an artifact, so a build can be sideloaded onto a
# phone without a local Android SDK.
# - Pull request builds published to a rolling prerelease and linked from the
# pull request, because an artifact is a login-walled zip and a release asset
# is a direct .apk URL you can tap on the phone.
# - Reports uploaded on failure, so a red run can be read without re-running it.
name: Android CI
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
# Builds an APK from any branch on demand, as an artifact. Deliberately does
# not publish: an arbitrary branch has no release worth naming.
workflow_dispatch:
# Pushes in quick succession overlap, and a slower older run finishing last
# leaves the release asset and the pull request comment pointing at an older
# build than the branch actually has. Only the newest run for a ref is worth
# finishing.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: write # publish the APK to the rolling prerelease
pull-requests: write # link it from the pull request
steps:
- uses: actions/checkout@v4
- name: Build the APK
id: build
uses: ./.github/actions/build-apk
with:
pr_number: ${{ github.event.pull_request.number }}
pr_sha: ${{ github.event.pull_request.head.sha }}
keystore_base64: ${{ secrets.DEBUG_KEYSTORE_BASE64 }}
- name: Upload debug APK
uses: actions/upload-artifact@v4
with:
name: oh-tello-debug-apk
path: ${{ steps.build.outputs.apk }}
if-no-files-found: error
# Pull requests only. Pushes to main build and test but publish nothing, so
# a release is never quietly replaced by the next merge. Fork pull requests
# are skipped too: their tokens are read-only, so publishing would fail the
# build rather than just not happening.
- name: Publish the APK to the pull request's prerelease
id: publish
if: >-
github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name == github.repository
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
# The pull request head, not the ephemeral merge commit: the tag has to
# point at something that exists on the remote.
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
APK: ${{ steps.build.outputs.apk }}
run: |
set -euo pipefail
tag="pr-$PR_NUMBER"
gh release view "$tag" --repo "$REPO" >/dev/null 2>&1 || \
gh release create "$tag" --repo "$REPO" --target "$HEAD_SHA" --prerelease \
--title "PR #$PR_NUMBER debug build" \
--notes 'Test build for this pull request, replaced on every push. Debug-signed,
so it installs without a keystore and is not suitable for distribution.'
# The filename carries a timestamp and commit, so each build is a new
# asset rather than a replacement. Drop the previous ones: the prerelease
# should hold the current build and nothing else, so there is no way to
# install a stale APK by picking the wrong row.
stale=$(gh release view "$tag" --repo "$REPO" --json assets --jq '.assets[].name' \
| grep '\.apk$' | grep -v "^$APK$" || true)
while IFS= read -r asset; do
[ -n "$asset" ] || continue
# Tidying, not the job: the API 502s now and then, and the upload below
# publishes the new build regardless. Worth a warning, not a failed build.
gh release delete-asset "$tag" "$asset" --repo "$REPO" --yes \
|| echo "::warning::could not delete stale asset $asset"
done <<< "$stale"
gh release upload "$tag" "$APK" --repo "$REPO" --clobber
echo "url=https://github.com/$REPO/releases/download/$tag/$APK" >> "$GITHUB_OUTPUT"
# One comment, edited in place on later pushes, rather than a new one per build.
- name: Link the APK from the pull request
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
APK_URL: ${{ steps.publish.outputs.url }}
APK: ${{ steps.build.outputs.apk }}
BUILT_AT: ${{ steps.build.outputs.built_at }}
SHARED_KEY: ${{ steps.build.outputs.signing_shared }}
run: |
set -euo pipefail
marker='<!-- oh-tello-apk -->'
if [ "$SHARED_KEY" = "true" ]; then
signing_note='Builds share a debug signing key, so this replaces any previous Oh-Tello
build. If Android refuses to install it, the copy on the device predates
that shared key — uninstall it once and later builds will replace each
other cleanly.'
else
signing_note='**This build carries a throwaway signing key**, because the
DEBUG_KEYSTORE_BASE64 secret is not set. It will not install over an existing
Oh-Tello — uninstall that one first.'
fi
body=$(cat <<EOF
$marker
### 📲 [$APK]($APK_URL)
Built from \`${HEAD_SHA:0:7}\` at $BUILT_AT. Tap the link on the phone and
Android will offer to install it — no zip to unpack, no sign-in.
The same string is stamped into the app version, so once installed you can
still tell which build it is: it shows under the title on the first screen,
and in Settings > Apps > Oh-Tello. This comment and the release asset are
both replaced as new commits are pushed.
$signing_note
---
_Generated by [Claude Code](https://claude.ai/code)_
EOF
)
id=$(gh api "repos/$REPO/issues/$PR_NUMBER/comments?per_page=100" \
--jq "map(select(.body | contains(\"$marker\"))) | .[0].id // empty" | head -n1)
if [ -n "$id" ]; then
gh api -X PATCH "repos/$REPO/issues/comments/$id" -f body="$body" >/dev/null
else
gh api -X POST "repos/$REPO/issues/$PR_NUMBER/comments" -f body="$body" >/dev/null
fi
- name: Upload reports
if: failure()
uses: actions/upload-artifact@v4
with:
name: reports
path: |
app/build/reports/
app/build/test-results/
if-no-files-found: ignore