Commit Graph
4 Commits
Author SHA1 Message Date
JMR-devandClaude Opus 5 97cddea973 Pin build.yml's actions and verify what it publishes
Brings the release workflow in line with the status check. It matters more
here, not less: these jobs publish the artifacts people install, so running
whatever a mutable tag points at on the day is a worse bargain than it is on
a pull request.

Every action is pinned to a commit with its release in a trailing comment,
and each hash was checked to resolve to the tag it claims. gradle/actions is
dropped for the same reason as before -- its v6 caching component is closed
source and carries separate terms -- with Gradle running through the
committed wrapper, which verifies its own distribution against
distributionSha256Sum.

The release job now checks what it is about to publish. A release that
shipped a single ABI, or that lost 16 KB alignment in a rebuild, installs
fine on a test device and then fails for users or at Play submission. Both
are cheap to assert and expensive to discover afterwards. It deliberately
does not pass -PabiFilters: that override exists so emulator jobs skip
libraries they cannot execute, and a released artifact must carry every ABI.

The contents permission is declared explicitly rather than inherited from the
repository default, so the token's reach is visible in the file that uses it.

The corresponding-source tarball now includes bin/README.md as PREBUILT.md,
so the GPL source drop carries the shipped binary's SHA-256 and configure
line rather than only the recipe that produces it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 19:08:51 -05:00
JMR-devandClaude Opus 5 128763e99c Commit the FFmpeg binary so test runs stop depending on a rebuild
CI rebuilt FFmpeg on every cold cache, which made results ambiguous: a red run
could mean the code was broken or that a forty-minute cross-compile of FFmpeg,
x264, x265 and SVT-AV1 had hiccuped. Those are not the same signal, and only
one of them is worth a developer's attention. The archive is now checked in
under bin/, so a failing run points at code.

It also removes roughly forty minutes from a cold run and lets a fresh clone
build without a container toolchain.

bin/README.md records provenance -- upstream tag, FFmpeg version, NDK, ABIs,
SHA-256 and the full configure line read back out of the shipped libavutil --
so the binary is auditable rather than opaque. The recipe in tools/ffmpeg
remains the authority: this archive is its output, and is also what satisfies
the GPL corresponding-source obligation.

The status check is now seven independent runners: one validating the archive,
one for the JVM tests, and one per API level from 33 to 37. The FFmpeg job
verifies rather than builds. It asserts native libraries are present for both
ABIs and that every one is 16 KB aligned, which is a Play requirement that is
easy to lose in a rebuild and expensive to discover at submission. Checking
for file existence alone would not do: a Git LFS pointer checked out without
LFS passes that and then surfaces as an obscure linker error much later.

It is a separate job rather than a step in each emulator run so a bad archive
reports once, clearly, instead of five confusing emulator failures.

build.yml no longer builds FFmpeg either, and keeps only its post-merge and
release duties.

Two costs, deliberately accepted. The repository goes from about 1 MB to
35 MB, and every future rebuild adds another 35 MB blob to history
permanently, so bin/README.md says to regenerate only when the FFmpeg version
or the configure flags actually change. And F-Droid's scanner flags checked-in
native libraries, so submitting there needs a scandelete entry for bin/ --
noted in bin/README.md, and nothing prevents a from-source build.

Verified against the relocated archive: 66 unit tests, and 40 instrumented
tests on an API 36 emulator, 0 failures.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 13:18:10 -05:00
JMR-devandClaude Opus 5 54d6e9c57b Add a pull-request status check across API 33-37
Runs the JVM tests and the instrumented suite on every pull request to main,
with one emulator job per supported API level.

The matrix is the whole range rather than a single level because the
foreground-service type differs across it -- none below 34, dataSync at 34,
mediaProcessing from 35 -- so testing one level would leave two thirds of that
branch unexercised. Running the range locally is what caught a test that had
baked in an assumption about the host's encoders.

API 37 needs its image level stated separately. It is published as
android-37.0, not android-37, so a plain integer resolves to nothing and the
image download silently finds no package.

FFmpeg is built once and shared. The AAR is not committed -- 35 MB of native
code, and F-Droid strips checked-in binaries -- but every job needs it, since
the app compiles against it and the instrumented tests exercise it for real.
Building it is a full cross-compile of FFmpeg, x264, x265 and SVT-AV1, so it
is cached on the contents of tools/ffmpeg, which is what actually determines
the output. The job also asserts the AAR carries native libraries for both
ABIs: a truncated or stub archive would otherwise pass a file-exists check and
send the matrix off to fail confusingly five times over.

fail-fast is off. Knowing whether a failure is universal or specific to one
API level is most of the diagnosis.

build.yml no longer runs on pull requests. It triggered on every PR with no
branch filter, so both workflows would have run, and its unit job falls back
to a stub AAR -- a weaker check that could mask a compile break the real one
would catch. It keeps its post-merge and release duties.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 13:08:06 -05:00
JMR-devandClaude Opus 5 bb969ece64 Enable R8 and add release, store and F-Droid infrastructure
Turning on R8 immediately surfaced a latent runtime bug: the ffmpeg-kit-next
wrapper references com.arthenica.smartexception.java.Exceptions from
AbstractSession.fail() in eighteen places, but a local .aar carries no
transitive dependencies, so nothing was pulling it in. Debug builds tolerate
this through lazy class loading -- the class is only touched on an error
path -- so it would have shipped as a crash the first time an FFmpeg
conversion failed. Declared explicitly now.

Keep rules cover the JNI boundary. The native library resolves classes and
methods by name, which R8 cannot see, so without them the FFmpeg calls fail
with NoSuchMethodError in release builds only. Workers are kept too, since
WorkManager reconstructs them reflectively from a class name persisted in its
database, and a rename breaks jobs enqueued before the update.

Verified on the produced artifacts rather than assumed: all 22 native
libraries survive minification and every one is still 16 KB aligned inside
the APK. Release is 82 MB against 115 MB for debug; the AAB is 40 MB and Play
splits it per ABI.

The privacy policy lists every permission, including the three WorkManager
adds automatically (WAKE_LOCK, RECEIVE_BOOT_COMPLETED, ACCESS_NETWORK_STATE).
Checking the merged manifest showed those, and a policy that omitted them
would look dishonest to anyone who inspected the app. INTERNET is genuinely
absent, so "files stay on the device" is enforced by the OS rather than a
promise.

CI runs unit tests on every push and builds the FFmpeg AAR only for release
tags, since that is a full cross-compile. Releases attach the FFmpeg
corresponding source next to the APK: GPL-3.0 requires it, and FFmpeg's
instruction to host it "on the same webserver" cannot be satisfied by a Play
listing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 22:45:40 -05:00