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
1 change: 1 addition & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ko_fi: superuser404
77 changes: 0 additions & 77 deletions .github/workflows/beta-sdk-watch.yml

This file was deleted.

97 changes: 97 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,20 @@ jobs:
- name: swift test
run: swift test

- name: Software VOD packet-cache regressions
run: |
bash Scripts/test-software-packet-coverage.sh
bash Scripts/test-software-video-packet-coverage.sh
bash Scripts/test-software-packet-disk-fifo.sh
bash Scripts/test-software-packet-read-ahead.sh
bash Scripts/test-software-read-admission.sh
bash Scripts/test-software-stored-packet.sh

- name: Partial H.264 composition-offset runtime regression
run: |
command -v ffmpeg >/dev/null || brew install ffmpeg
bash Scripts/test-h264-partial-composition-controls.sh

# The transport probe is a device harness and runs nowhere in CI, so nothing else would keep
# it compiling. A harness that stopped building is discovered by the person who needed it.
- name: build the transport probe (AE#377)
Expand All @@ -64,6 +78,23 @@ jobs:
-derivedDataPath .build/xcode-tvos \
CODE_SIGNING_ALLOWED=NO

# The test target is the only place a platform gate in TEST code is ever checked. `swift test`
# on macOS compiles the macOS side of every `#if`, so a gate put around a helper instead of
# around the tests that call it looks complete there and costs nothing until someone builds
# for a simulator. That is how `AetherEngineTests` stopped compiling for iOS and tvOS without
# a single red run (2026-09-14, HLSOriginRelayTests).
#
# EXCLUDED_ARCHS because a generic simulator destination builds both slices and `libdovi.a`
# ships arm64 only, so the x86_64 link fails and says nothing about this repo.
- name: Build the tests for tvOS Simulator
run: |
xcodebuild build-for-testing \
-scheme AetherEngineTests \
-destination 'generic/platform=tvOS Simulator' \
-derivedDataPath .build/xcode-tvos-tests \
EXCLUDED_ARCHS=x86_64 \
CODE_SIGNING_ALLOWED=NO

# The transport probe also builds as an app-hosted test bundle, because a SwiftPM test target
# is tool-hosted and tool-hosted testing does not exist on device destinations. That project
# runs nowhere in CI either, and the person who discovers it stopped building is the one who
Expand Down Expand Up @@ -96,6 +127,17 @@ jobs:
-derivedDataPath .build/xcode-ios \
CODE_SIGNING_ALLOWED=NO

# The other half of the gate check above; see the tvOS job for why the test target needs its
# own build and why x86_64 is excluded.
- name: Build the tests for iOS Simulator
run: |
xcodebuild build-for-testing \
-scheme AetherEngineTests \
-destination 'generic/platform=iOS Simulator' \
-derivedDataPath .build/xcode-ios-tests \
EXCLUDED_ARCHS=x86_64 \
CODE_SIGNING_ALLOWED=NO

# #344: the package has declared visionOS since 6.0.0 and nothing here compiled for it, so an
# availability list that named tvOS/iOS/macOS and then fell through to `*` broke the visionOS
# build silently. `*` resolves to the declared visionOS floor (1.0), so any API newer than that
Expand All @@ -120,6 +162,61 @@ jobs:
-derivedDataPath .build/xcode-visionos \
CODE_SIGNING_ALLOWED=NO

# The Xcode 27 lane. Every job above resolves `latest-stable` on macos-15, which is Xcode 26.3,
# while the engine is consumed and released from Xcode 27, and Swift 6.4 rejects isolation shapes
# 6.3 accepted (the Sodalite test target stopped compiling on exactly that, 2026-09-16). These two
# jobs compile the package and both test targets against the 27 toolchain, for macOS and for the
# tvOS 27 SDK.
#
# They build and do not run the tests, on purpose. `xcode-27` is GitHub's preview image and runs a
# BETA macOS 27 (26A5406e) under Xcode 27 beta 6: measured on two attempts of the first run, Vision
# text recognition returns nil there and the loopback-origin tests starve (a global-queue block not
# scheduled within 30 s, a TLS relay request timing out), a different set of network tests each
# time, while all 2983 pass on macOS 27 GA on a Mac. A lane that is red for its image teaches
# everyone to ignore red. Run `swift test` here once a GA macOS 27 image ships, and that is also
# the point to move to its label; the preview label disappearing fails these jobs loudly first.
# This lane replaces the weekly beta SDK watcher, whose one subject (#351) the 7.0 platform floor
# closed.
build-macos-xcode27:
name: swift build --build-tests (macOS, Xcode 27)
runs-on: xcode-27
timeout-minutes: 30
steps:
- uses: actions/checkout@v4

- name: Show toolchain
run: |
swift --version
xcodebuild -version

- name: swift build --build-tests
run: swift build --build-tests

build-tvos-xcode27:
name: xcodebuild (tvOS Simulator, Xcode 27)
runs-on: xcode-27
timeout-minutes: 30
steps:
- uses: actions/checkout@v4

- name: Build for tvOS Simulator
run: |
xcodebuild build \
-scheme AetherEngine \
-destination 'generic/platform=tvOS Simulator' \
-derivedDataPath .build/xcode-tvos \
CODE_SIGNING_ALLOWED=NO

# See the tvOS job above for why the test target needs its own build and why x86_64 is excluded.
- name: Build the tests for tvOS Simulator
run: |
xcodebuild build-for-testing \
-scheme AetherEngineTests \
-destination 'generic/platform=tvOS Simulator' \
-derivedDataPath .build/xcode-tvos-tests \
EXCLUDED_ARCHS=x86_64 \
CODE_SIGNING_ALLOWED=NO

# The docs site (aetherengine-website) validates every heading anchor at build time, but it
# lives in another repo and only builds AFTER a push to main, so a broken anchor ships first
# and is found second: `docs/api.md` linked `formats.md#dolby-vision` against a heading that
Expand Down
98 changes: 91 additions & 7 deletions .github/workflows/used-by-add.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
name: Add to Used by

# When a maintainer adds the "approved" label to a "used-by-submission" issue,
# parse the issue form, insert the entry into the README's Used by list (between
# the <!-- used-by:start --> / <!-- used-by:end --> markers), and open a PR.
# When a "used-by-submission" issue carries the "approved" label, parse the issue
# form, insert the entry into the README's Used by list (between the
# <!-- used-by:start --> / <!-- used-by:end --> markers), and open a PR.
# Merging the PR is the final approval gate; this workflow never pushes to main.

on:
Expand All @@ -15,19 +15,57 @@ permissions:

jobs:
add-entry:
# Either label can arrive last. Submissions that reach the tracker outside the
# issue form (mobile clients, the API) carry no template label, so the
# maintainer adds it after "approved" is already on; keying the run on
# "approved" alone lost those runs with no way to retry but to re-apply the label.
if: >
github.event.label.name == 'approved' &&
(github.event.label.name == 'approved' || github.event.label.name == 'used-by-submission') &&
contains(github.event.issue.labels.*.name, 'approved') &&
contains(github.event.issue.labels.*.name, 'used-by-submission')
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Normalise the issue body
id: normalise
env:
# Body content stays in env, it is never interpolated into the script.
ISSUE_BODY: ${{ github.event.issue.body }}
run: |
set -euo pipefail

# The parser splits a section into key and value on the blank line that
# the web form writes after each "### " heading. A body that arrives
# without it (mobile clients, hand-written, copied between issues)
# collapses the whole section into the key, leaves the value undefined,
# and JSON.stringify drops undefined, so the parser returns {} and every
# field reads as missing. Re-inserting the blank line makes both shapes
# parse; a doubled blank line is harmless, the parser filters empties.
# Written outside the checkout so it cannot end up in the commit.
printf '%s\n' "$ISSUE_BODY" | awk '
BEGIN { fence = 0 }
/^```/ { fence = !fence; print; next }
!fence && /^### / { print; print ""; next }
{ print }
' > "$RUNNER_TEMP/body.md"

# Random delimiter: the body is untrusted and must not be able to close
# its own heredoc and write further outputs.
delim="BODY_EOF_$(openssl rand -hex 16)"
{
echo "body<<$delim"
cat "$RUNNER_TEMP/body.md"
echo "$delim"
} >> "$GITHUB_OUTPUT"

- name: Parse issue form
id: parse
uses: stefanbuck/github-issue-parser@v3
with:
template-path: .github/ISSUE_TEMPLATE/used-by-submission.yml
issue-body: ${{ steps.normalise.outputs.body }}

- name: Build entry and insert into README
id: build
Expand All @@ -41,21 +79,45 @@ jobs:
url=$(printf '%s' "$ISSUE_JSON" | jq -r '.url // empty' | head -n1)
desc=$(printf '%s' "$ISSUE_JSON" | jq -r '.description // empty' | head -n1)

# A submission that cannot be read is a failure, not a no-op. It used to
# exit 0, which left a green run, no PR and no hint that anything broke.
if [ -z "$name" ] || [ -z "$url" ] || [ -z "$desc" ]; then
echo "Missing a required field (name/url/description). Skipping."
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
{
echo "### Could not read the submission"
echo
echo "Missing:"
if [ -z "$name" ]; then echo "- app name"; fi
if [ -z "$url" ]; then echo "- url"; fi
if [ -z "$desc" ]; then echo "- description"; fi
echo
echo "Parsed form:"
echo '```json'
printf '%s\n' "$ISSUE_JSON"
echo '```'
echo
echo "Fix the issue body to match the form, then re-apply the \`approved\` label."
} >> "$GITHUB_STEP_SUMMARY"
echo "::error::Could not read name/url/description from the submission."
exit 1
fi

# Normalise: trim whitespace, collapse to one trailing period.
name=$(printf '%s' "$name" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
url=$(printf '%s' "$url" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
desc=$(printf '%s' "$desc" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' -e 's/\.*$//')

# The markers are what the insert aims at. Without them awk would print
# the file back unchanged and the PR step would find nothing to commit.
if ! grep -q '<!-- used-by:start -->' README.md || ! grep -q '<!-- used-by:end -->' README.md; then
echo "::error::The used-by markers are missing from README.md."
exit 1
fi

# Dedupe: bail out if the URL is already listed between the markers.
block=$(sed -n '/<!-- used-by:start -->/,/<!-- used-by:end -->/p' README.md)
if printf '%s' "$block" | grep -qF "$url"; then
echo "$url is already in the Used by list. Skipping."
echo "Skipped: [$name]($url) is already in the Used by list." >> "$GITHUB_STEP_SUMMARY"
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
Expand All @@ -70,6 +132,12 @@ jobs:
' README.md > "$tmp"
mv "$tmp" README.md

# Prove the insert landed, so an empty PR can never be the outcome.
if git diff --quiet -- README.md; then
echo "::error::README.md is unchanged after the insert."
exit 1
fi

echo "skip=false" >> "$GITHUB_OUTPUT"
echo "name=$name" >> "$GITHUB_OUTPUT"
{
Expand All @@ -82,8 +150,11 @@ jobs:

- name: Open pull request
if: steps.build.outputs.skip == 'false'
id: pr
uses: peter-evans/create-pull-request@v6
with:
# Only the README, so no stray runner file can ride along.
add-paths: README.md
branch: used-by/issue-${{ github.event.issue.number }}
commit-message: "docs(used-by): add ${{ steps.build.outputs.name }}"
title: "docs(used-by): add ${{ steps.build.outputs.name }}"
Expand All @@ -95,3 +166,16 @@ jobs:
Closes #${{ github.event.issue.number }}
labels: used-by-submission
delete-branch: true

- name: Confirm the pull request exists
if: steps.build.outputs.skip == 'false'
env:
PR_NUMBER: ${{ steps.pr.outputs.pull-request-number }}
PR_URL: ${{ steps.pr.outputs.pull-request-url }}
run: |
set -euo pipefail
if [ -z "$PR_NUMBER" ]; then
echo "::error::The README changed but no pull request was created."
exit 1
fi
echo "Pull request: $PR_URL" >> "$GITHUB_STEP_SUMMARY"
Loading
Loading