CodeQL alert #1, the only open one on this repository: actions/missing-workflow-permissions, warning / medium, build.yml:23 Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Alerts 2, 3 and 4 were the same rule against status_check.yml and are fixed -- that file has a top-level block. build.yml declares permissions in exactly one place, the release job's `contents: write`, and has no top-level default, so the `test` job inherits the repository setting. **Nothing is over-privileged today.** The repository default is already `read` (default_workflow_permissions: read, can_approve_pull_request_reviews: false, read from the API rather than assumed), so the test job holds a read token now. Saying so matters: this is hygiene, and a commit that implied it was closing a live hole would be overstating it. What it buys is that the default CANNOT widen these jobs later without someone editing this file. That is not invented for the occasion -- it is the argument status_check.yml already makes, which even names this file: 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. So the principle was decided, applied in two workflows and in one job of this one, and the top level of build.yml was the gap. Verified the thing that would actually break: the release job's `contents: write` still wins. Top level is a default, not a ceiling -- parsed and printed both, test inherits `contents: read`, release keeps `contents: write`. Also ran the ticket's mutation, and it found something. Deleting the release job's `contents: write` leaves actionlint green and CodeQL quiet -- a narrower permission is not an alert -- so nothing would catch it until a tagged release failed to publish. That is a separate gap and is filed rather than fixed here. actionlint clean at the pinned digest. Comment and permissions only; no step, job or trigger changes. Closes #100.
146 lines
6.3 KiB
YAML
146 lines
6.3 KiB
YAML
name: Build
|
|
|
|
# Pull requests are covered by status_check.yml, which runs the unit tests and the
|
|
# instrumented suite across API 33-37. This workflow keeps the post-merge and release
|
|
# duties and does not duplicate PR validation.
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
tags: ['v*']
|
|
|
|
# Actions are pinned to a commit rather than a tag, with the release in a trailing
|
|
# comment. A tag is mutable -- the owner can repoint it at new code -- so a tag
|
|
# reference amounts to running whatever that repository contains tomorrow. This matters
|
|
# more here than on pull requests: these jobs sign nothing today, but they do publish
|
|
# the artifacts people install.
|
|
# Declared here rather than inherited, for the reason status_check.yml gives for its own
|
|
# block: the token's reach should be readable in the file that uses it, and a repository
|
|
# default that widens later should not silently widen these jobs with it. The repository
|
|
# default is `read` today, so this changes nothing about what runs -- it fixes what a
|
|
# reader can know without leaving the file, and it is what CodeQL alert #1 asked for.
|
|
#
|
|
# The `release` job below overrides this with `contents: write`, which is how job-level
|
|
# permissions work: this is a default, not a ceiling.
|
|
permissions:
|
|
contents: read
|
|
|
|
env:
|
|
GRADLE_CACHE_PATHS: |
|
|
~/.gradle/caches
|
|
~/.gradle/wrapper
|
|
|
|
jobs:
|
|
test:
|
|
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 runs 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
|
|
|
|
- name: Upload test report
|
|
if: always()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: unit-test-report
|
|
path: app/build/reports/tests/
|
|
|
|
release:
|
|
name: Release
|
|
needs: [test]
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 60
|
|
if: startsWith(github.ref, 'refs/tags/v')
|
|
permissions:
|
|
# Needed to create the release. Declared explicitly rather than relying on the
|
|
# repository default, so the token's reach is visible here.
|
|
contents: write
|
|
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 }}-
|
|
|
|
# No -PabiFilters here: released artifacts must carry every ABI. That override
|
|
# exists only so emulator jobs skip libraries they cannot execute.
|
|
- name: Build release artifacts
|
|
run: ./gradlew :app:assembleRelease :app:bundleRelease
|
|
|
|
- name: Verify the released artifacts
|
|
run: |
|
|
# A glob, not `ls | head`: the glob is already here, and parsing ls is what
|
|
# SC2012 is about. Gradle's names have no spaces today, which is exactly the
|
|
# kind of assumption that holds until it does not.
|
|
apks=(app/build/outputs/apk/release/*.apk)
|
|
APK="${apks[0]}"
|
|
# A release that shipped one ABI, or lost 16 KB alignment, would install
|
|
# fine on a test device and fail for users or at Play submission. Both are
|
|
# cheap to check and expensive to discover later.
|
|
for abi in arm64-v8a x86_64; do
|
|
n=$(unzip -l "$APK" | grep -c "lib/$abi/.*\.so$" || true)
|
|
echo " $abi: $n shared libraries"
|
|
test "$n" -gt 0 || { echo "::error::release APK is missing $abi"; exit 1; }
|
|
done
|
|
unzip -q -o "$APK" 'lib/*' -d /tmp/relcheck
|
|
bad=0
|
|
for f in /tmp/relcheck/lib/*/*.so; do
|
|
align=$(readelf -lW "$f" | awk '$1=="LOAD"{print $NF}' | sort -u)
|
|
[ "$align" = "0x4000" ] || { echo "::error::$(basename "$f") is $align"; bad=1; }
|
|
done
|
|
test "$bad" -eq 0 || exit 1
|
|
echo " all libraries are 16 KB aligned"
|
|
|
|
# GPL-3.0 requires that complete corresponding source accompany the binary.
|
|
# FFmpeg's guidance says to host it on the same server as the binary; for a Play
|
|
# listing that is impossible, so it is attached to the GitHub release next to the
|
|
# APK and linked from both the store listing and the in-app About screen.
|
|
- name: Assemble corresponding source
|
|
run: |
|
|
mkdir -p release-source
|
|
cp -r tools/ffmpeg release-source/
|
|
cp bin/README.md release-source/PREBUILT.md
|
|
{
|
|
echo "FFmpeg corresponding source for ${GITHUB_REF_NAME}"
|
|
echo
|
|
echo "Upstream: https://github.com/arthenica/ffmpeg-kit-next"
|
|
echo "Tag: v8.1.1 (FFmpeg 8.1.2)"
|
|
echo
|
|
echo "tools/ffmpeg reproduces the binary shipped in this release."
|
|
echo "PREBUILT.md records its provenance, including the SHA-256 and the"
|
|
echo "configure line read back out of the shipped libavutil."
|
|
} > release-source/README.txt
|
|
tar czf ffmpeg-corresponding-source.tar.gz release-source
|
|
|
|
- uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2
|
|
with:
|
|
files: |
|
|
app/build/outputs/apk/release/*.apk
|
|
app/build/outputs/bundle/release/*.aab
|
|
ffmpeg-corresponding-source.tar.gz
|
|
LICENSE
|
|
LICENSES/README.md
|