mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 19:28:25 +00:00
test-and-build-android died again with "the runner has received a shutdown signal" — the OOM killer taking the runner agent, about 20 minutes in, with every task up to that point green. No test failed and nothing failed to compile. The previous fix is not wrong, it was validated on the easy case.ea459bc5changed only this file, so the run restored main's Gradle cache and rebuilt almost nothing; it passed in 54 minutes and I read that as confirmation. The first PR run since that touches `commons` invalidated the cache — this job's own comment above notes a quartz/commons change does exactly that and costs 2-3x — rebuilt from cold, and died the same way. Cold is the case the 4g cap had never actually faced. So the lever is concurrency rather than ceilings. Those heap numbers are per-JVM limits; what tips a 16GB runner over is how many heavy JVMs are resident at once, and Gradle defaults max-workers to the core count with org.gradle.parallel on. The log shows the peak: R8 and both lintAnalyze tasks behind it, a cold compileFdroidDebugKotlin, and a forked test JVM starting, alongside the 6g Gradle and 4g Kotlin daemons. --max-workers=3, not 2. This job is mostly a chain of single-task module compiles, so it loses little real parallelism, and halving it risks the timeout on a cold run instead — the cap goes to 120 for that reason, which costs nothing on runs that finish early. Dropping the Kotlin daemon further was the obvious alternative and is the wrong one:cdb4f1f7already lost that trade, starving R8 and lintAnalyze into "java.lang.OutOfMemoryError: Java heap space". If this still dies early, the job is simply too big for one 16GB runner and the answer is splitting lint out into its own job — not another number. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AXvKXakvup4inNFfAhhr4L
492 lines
21 KiB
YAML
492 lines
21 KiB
YAML
name: Test/Build
|
||
|
||
on:
|
||
pull_request:
|
||
branches: [main]
|
||
push:
|
||
branches: [main]
|
||
|
||
permissions:
|
||
contents: read
|
||
|
||
concurrency:
|
||
group: ${{ github.workflow }}-${{ github.ref }}
|
||
cancel-in-progress: true
|
||
|
||
jobs:
|
||
lint:
|
||
runs-on: ubuntu-latest
|
||
timeout-minutes: 15
|
||
steps:
|
||
- name: Checkout code
|
||
uses: actions/checkout@v7
|
||
|
||
- name: Orphaned translations (no locale string may outlive its default key)
|
||
run: .claude/hooks/orphan_strings_check.py
|
||
|
||
- name: Compose catalog escaping (Android escapes render literally there)
|
||
run: .claude/hooks/compose_escaping_check.py
|
||
|
||
- name: Set up JDK 21
|
||
uses: actions/setup-java@v6.0.0
|
||
with:
|
||
distribution: 'temurin'
|
||
java-version: 21
|
||
|
||
# Remote Gradle build cache: writes on push to main, reads on PRs and
|
||
# other branches. Caches both `~/.gradle/caches/` and individual task
|
||
# outputs, so dependency-only changes hit the cache and skip recompiling
|
||
# downstream modules / re-merging native libs (~600MB of work on
|
||
# :amethyst alone). Replaces the narrower `cache: gradle` previously on
|
||
# actions/setup-java, which only cached `modules-2`.
|
||
- name: Set up Gradle
|
||
uses: gradle/actions/setup-gradle@v6
|
||
with:
|
||
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
|
||
|
||
- name: Linter (gradle)
|
||
# The three metadata compiles resolve commonMain against only the
|
||
# deps every target shares, which is the Apple classpath — a
|
||
# dependency that reaches JVM transitively (okio via OkHttp) but is
|
||
# missing for iOS fails here, on Linux, instead of in test-quartz-ios.
|
||
run: |
|
||
./gradlew spotlessCheck \
|
||
:quartz:verifyKmpPurity :commons:verifyKmpPurity :commonsUI:verifyKmpPurity \
|
||
:quartz:compileCommonMainKotlinMetadata \
|
||
:commons:compileCommonMainKotlinMetadata \
|
||
:commonsUI:compileCommonMainKotlinMetadata
|
||
|
||
build-desktop:
|
||
needs: lint
|
||
strategy:
|
||
fail-fast: false
|
||
matrix:
|
||
include:
|
||
- os: ubuntu-latest
|
||
desktop-task: packageDeb
|
||
desktop-artifact-name: Desktop Linux DEB
|
||
desktop-artifact-path: desktopApp/build/compose/binaries/main/deb/*.deb
|
||
- os: macos-latest
|
||
desktop-task: packageDmg
|
||
desktop-artifact-name: Desktop macOS DMG
|
||
desktop-artifact-path: desktopApp/build/compose/binaries/main/dmg/*.dmg
|
||
- os: windows-latest
|
||
desktop-task: packageMsi
|
||
desktop-artifact-name: Desktop Windows MSI
|
||
desktop-artifact-path: desktopApp/build/compose/binaries/main/msi/*.msi
|
||
runs-on: ${{ matrix.os }}
|
||
timeout-minutes: 60
|
||
defaults:
|
||
run:
|
||
shell: bash
|
||
steps:
|
||
- name: Checkout code
|
||
uses: actions/checkout@v7
|
||
|
||
- name: Set up JDK 21
|
||
uses: actions/setup-java@v6.0.0
|
||
with:
|
||
distribution: 'temurin'
|
||
java-version: 21
|
||
|
||
- name: Set up Gradle
|
||
uses: gradle/actions/setup-gradle@v6
|
||
with:
|
||
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
|
||
|
||
# Compose UI smoke test (DesktopLaunchSmokeTest) uses Skiko which needs
|
||
# a display server on Linux. xvfb provides a virtual framebuffer.
|
||
- name: Install xvfb (Linux)
|
||
if: runner.os == 'Linux'
|
||
run: sudo apt-get update && sudo apt-get install -y xvfb
|
||
|
||
- name: Test + Build Desktop (gradle)
|
||
run: |
|
||
CMD="./gradlew :quartz:jvmTest :commons:jvmTest :commonsUI:jvmTest :quic:jvmTest :nestsClient:jvmTest :marmotQuic:jvmTest :cli:test :desktopApp:test :desktopApp:${{ matrix.desktop-task }}"
|
||
if [ "${{ runner.os }}" = "Linux" ]; then
|
||
xvfb-run --auto-servernum $CMD
|
||
else
|
||
$CMD
|
||
fi
|
||
|
||
# This job runs the JVM test suites (:quartz, :commons, :commonsUI, :quic,
|
||
# :nestsClient, :marmotQuic, :cli, :desktopApp) but, unlike test-geode /
|
||
# test-quartz-ios /
|
||
# test-and-build-android, published nothing when one of them failed. The
|
||
# console line names the failing test and the exception class and stops
|
||
# there, so the message is lost with the runner. That is how the
|
||
# NostrClientNegentropySyncTest failure in run 10540 became
|
||
# undiagnosable: NegentropySyncException carries a `detail` naming which
|
||
# branch fired (connect timeout / idle silence / NEG-ERR / disconnect),
|
||
# and nobody could read it. Same action and pin as the Android job below.
|
||
- name: Desktop Test Report
|
||
uses: mikepenz/action-junit-report@a9170d5795813c01ab4901ffb045b52bab4ab09d # v6.5.0
|
||
if: always()
|
||
with:
|
||
report_paths: '**/build/test-results/**/TEST-*.xml'
|
||
annotate_only: true
|
||
detailed_summary: true
|
||
fail_on_failure: true
|
||
|
||
# The HTML reports carry the full stack traces and stdout/stderr the
|
||
# annotations truncate. Named per-OS because the three matrix legs upload
|
||
# into the same run and artifact names must be unique.
|
||
- name: Upload Desktop Test Reports
|
||
uses: actions/upload-artifact@v7
|
||
if: failure()
|
||
with:
|
||
name: Desktop Test Reports (${{ matrix.os }})
|
||
path: |
|
||
quartz/build/reports/tests
|
||
commons/build/reports/tests
|
||
commonsUI/build/reports/tests
|
||
nestsClient/build/reports/tests
|
||
cli/build/reports/tests
|
||
desktopApp/build/reports/tests
|
||
|
||
# jpackage pins libicu to the build host's version (libicu74 on
|
||
# ubuntu-24.04). Rewrite the .deb so testers on other Debian/Ubuntu
|
||
# releases can install the uploaded artifact.
|
||
- name: Relax libicu dependency in .deb
|
||
if: matrix.desktop-task == 'packageDeb'
|
||
run: |
|
||
set -euo pipefail
|
||
chmod +x scripts/relax-deb-libicu.sh
|
||
scripts/relax-deb-libicu.sh desktopApp/build/compose/binaries/main/deb/*.deb
|
||
|
||
- name: Upload Desktop Distribution
|
||
uses: actions/upload-artifact@v7
|
||
with:
|
||
name: ${{ matrix.desktop-artifact-name }}
|
||
path: ${{ matrix.desktop-artifact-path }}
|
||
|
||
# geode (the standalone Nostr relay) is JVM-only, so it runs in its own job
|
||
# rather than the build-desktop matrix — one runner suffices (no reason to test
|
||
# a platform-independent module 3× across the desktop OS matrix). Isolating it
|
||
# also keeps its default suite's CPU-heavy throughput benchmarks (a 1M-event
|
||
# mirror sync, WireReqFloor, NegentropyServerReconcile) from contending with the
|
||
# timing-sensitive quartz relay-client tests under org.gradle.parallel — that
|
||
# contention flakes tests like NostrClientReqBypassingRelayLimitsTest.
|
||
test-geode:
|
||
needs: lint
|
||
runs-on: ubuntu-latest
|
||
timeout-minutes: 30
|
||
steps:
|
||
- name: Checkout code
|
||
uses: actions/checkout@v7
|
||
|
||
- name: Set up JDK 21
|
||
uses: actions/setup-java@v6.0.0
|
||
with:
|
||
distribution: 'temurin'
|
||
java-version: 21
|
||
|
||
- name: Set up Gradle
|
||
uses: gradle/actions/setup-gradle@v6
|
||
with:
|
||
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
|
||
|
||
# -DsyncN shrinks MirrorSyncThroughputTest's corpus from its 1,000,000-event
|
||
# default. At 1M the sink cannot keep up with the in-process source, and the
|
||
# MirrorWorker's deliberately unbounded intake channel buffers the backlog until
|
||
# the runner's heap is gone: throughput collapses (13,800 -> 86 ev/s) and an
|
||
# OutOfMemoryError lands on a coroutine thread, where the UncaughtExceptionHandler
|
||
# swallows it. JUnit never sees a failure, so the JVM wedges and the job burns to
|
||
# the timeout with no signal rather than failing. 100k keeps a real ev/s number
|
||
# while bounding the worst-case backlog to a tenth of what died.
|
||
#
|
||
# Only CI is shrunk: -DsyncN is unset everywhere else, so a local or manual run
|
||
# still measures the full 1M — the number written up in
|
||
# relayBench/plans/2026-07-04-sync-throughput-1m.md.
|
||
- name: Test geode (gradle)
|
||
run: ./gradlew :geode:test -DsyncN=100000
|
||
|
||
- name: Upload geode Test Reports
|
||
uses: actions/upload-artifact@v7
|
||
if: failure()
|
||
with:
|
||
name: geode Test Reports
|
||
path: geode/build/reports
|
||
|
||
# Until this job existed nothing ran the linuxX64 target at all — it was compiled by
|
||
# no CI leg. That is how a copy-on-write LargeCache with O(n) writes and a non-atomic
|
||
# read-copy-write (concurrent writers silently dropped entries) sat in the tree
|
||
# unnoticed, and how TestResourceLoader stayed a TODO() that failed every vector-driven
|
||
# suite on the target.
|
||
#
|
||
# Runs the whole :quartz suite on a Linux Native frontend, which also catches a
|
||
# commonMain or commonTest source reaching for a JVM-only API on a target that, unlike
|
||
# Apple, has no Foundation to fall back on.
|
||
test-quartz-linux-native:
|
||
needs: lint
|
||
runs-on: ubuntu-latest
|
||
timeout-minutes: 45
|
||
steps:
|
||
- name: Checkout code
|
||
uses: actions/checkout@v7
|
||
|
||
- name: Set up JDK 21
|
||
uses: actions/setup-java@v6.0.0
|
||
with:
|
||
distribution: 'temurin'
|
||
java-version: 21
|
||
|
||
- name: Set up Gradle
|
||
uses: gradle/actions/setup-gradle@v6
|
||
with:
|
||
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
|
||
|
||
# The Kotlin/Native toolchain (compiler distribution + LLVM + the sysroot) lands
|
||
# in ~/.konan, which setup-gradle does not cache. Without this the job re-downloads
|
||
# well over a gigabyte on every run. Keyed on the version catalog so a Kotlin bump
|
||
# re-populates it.
|
||
- name: Cache Kotlin/Native toolchain
|
||
uses: actions/cache@v6
|
||
with:
|
||
path: ~/.konan
|
||
key: konan-${{ runner.os }}-${{ hashFiles('gradle/libs.versions.toml') }}
|
||
restore-keys: konan-${{ runner.os }}-
|
||
|
||
- name: Test Quartz on Linux Native
|
||
run: ./gradlew :quartz:linuxX64Test
|
||
|
||
- name: Linux Native Test Report
|
||
uses: mikepenz/action-junit-report@a9170d5795813c01ab4901ffb045b52bab4ab09d # v6.5.0
|
||
if: always()
|
||
with:
|
||
report_paths: 'quartz/build/test-results/linuxX64Test/TEST-*.xml'
|
||
annotate_only: true
|
||
detailed_summary: true
|
||
fail_on_failure: true
|
||
|
||
- name: Upload Linux Native Test Reports
|
||
uses: actions/upload-artifact@v7
|
||
if: failure()
|
||
with:
|
||
name: Quartz Linux Native Test Reports
|
||
path: quartz/build/reports
|
||
|
||
test-quartz-ios:
|
||
# Phase 1 of the iOS support plan
|
||
# (amethyst/plans/2026-05-24-ios-support.md): keep :quartz green on iOS
|
||
# so JVM-only imports can't sneak into commonMain unnoticed. The
|
||
# `verifyKmpPurity` task in the lint job is the fast pre-check (Linux,
|
||
# ~1s); this job is the real one — compiles for the device variant
|
||
# and actually runs the simulator test suite.
|
||
needs: lint
|
||
runs-on: macos-latest
|
||
timeout-minutes: 45
|
||
steps:
|
||
- name: Checkout code
|
||
uses: actions/checkout@v7
|
||
|
||
- name: Set up JDK 21
|
||
uses: actions/setup-java@v6.0.0
|
||
with:
|
||
distribution: 'temurin'
|
||
java-version: 21
|
||
|
||
- name: Set up Gradle
|
||
uses: gradle/actions/setup-gradle@v6
|
||
with:
|
||
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
|
||
|
||
# Two tasks, two purposes:
|
||
# - iosSimulatorArm64Test runs the existing iosTest suite on the
|
||
# simulator (NIP-04 / NIP-17 / NIP-19 / NIP-49 / AES-GCM /
|
||
# Chatroom keys), exercising secp256k1 and CryptoKit-backed
|
||
# primitives on a real Apple toolchain.
|
||
# - compileTestKotlinIosArm64 catches any device-only compile drift
|
||
# (iosArm64 = aarch64-apple-ios) without needing a physical
|
||
# device to run on. Compile-only is enough — running on-device
|
||
# would require xcodebuild + a provisioning profile.
|
||
- name: Test Quartz on iOS
|
||
run: |
|
||
./gradlew \
|
||
:quartz:iosSimulatorArm64Test \
|
||
:quartz:compileTestKotlinIosArm64
|
||
|
||
# :commons gained iosArm64 + iosSimulatorArm64 targets in Phase 2 of the
|
||
# iOS plan. Actual UI / lifecycle wiring will land with the iosApp module
|
||
# in Phase 3, but the shared commonMain + commonTest sources are already
|
||
# built here against an Apple Native frontend. Same two-task shape as
|
||
# quartz above:
|
||
# - iosSimulatorArm64Test compiles AND runs the shared commonTest suite
|
||
# on the simulator. commonTest is built for every target, so a test
|
||
# reaching for a JVM-only API (JUnit, javaClass, @JvmStatic, or a
|
||
# jvmAndroid-only symbol) breaks the Apple build even though
|
||
# :commons:jvmTest stays green — this is the job that catches it.
|
||
# - compileTestKotlinIosArm64 catches device-only compile drift
|
||
# (iosArm64 = aarch64-apple-ios) without needing a physical device.
|
||
# :commonsUI (the Compose half split out of :commons) gets the same
|
||
# treatment so the shared composables keep compiling on Apple targets.
|
||
- name: Test Commons on iOS
|
||
run: |
|
||
./gradlew \
|
||
:commons:iosSimulatorArm64Test \
|
||
:commons:compileTestKotlinIosArm64 \
|
||
:commonsUI:iosSimulatorArm64Test \
|
||
:commonsUI:compileTestKotlinIosArm64
|
||
|
||
- name: Upload iOS Test Reports
|
||
uses: actions/upload-artifact@v7
|
||
if: failure()
|
||
with:
|
||
name: Quartz iOS Test Reports
|
||
path: quartz/build/reports
|
||
|
||
test-and-build-android:
|
||
needs: lint
|
||
runs-on: ubuntu-latest
|
||
# 90, not 60. On main this job's Gradle step takes ~46 minutes, which used
|
||
# 76% of a 60-minute budget — fine for an incremental run, but PR runs set
|
||
# `cache-read-only` (below), so they restore main's Gradle cache and never
|
||
# save. A PR that touches `quartz` or `commons` invalidates most of what
|
||
# that cache holds for everything downstream, and the job then rebuilds it
|
||
# from cold: measured 2-3x the main-branch time across every job in the
|
||
# workflow, which puts this one past the cap and gets it killed mid-step
|
||
# with no test report. Raising the cap costs nothing on runs that finish
|
||
# early — `timeout-minutes` bounds a job, it does not reserve the time.
|
||
timeout-minutes: 120
|
||
steps:
|
||
- name: Checkout code
|
||
uses: actions/checkout@v7
|
||
|
||
- name: Set up JDK 21
|
||
uses: actions/setup-java@v6.0.0
|
||
with:
|
||
distribution: 'temurin'
|
||
java-version: 21
|
||
|
||
- name: Set up Gradle
|
||
uses: gradle/actions/setup-gradle@v6
|
||
with:
|
||
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
|
||
|
||
# Lint + focused unit tests + benchmark assembly in one Gradle invocation.
|
||
# Previously: one invocation for lint, one for `test` (which compiled all
|
||
# six amethyst variants × all flavors), one for `assembleBenchmark`
|
||
# (re-walking the same task graph). Combining them keeps the daemon hot
|
||
# across phases and lets task-level dedup (e.g. compileKotlin) only
|
||
# happen once.
|
||
#
|
||
# `-PdisableAbiSplits=true` produces a single non-split APK per
|
||
# (flavor, buildType) instead of 5 (4 ABIs + universal). The CI only
|
||
# uploads the universal-equivalent benchmark APK; per-ABI splits were
|
||
# being built and discarded, costing ~600MB of stripped_native_libs
|
||
# intermediates and several minutes per run.
|
||
#
|
||
# Test scope: only Debug unit tests for amethyst. The release/benchmark
|
||
# variants are compile-equivalent for unit-test purposes; running all six
|
||
# adds ~5× the kotlinc work without catching new defects on PRs. Push to
|
||
# main still gets the full test matrix via the production-build path.
|
||
# Memory, CI-only. gradle.properties asks for -Xmx6g (Gradle) plus -Xmx8g
|
||
# and 2g metaspace (Kotlin daemon) — about 16GB of ceiling on a 16GB
|
||
# ubuntu-latest runner, before the launcher JVM, Lint's fork and the KSP
|
||
# workers. This job is the only one heavy enough to reach it, and it died
|
||
# five times mid-compile with "the runner has received a shutdown signal",
|
||
# which is the OOM killer taking the runner agent.
|
||
#
|
||
# Only the Kotlin daemon is cut. An earlier attempt capped BOTH to 4g and
|
||
# traded one OOM for another: the runner survived and the job ran to
|
||
# completion, but R8 and lintAnalyze then failed with
|
||
# "java.lang.OutOfMemoryError: Java heap space" — they draw on the Gradle
|
||
# daemon's heap, and 4g is not enough for them on this app. 6g always was,
|
||
# so it stays; 8g + 2g for kotlinc is the part that did not fit.
|
||
#
|
||
# Overridden here rather than in gradle.properties so local builds on
|
||
# bigger machines keep the headroom.
|
||
#
|
||
# `--max-workers` is the second half, and it is about concurrency rather
|
||
# than ceilings. The heap numbers above are per-JVM limits; what actually
|
||
# tips a 16GB runner over is how many heavy JVMs are live at once. Gradle
|
||
# defaults max-workers to the core count (4 on ubuntu-latest) and
|
||
# org.gradle.parallel is on, so a cold run can have several kotlinc
|
||
# workers, a lint fork and a forked test JVM resident alongside the two
|
||
# daemons.
|
||
#
|
||
# Cold is the case that matters. The 4g cap was first validated on a run
|
||
# that only changed this file, so it restored main's Gradle cache and
|
||
# built almost nothing; the next PR run that touched `commons`
|
||
# invalidated that cache, rebuilt from cold, and died the same way at
|
||
# ~20 minutes. Fewer workers is what makes the cold path fit — dropping
|
||
# heap further would start starving R8 again, which is the trade the
|
||
# previous attempt already lost.
|
||
#
|
||
# 3 rather than 2: this job is mostly a chain of single-task module
|
||
# compiles, so the parallelism it loses is small, and halving it risks
|
||
# the timeout on a cold run. The cap goes to 120 for the same reason.
|
||
- name: Test + Build Android (gradle)
|
||
run: |
|
||
./gradlew \
|
||
-Dkotlin.daemon.jvmargs="-Xmx4g -XX:MaxMetaspaceSize=1g" \
|
||
--max-workers=3 \
|
||
:amethyst:lintFdroidBenchmark \
|
||
:amethyst:lintPlayBenchmark \
|
||
:quartz:jvmTest \
|
||
:commons:jvmTest \
|
||
:commonsUI:jvmTest \
|
||
:commonsUI:testAndroidHostTest \
|
||
:nestsClient:jvmTest \
|
||
:amethyst:testFdroidDebugUnitTest \
|
||
:amethyst:testPlayDebugUnitTest \
|
||
:amethyst:assembleBenchmark \
|
||
-PdisableAbiSplits=true
|
||
|
||
- name: Upload Android Lint Reports
|
||
uses: actions/upload-artifact@v7
|
||
if: always()
|
||
with:
|
||
name: Android Lint Reports
|
||
path: amethyst/build/reports/lint-results-*.html
|
||
|
||
# Publishes the JUnit XML produced by the unit-test tasks above as inline
|
||
# annotations plus a job summary. Replaces asadmansr/android-test-report-action,
|
||
# which was abandoned (last release 2020) and rebuilt an EOL Ubuntu 18.04 +
|
||
# Python 2 Docker image on every run — bionic's apt archives have since gone
|
||
# unreliable and broke this job. Pinned to a commit SHA (not the movable
|
||
# v6.4.2 tag) to close the supply-chain hole. annotate_only avoids needing
|
||
# `checks: write`, so it keeps working on pull requests from forks (where the
|
||
# GITHUB_TOKEN is read-only). fail_on_failure preserves the old step's
|
||
# behavior of marking the job red when a test fails.
|
||
- name: Android Test Report
|
||
uses: mikepenz/action-junit-report@a9170d5795813c01ab4901ffb045b52bab4ab09d # v6.5.0
|
||
if: always()
|
||
with:
|
||
report_paths: '**/build/test-results/**/TEST-*.xml'
|
||
annotate_only: true
|
||
detailed_summary: true
|
||
fail_on_failure: true
|
||
|
||
- name: Upload Test Results
|
||
uses: actions/upload-artifact@v7
|
||
if: failure()
|
||
with:
|
||
name: Test Reports
|
||
path: amethyst/build/reports
|
||
|
||
# With -PdisableAbiSplits=true the APK is named without the ABI/universal
|
||
# suffix: amethyst-<flavor>-benchmark.apk. Glob both forms so this still
|
||
# works if a contributor runs CI on a branch that doesn't pass the flag.
|
||
- name: Upload Play APK Benchmark
|
||
uses: actions/upload-artifact@v7
|
||
with:
|
||
name: Play Benchmark APK
|
||
path: |
|
||
amethyst/build/outputs/apk/play/benchmark/amethyst-play-benchmark.apk
|
||
amethyst/build/outputs/apk/play/benchmark/amethyst-play-universal-benchmark.apk
|
||
|
||
- name: Upload FDroid APK Benchmark
|
||
uses: actions/upload-artifact@v7
|
||
with:
|
||
name: FDroid Benchmark APK
|
||
path: |
|
||
amethyst/build/outputs/apk/fdroid/benchmark/amethyst-fdroid-benchmark.apk
|
||
amethyst/build/outputs/apk/fdroid/benchmark/amethyst-fdroid-universal-benchmark.apk
|
||
|
||
- name: Upload Compose Reports
|
||
uses: actions/upload-artifact@v7
|
||
with:
|
||
name: Compose Reports
|
||
path: amethyst/build/compose_compiler
|