Two gaps, both found the same way -- by something going wrong quietly.
`gh issue create` does not touch the project board. The issue is created, carries its
labels, and is invisible in the Kanban, which looks exactly like a ticket nobody filed.
On 2026-08-24 eight issues filed as a scripted batch all reached the board and one filed
as a one-off minutes later did not; it surfaced only because someone went looking for it.
A batch carries the board step inside its loop. One-offs are where it slips, so
tools/github/file-issue.sh is for one-offs.
Three things it does that a two-command shell snippet would not:
- Resolves the project, Status field and option ids BY NAME, every run. Caching them
is the obvious optimisation and the wrong one -- a renamed or reordered column would
then have this writing a stale id into the board with no error anywhere.
- Reads the item back. A mutation returning 200 says the request was accepted, not that
the board shows what was asked for; the read-back is the only step that checks the
claim this script exists to make. It is a GraphQL query because REST cannot do it --
the `fields` array REST returns on a project item carries Title and nothing else, so
a REST-only check reports every item's Status as unset.
- Exits 3, loudly, with the issue number on a line of its own, when the issue was
created but the board step failed. That exact combination is the failure being
prevented; it must never be the quiet path.
Shell was the other language here with nothing checking it -- four scripts, one of them
the CI entry point. shellcheck now runs in the Static analysis job over
`git ls-files '*.sh'`, so a script added later is covered without editing the workflow,
and it runs at full severity with `info` included.
That raises two findings today and both are the tool being wrong, so both are answered
with a targeted `disable` carrying its reason rather than by lowering the severity:
run-e2e.sh's `on_signal` is reported as never invoked when it is installed as the INT and
TERM trap eleven lines below it, and the `$names` inside file-issue.sh's queries are
GraphQL variables that must not expand -- expanding them would send the shell's idea of
$owner to the API instead of declaring a parameter. A blanket --severity=warning would
have hidden both, and the next real finding with them.
The gradle step gains `if: !cancelled()` so a shellcheck failure cannot cost the
ktlint/detekt/lint lists -- the same reason that step already passes --continue.
Not covered, deliberately: shellcheck here reads .sh files, not the inline `run:` blocks
in the workflows, where a good deal of this repo's bash actually lives. actionlint does
read them, and finds one pre-existing info-level issue in build.yml. Wiring it in means
pinning a container digest, because every action here is pinned by SHA and actionlint's
usual installer is a curl-pipe-bash off a moving branch. Its own ticket, not this commit.
449 lines
22 KiB
YAML
449 lines
22 KiB
YAML
name: Status check
|
|
|
|
on:
|
|
pull_request:
|
|
branches: [main]
|
|
|
|
# A newer push to the same PR makes the in-flight run obsolete. An emulator matrix is
|
|
# expensive, so cancel rather than let runs pile up.
|
|
concurrency:
|
|
group: status-check-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
# Third-party actions are pinned to a commit rather than a tag. A tag is mutable: the
|
|
# owner can repoint v4 at new code, so a tag reference is an open invitation to run
|
|
# whatever that repository contains tomorrow. The trailing comment records which
|
|
# release each hash corresponds to, since a bare hash is unreadable.
|
|
# Nothing here writes: these jobs read the code, build it and attach reports. Declared
|
|
# explicitly rather than inherited from the repository default, for the same reason the
|
|
# action SHAs above are pinned -- the token's reach should be readable here, and a default
|
|
# that widens later should not silently widen these jobs with it. build.yml's release job
|
|
# makes the opposite declaration for the same reason.
|
|
permissions:
|
|
contents: read
|
|
|
|
env:
|
|
GRADLE_CACHE_PATHS: |
|
|
~/.gradle/caches
|
|
~/.gradle/wrapper
|
|
|
|
jobs:
|
|
# ---------------------------------------------------------------------------
|
|
# Validates the committed FFmpeg archive. It does not build anything: the whole
|
|
# point of checking the binary in is that a red run means broken code rather than
|
|
# a cross-compile that hiccuped.
|
|
#
|
|
# Its own job so a bad archive reports once, clearly, instead of surfacing as five
|
|
# confusing emulator failures. It takes seconds, so gating the matrix on it costs
|
|
# almost nothing.
|
|
# ---------------------------------------------------------------------------
|
|
ffmpeg:
|
|
name: FFmpeg binary
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
|
|
- name: Verify the committed archive
|
|
run: |
|
|
AAR=bin/ffmpeg-kit-next-8.1.1.aar
|
|
test -f "$AAR" || { echo "::error::$AAR is missing"; exit 1; }
|
|
|
|
# A truncated file, or a Git LFS pointer checked out without LFS, would pass
|
|
# a file-exists check and then surface much later as a confusing linker
|
|
# error. Assert the archive actually carries native libraries for both ABIs.
|
|
for abi in arm64-v8a x86_64; do
|
|
n=$(unzip -l "$AAR" | grep -c "jni/$abi/.*\.so$" || true)
|
|
echo " $abi: $n shared libraries"
|
|
test "$n" -gt 0 || { echo "::error::AAR has no $abi libraries"; exit 1; }
|
|
done
|
|
|
|
# 16 KB alignment is a Play requirement and is easy to lose in a rebuild,
|
|
# so it is checked here rather than discovered at submission.
|
|
unzip -q -o "$AAR" 'jni/*' -d /tmp/aarcheck
|
|
bad=0
|
|
for f in /tmp/aarcheck/jni/*/*.so; do
|
|
align=$(readelf -lW "$f" | awk '$1=="LOAD"{print $NF}' | sort -u)
|
|
if [ "$align" != "0x4000" ]; then
|
|
echo "::error::$(basename "$f") is $align, not 16 KB aligned"; bad=1
|
|
fi
|
|
done
|
|
test "$bad" -eq 0 || exit 1
|
|
echo " all libraries are 16 KB aligned"
|
|
|
|
# Record what shipped, so a failing run elsewhere can be tied to a version.
|
|
echo " sha256: $(sha256sum "$AAR" | cut -d' ' -f1)"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# JVM tests: the routing matrix, the FFmpeg argument builder, the concat planner
|
|
# and the retry rule. No device needed, so this is the fastest signal on a PR.
|
|
# ---------------------------------------------------------------------------
|
|
unit:
|
|
name: Unit tests
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
|
|
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
|
with:
|
|
distribution: temurin
|
|
java-version: '25' # Matches the daemon JVM pinned in gradle/gradle-daemon-jvm.properties
|
|
|
|
# Gradle is invoked through the committed wrapper rather than a setup action.
|
|
# The wrapper verifies its own distribution against distributionSha256Sum, and
|
|
# caching is a handful of lines, so the action earned little here.
|
|
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: ${{ env.GRADLE_CACHE_PATHS }}
|
|
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle.kts', 'gradle/libs.versions.toml', 'gradle/wrapper/gradle-wrapper.properties') }}
|
|
restore-keys: gradle-${{ runner.os }}-
|
|
|
|
- name: Unit tests
|
|
run: ./gradlew :app:testDebugUnitTest
|
|
|
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
if: always()
|
|
with:
|
|
name: unit-test-report
|
|
path: app/build/reports/tests/
|
|
|
|
# Reported, not gated. A coverage floor is only meaningful against a measured
|
|
# baseline, and this is the thing that measures it -- currently 31% of lines. Once
|
|
# that number has settled, a jacocoTestCoverageVerification task can hold it.
|
|
- name: Coverage report
|
|
run: ./gradlew :app:jacocoTestReport
|
|
|
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
if: always()
|
|
with:
|
|
name: coverage-report
|
|
path: app/build/reports/jacoco/jacocoTestReport/
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Three tools, one job, because they answer three different questions and a
|
|
# developer wants all three answers at once rather than one per push.
|
|
#
|
|
# ktlint -- formatting. Owns it outright; detekt's formatting ruleset is off,
|
|
# so the two can never disagree about the same line.
|
|
# detekt -- static analysis. Its config lives in config/detekt/detekt.yml and
|
|
# overrides only the rules this codebase legitimately breaks.
|
|
# lint -- the Android-specific things neither of the others can see: opt-in
|
|
# markers, API-level misuse, manifest and resource problems.
|
|
#
|
|
# --continue is what makes it one round trip: a ktlint failure still lets detekt
|
|
# and lint report, so a red run hands over the whole list rather than the first
|
|
# item on it.
|
|
#
|
|
# No emulator and no FFmpeg archive needed, so this is the cheapest gate here and
|
|
# deliberately does not depend on the ffmpeg job.
|
|
# ---------------------------------------------------------------------------
|
|
static-analysis:
|
|
name: Static analysis
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
|
|
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
|
with:
|
|
distribution: temurin
|
|
java-version: '25'
|
|
|
|
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: ${{ env.GRADLE_CACHE_PATHS }}
|
|
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle.kts', 'gradle/libs.versions.toml', 'gradle/wrapper/gradle-wrapper.properties') }}
|
|
restore-keys: gradle-${{ runner.os }}-
|
|
|
|
# Shell is the other language in this repo -- four scripts, one of them the CI
|
|
# entry point itself -- and nothing was checking it. `git ls-files` rather than a
|
|
# fixed list, so a script added later is covered without editing this workflow.
|
|
#
|
|
# Full severity, `info` included. The two findings it raises today are answered
|
|
# with targeted `disable` directives carrying their reason, the same way
|
|
# config/detekt/detekt.yml carries only the rules this codebase legitimately
|
|
# breaks. A blanket --severity=warning would have hidden them and the next real
|
|
# one alike. shellcheck is preinstalled on the ubuntu runner image; the version is
|
|
# printed so a finding that appears out of nowhere can be pinned to an upgrade.
|
|
- name: shellcheck
|
|
run: |
|
|
shellcheck --version
|
|
git ls-files -z '*.sh' | xargs -0 -r shellcheck
|
|
|
|
# `!cancelled()` rather than a plain sequence: a shellcheck failure above must not
|
|
# cost the ktlint/detekt/lint lists. Same reason this step passes --continue -- one
|
|
# round trip should produce every list, not stop at the first.
|
|
- name: ktlint, detekt and Android lint
|
|
if: '!cancelled()'
|
|
run: ./gradlew :app:ktlintCheck :app:detekt :app:lintDebug --continue --stacktrace
|
|
|
|
# The XML matters as much as the HTML: it is the one that can be diffed between
|
|
# runs to see what a change actually moved.
|
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
if: always()
|
|
with:
|
|
name: static-analysis-reports
|
|
path: |
|
|
app/build/reports/ktlint/
|
|
app/build/reports/detekt/
|
|
app/build/reports/lint-results-debug.html
|
|
app/build/reports/lint-results-debug.xml
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# One runner per API level, across the whole supported range.
|
|
#
|
|
# The range is the point: minSdk is 33, and the foreground service type differs
|
|
# across it -- none below 34, dataSync at 34, mediaProcessing from 35. Testing a
|
|
# single level would leave two thirds of that branch unexercised.
|
|
#
|
|
# It reaches targetSdk 37, but the API 37 row is not like the other four and the
|
|
# comment on it says how. Two tests are excluded there and run in their own
|
|
# advisory job below. See docs/api-37-emulator-crash.md.
|
|
#
|
|
# FFmpeg is not built here. The AAR is committed under bin/, so a red run means the
|
|
# code is broken rather than that a cross-compile hiccuped.
|
|
# ---------------------------------------------------------------------------
|
|
e2e:
|
|
name: E2E API ${{ matrix.label }}
|
|
runs-on: ubuntu-latest
|
|
needs: ffmpeg
|
|
timeout-minutes: 60
|
|
strategy:
|
|
# Report every API level rather than stopping at the first red one. Knowing
|
|
# whether a failure is universal or specific to one level is most of the
|
|
# diagnosis.
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- label: "33"
|
|
api-level: "33"
|
|
- label: "34"
|
|
api-level: "34"
|
|
- label: "35"
|
|
api-level: "35"
|
|
- label: "36"
|
|
api-level: "36"
|
|
# API 37, and it is NOT the same device as the four rows above it.
|
|
#
|
|
# CAVEAT, read this before trusting a green here: this leg runs with
|
|
# SystemUI disabled and the framework restarted under it. No other leg
|
|
# and no Pixel run uses that configuration. It is defensible only because
|
|
# nothing in this suite touches system UI -- these are Media3, FFmpeg and
|
|
# WorkManager tests -- and because the alternative is no CI coverage of
|
|
# the level this app targets. **Anything that ever does depend on system
|
|
# UI must not trust this row.** E2E_DISABLE_SYSTEM_UI is what does it;
|
|
# .github/scripts/e2e-run.sh explains the mechanism and why every step of
|
|
# it is verified rather than assumed.
|
|
#
|
|
# api-level must be "37.0". A bare 37 is not an SDK package and fails
|
|
# during setup, which cost a run to discover.
|
|
#
|
|
# notAnnotation removes the two tests that do not pass on this image; they
|
|
# run in the advisory job below, off the same marker so they cannot end up
|
|
# in both or neither. docs/api-37-emulator-crash.md has the measurements.
|
|
- label: "37"
|
|
api-level: "37.0"
|
|
disable-system-ui: "1"
|
|
gradle-args: "-Pandroid.testInstrumentationRunnerArguments.notAnnotation=org.libremediaconverter.FailsOnEmulatorApi37"
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
|
|
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
|
with:
|
|
distribution: temurin
|
|
java-version: '25'
|
|
|
|
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: ${{ env.GRADLE_CACHE_PATHS }}
|
|
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle.kts', 'gradle/libs.versions.toml', 'gradle/wrapper/gradle-wrapper.properties') }}
|
|
restore-keys: gradle-${{ runner.os }}-
|
|
|
|
# Without this the emulator falls back to software rendering and takes minutes
|
|
# longer to boot, when it boots at all.
|
|
- name: Enable KVM
|
|
run: |
|
|
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \
|
|
| sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
|
sudo udevadm control --reload-rules
|
|
sudo udevadm trigger --name-match=kvm
|
|
|
|
- name: Instrumented tests
|
|
uses: reactivecircus/android-emulator-runner@a421e43855164a8197daf9d8d40fe71c6996bb0d # v2.38.0
|
|
# Both of these are empty on every row but 37, and both are read with a
|
|
# `:-` default in e2e-run.sh, so the four legs below 37 run the identical
|
|
# gradle command they always have.
|
|
env:
|
|
E2E_DISABLE_SYSTEM_UI: ${{ matrix.disable-system-ui }}
|
|
E2E_EXTRA_GRADLE_ARGS: ${{ matrix.gradle-args }}
|
|
with:
|
|
api-level: ${{ matrix.api-level }}
|
|
target: google_apis
|
|
arch: x86_64
|
|
profile: pixel_6
|
|
# swiftshader_indirect is correct here only because runners have no GPU to
|
|
# pass through. On a workstation the same setting routes through
|
|
# SwiftShader's JIT, which is a known crash source.
|
|
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
|
disable-animations: true
|
|
# The default userdata partition is not big enough for this APK once the
|
|
# FFmpeg libraries are in it. One level failed outright with "Requested
|
|
# internal only, but not enough space", and the margin was thin everywhere
|
|
# else, so give them all room.
|
|
disk-size: 8G
|
|
# Pinned because the emulator's own default is not uniform: it raises an
|
|
# undersized guest to a floor that varies by API level -- 2048M at 33, 2560M
|
|
# at 34 through 36 -- and skips levels it does not recognise entirely. 2560M
|
|
# is the highest of those floors, so no level gets less memory than it
|
|
# already had, and none of them depend on that heuristic any more.
|
|
ram-size: 2560M
|
|
# Build only the ABI the emulator can execute. FFmpeg's native libraries
|
|
# dominate the APK, so shipping arm64 to an x86_64 emulator doubles the
|
|
# install for code that can never run: 114 MB against 80 MB.
|
|
#
|
|
# Diagnostics live in .github/scripts/e2e-run.sh, not here. This action splits
|
|
# `script` on newlines and runs each line as its own `sh -c`, so a handler written
|
|
# inline has to fit on ONE line -- which is how the previous version ended up as an
|
|
# unreadable chain of semicolons. One line invokes the script; the script can use
|
|
# functions, and captures a hang as well as a failure. See its header.
|
|
script: bash .github/scripts/e2e-run.sh ${{ matrix.label }}
|
|
|
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
if: always()
|
|
with:
|
|
name: e2e-report-api${{ matrix.label }}
|
|
path: |
|
|
app/build/reports/androidTests/
|
|
app/build/outputs/androidTest-results/
|
|
if-no-files-found: warn
|
|
|
|
# The streamed logcat and the failure dump. Uploaded always, because a leg that goes red
|
|
# once and green on re-run is exactly the one worth reading afterwards, and by then the
|
|
# emulator is long gone.
|
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
if: always()
|
|
with:
|
|
name: e2e-diagnostics-api${{ matrix.label }}
|
|
path: |
|
|
${{ runner.temp }}/logcat-api${{ matrix.label }}.txt
|
|
${{ runner.temp }}/diagnostics-api${{ matrix.label }}.txt
|
|
if-no-files-found: warn
|
|
|
|
# Only exists when the wrapper timeout tripped, so `ignore` keeps healthy runs quiet
|
|
# instead of warning on every green leg.
|
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
if: always()
|
|
with:
|
|
name: e2e-wedge-api${{ matrix.label }}
|
|
path: ${{ runner.temp }}/wedge-diagnostics-api${{ matrix.label }}.txt
|
|
if-no-files-found: ignore
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# The two API 37 tests the gating row above excludes, run on their own so they
|
|
# stay visible instead of disappearing behind a notAnnotation.
|
|
#
|
|
# continue-on-error: it reports, it never blocks. That is the whole reason it is
|
|
# a separate job rather than a sixth matrix row: a row would share the gating
|
|
# job's `E2E API <label>` name, and a check cannot be both required and advisory
|
|
# under one name.
|
|
#
|
|
# It is named for WHAT IT RUNS, deliberately. Both tests drive a full H.264 ->
|
|
# H.265 hardware transcode through Media3Engine -- which is exactly what
|
|
# separates them from the two Media3EngineTest cases that pass here, since those
|
|
# two never decode video. The current theory about why they fail is in the next
|
|
# paragraph, where it can be corrected without renaming a check that people have
|
|
# already learned to look for.
|
|
#
|
|
# THEORY, NOT SETTLED: the exception surfaces at `dequeueOutputBuffer` on
|
|
# `c2.goldfish.h264.decoder`, the emulator's own codec, which gets its frames out
|
|
# of a host-side colour buffer -- the same readback machinery that aborts
|
|
# surfaceflinger on this image. What is MEASURED is narrower: these two fail on
|
|
# the API 37 emulator image; pass at API 36 on this runner under the same renderer
|
|
# AND the same SystemUI-disable path; pass at API 33-36 without that path at all,
|
|
# since nothing below 37 needs it; and pass on a physical Pixel 10 Pro XL at 37. That the decoder is the culprit rather than something else
|
|
# the decode path touches is inference. docs/api-37-emulator-crash.md separates
|
|
# the two, and the images also differ on the encoder side, which is why "broken
|
|
# h264 decoder" is not written into this job's name.
|
|
#
|
|
# WHEN THIS GOES GREEN, delete the annotation rather than this job: the gating
|
|
# row picks the tests back up automatically, and this job goes empty and can go
|
|
# with it.
|
|
# ---------------------------------------------------------------------------
|
|
e2e-api37-advisory:
|
|
name: E2E API 37 Media3 hardware transcode (advisory)
|
|
runs-on: ubuntu-latest
|
|
needs: ffmpeg
|
|
timeout-minutes: 60
|
|
continue-on-error: true
|
|
env:
|
|
E2E_LABEL: "37-media3-transcode"
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
|
|
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
|
with:
|
|
distribution: temurin
|
|
java-version: '25'
|
|
|
|
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: ${{ env.GRADLE_CACHE_PATHS }}
|
|
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle.kts', 'gradle/libs.versions.toml', 'gradle/wrapper/gradle-wrapper.properties') }}
|
|
restore-keys: gradle-${{ runner.os }}-
|
|
|
|
- name: Enable KVM
|
|
run: |
|
|
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \
|
|
| sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
|
sudo udevadm control --reload-rules
|
|
sudo udevadm trigger --name-match=kvm
|
|
|
|
- name: Instrumented tests
|
|
uses: reactivecircus/android-emulator-runner@a421e43855164a8197daf9d8d40fe71c6996bb0d # v2.38.0
|
|
env:
|
|
E2E_DISABLE_SYSTEM_UI: "1"
|
|
# The complement of the gating row's notAnnotation, off the same marker,
|
|
# so a test can never be excluded from both jobs or run in both.
|
|
E2E_EXTRA_GRADLE_ARGS: "-Pandroid.testInstrumentationRunnerArguments.annotation=org.libremediaconverter.FailsOnEmulatorApi37"
|
|
with:
|
|
# Every device pin below matches the gating row exactly, so a difference
|
|
# between the two jobs is the test selection and nothing else.
|
|
api-level: "37.0"
|
|
target: google_apis
|
|
arch: x86_64
|
|
profile: pixel_6
|
|
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
|
disable-animations: true
|
|
disk-size: 8G
|
|
ram-size: 2560M
|
|
script: bash .github/scripts/e2e-run.sh ${{ env.E2E_LABEL }}
|
|
|
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
if: always()
|
|
with:
|
|
name: e2e-report-api${{ env.E2E_LABEL }}
|
|
path: |
|
|
app/build/reports/androidTests/
|
|
app/build/outputs/androidTest-results/
|
|
if-no-files-found: warn
|
|
|
|
# Uploaded always, and here it matters more than anywhere else in this file:
|
|
# this job is EXPECTED to be red, so the logcat is the only thing that says
|
|
# whether it is red for the known reason or for a new one.
|
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
if: always()
|
|
with:
|
|
name: e2e-diagnostics-api${{ env.E2E_LABEL }}
|
|
path: |
|
|
${{ runner.temp }}/logcat-api${{ env.E2E_LABEL }}.txt
|
|
${{ runner.temp }}/diagnostics-api${{ env.E2E_LABEL }}.txt
|
|
if-no-files-found: warn
|
|
|
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
if: always()
|
|
with:
|
|
name: e2e-wedge-api${{ env.E2E_LABEL }}
|
|
path: ${{ runner.temp }}/wedge-diagnostics-api${{ env.E2E_LABEL }}.txt
|
|
if-no-files-found: ignore
|