Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 93 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,85 @@ name: Build APK & AAB

on:
workflow_dispatch:
pull_request:
types:
- opened
- reopened
- synchronize
- labeled
- unlabeled
- ready_for_review
branches:
- main
push:
tags:
- "v*.*.*"

permissions:
contents: write

concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

env:
CARGO_TERM_COLOR: always
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}

jobs:
validate-release-label:
name: Validate Release Label
runs-on: ubuntu-latest
outputs:
should_build: ${{ steps.classify.outputs.should_build }}

steps:
- name: Classify PR
id: classify
uses: actions/github-script@v7
with:
script: |
if (context.eventName !== "pull_request") {
core.setOutput("should_build", "true");
return;
}

const pr = context.payload.pull_request;
const appReleaseLabels = new Set([
"release:breaking",
"release:feature",
"release:bugfix",
"release:patch",
"release:improvement",
"release:optimisation",
]);
const releaseLabels = new Set([...appReleaseLabels, "release:docs"]);
const labels = pr.labels.map((label) => label.name);
const selected = labels.filter((label) => releaseLabels.has(label));
const sameRepo = pr.head.repo.full_name === `${context.repo.owner}/${context.repo.repo}`;
if (selected.length !== 1) {
core.setFailed(
`PRs to main must have exactly one release label. Found: ${
selected.join(", ") || "none"
}`
);
return;
}

const shouldBuild =
sameRepo &&
appReleaseLabels.has(selected[0]);

core.info(`same-repo PR: ${sameRepo}`);
core.info(`release label: ${selected[0]}`);
core.info(`build signed APK: ${shouldBuild}`);
core.setOutput("should_build", shouldBuild ? "true" : "false");

build:
needs: validate-release-label
if: github.event_name != 'pull_request' || needs.validate-release-label.outputs.should_build == 'true'
runs-on: ubuntu-latest

steps:
Expand All @@ -32,6 +96,7 @@ jobs:
run: echo "version=$(cargo metadata --format-version=1 --no-deps | jq -r '.packages[0].version')" >> "$GITHUB_OUTPUT"

- name: Extract release notes
if: github.ref_type == 'tag'
id: release_notes
uses: actions/github-script@v7
with:
Expand Down Expand Up @@ -67,7 +132,7 @@ jobs:
core.setOutput("path", notesPath);

- name: Fallback release notes to commit message
if: steps.release_notes.outputs.found != 'true'
if: github.ref_type == 'tag' && steps.release_notes.outputs.found != 'true'
run: |
git log -1 --pretty=%B > "${{ steps.release_notes.outputs.path }}"

Expand Down Expand Up @@ -123,10 +188,37 @@ jobs:
run: |
mv target/x/release/android/gradle/app/build/outputs/apk/debug/app-debug.apk target/x/localdesktop-${{ steps.extract_version.outputs.version }}-debug.apk
mv target/x/release/android/gradle/app/build/outputs/apk/release/app-release.apk target/x/localdesktop-${{ steps.extract_version.outputs.version }}.apk

- name: Compute PR APK artifact name
id: pr_apk_artifact
if: github.event_name == 'pull_request'
env:
HEAD_REF: ${{ github.head_ref }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
PR_NUMBER: ${{ github.event.pull_request.number }}
VERSION: ${{ steps.extract_version.outputs.version }}
run: |
SAFE_REF=$(printf '%s' "$HEAD_REF" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9._-]+/-/g; s/^-+//; s/-+$//; s/-+/-/g' | cut -c1-60)
if [ -z "$SAFE_REF" ]; then
SAFE_REF="pr-${PR_NUMBER}"
fi
SHORT_SHA="${HEAD_SHA:0:7}"
echo "name=localdesktop-${VERSION}-${SAFE_REF}-${SHORT_SHA}-release-apk" >> "$GITHUB_OUTPUT"

- name: Upload PR release APK
if: github.event_name == 'pull_request'
uses: actions/upload-artifact@v4
with:
name: ${{ steps.pr_apk_artifact.outputs.name }}
path: target/x/localdesktop-${{ steps.extract_version.outputs.version }}.apk
if-no-files-found: error

- name: Build AAB
if: github.event_name != 'pull_request'
run: x build --release --platform=android --arch=arm64 --format=aab

- name: Rename AAB
if: github.event_name != 'pull_request'
run: |
mv target/x/release/android/gradle/app/build/outputs/bundle/debug/app-debug.aab target/x/localdesktop-${{ steps.extract_version.outputs.version }}-debug.aab
mv target/x/release/android/gradle/app/build/outputs/bundle/release/app-release.aab target/x/localdesktop-${{ steps.extract_version.outputs.version }}.aab
Expand Down
6 changes: 6 additions & 0 deletions gh-pages/docs/developer/1-how-to-build.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ Then you will find the APK file in `target/x/release/android/localdesktop.apk`.

## FAQ

### Can I test a release-signed APK from a PR?

Yes. Same-repository PRs targeting `main` produce a signed release APK artifact when they have exactly one app-release label: `release:breaking`, `release:feature`, `release:bugfix`, `release:patch`, `release:improvement`, or `release:optimisation`. Download the workflow artifact from GitHub Actions and install it over the GitHub APK.

PRs labeled `release:docs` do not build an APK.

### Can I build on Termux?

Yes.
Expand Down
Loading