Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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
68 changes: 68 additions & 0 deletions .github/workflows/fixtures.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: Fixtures

# Consumption-topology gate. Builds the Fixtures/DynamicHost apps (one
# dynamic framework wrapping SwiftVLC, static feature libraries above it,
# an app on top) and audits the images each app loads for exactly one
# copy of the libvlc static archive. Catches packaging regressions —
# e.g. a product type change in Package.swift that makes layered
# consumers double-link libvlc — that the test suite and the Showcase
# build can't see.
on:
pull_request:
paths:
- Package.swift
- Fixtures/**
- .github/workflows/fixtures.yml
- Sources/**
push:
branches: [main]

concurrency:
group: fixtures-${{ github.ref }}
cancel-in-progress: true

jobs:
dynamic-host:
# xcodebuild resolves the package graph with Xcode's built-in SwiftPM,
# which must understand the manifests' Swift 6.3 tools-version — needs
# Xcode 26.4+, hence macos-26 (same rationale as the showcase job in
# test.yml).
runs-on: macos-26
timeout-minutes: 45
steps:
- uses: actions/checkout@v6
timeout-minutes: 2

- uses: maxim-lobanov/setup-xcode@v1
timeout-minutes: 5
with:
xcode-version: "26.4"

- name: Print toolchain
timeout-minutes: 1
run: |
xcodebuild -version
xcrun swift --version

# The libvlc xcframework dominates the artifact cache.
- name: Cache libvlc xcframework
uses: actions/cache@v5
timeout-minutes: 5
with:
path: Vendor
key: libvlc-xcf-vendor-${{ hashFiles('Package.swift') }}
enableCrossOsArchive: false

# Download the libvlc xcframework + flip Package.swift to the local
# Vendor checkout so the fixture compiles against in-PR code.
- name: Set up Vendor + local Swift package
timeout-minutes: 5
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./scripts/setup-dev.sh

# Builds both simulator apps and runs the single-copy audit. Linking
# the full libvlc static archive takes several minutes per platform.
- name: Build fixture apps + single-copy audit
timeout-minutes: 35
run: ./Fixtures/DynamicHost/verify.sh
10 changes: 9 additions & 1 deletion .github/workflows/sanitize.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,14 @@ jobs:
# Run only the suites that exist *because* of races/lifecycle/memory
# concerns. The general test suite runs unsanitized in `test.yml`.
#
# Playback-driven race suites (PlayerCleanupRaceTests,
# AudioOutputRaceTests, …) are gated on `TestCondition.canPlayMedia`
# and self-skip under CI=true, so the sanitizers never see them here.
# PlaybackFreeTeardownRaceTests exists to cover the stop, native-
# handle-swap, and offloaded-deinit paths without playback so they
# run under both sanitizers. The residual gap is real-playback
# stop/teardown, which remains device/local-only.
#
# `--no-parallel` per the same rationale as the test job: serialized
# runs avoid main-actor contention that masks real races as timeouts.
#
Expand All @@ -124,4 +132,4 @@ jobs:
xcrun --toolchain "$TOOLCHAINS" swift test \
--sanitize=${{ matrix.sanitizer }} \
--no-parallel \
--filter "RaceTests|StressTests|MemoryTests|MemoryAndRetainTests|MemoryPressureTests|LifecycleStressTests"
--filter "RaceTests|StressTests|MemoryAndRetainTests|MemoryPressureTests|LifecycleStressTests"
58 changes: 58 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,64 @@ jobs:
CODE_SIGNING_REQUIRED=NO \
CODE_SIGNING_ALLOWED=NO

# tvOS test-run gate. The `test` job below runs `swift test` on the macOS
# host, which only type-checks the macOS slice of the test target — the
# tvOS slices of Sources and Tests are never *executed* anywhere else.
# This job runs the SwiftVLCTests suite on a tvOS simulator against real
# libVLC, so tvOS-only runtime breakage can't reach `main` unseen.
tvos-test:
# Same rationale as ios-build: xcodebuild resolves the package with
# Xcode's built-in SwiftPM, which must understand the manifest's Swift
# 6.3 tools-version — needs Xcode 26.4+, hence macos-26.
runs-on: macos-26
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
timeout-minutes: 2

- uses: maxim-lobanov/setup-xcode@v1
timeout-minutes: 5
with:
xcode-version: "26.4"

- name: Cache libvlc xcframework
uses: actions/cache@v5
timeout-minutes: 5
with:
path: Vendor
key: libvlc-xcf-vendor-${{ hashFiles('Package.swift') }}
enableCrossOsArchive: false

- name: Set up Vendor + local Swift package
timeout-minutes: 5
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./scripts/setup-dev.sh

# TEST_RUNNER_CI=true is load-bearing: xcodebuild strips the
# TEST_RUNNER_ prefix from variables in its own *environment* and
# forwards them into the test process, so the test process sees
# CI=true. That is how TestCondition.canPlayMedia (which reads CI)
# makes the playback-gated tests self-skip on the headless runner —
# without it those tests would hang or fail for lack of video
# output. It must be set via `env:` — passing it as an xcodebuild
# command-line argument would make it a build setting, which is
# never forwarded.
- name: Run tests on tvOS Simulator
timeout-minutes: 25
env:
TEST_RUNNER_CI: "true"
run: |
xcodebuild test \
-scheme SwiftVLC \
-destination "platform=tvOS Simulator,name=Apple TV" \
-configuration Debug \
-skipPackagePluginValidation \
-skipMacroValidation \
CODE_SIGN_IDENTITY="" \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGNING_ALLOWED=NO

test:
# `macos-latest` tracks the newest GitHub-provided runner. SwiftVLC
# requires Swift 6.3 (Package.swift tools-version), and the current
Expand Down
62 changes: 62 additions & 0 deletions .github/workflows/vendor-manifest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Guards the vendored libvlc binary against silent per-slice drift.
#
# Every slice of Vendor/libvlc.xcframework has a checked-in manifest of
# its static-archive members (scripts/libvlc-manifests/<slice>.txt). A
# rebuilt libvlc.a that drops or gains plugins/objects in one slice —
# e.g. the tvOS slice intentionally lacks the Chromecast plugin stack,
# and a careless rebuild could reintroduce it (or strip a plugin a
# platform relies on) — passes compilation and most tests unnoticed.
# Diffing the member lists catches that drift at PR time, whenever the
# pinned binary (Package.swift) or the manifests themselves change.
name: Vendor manifest

on:
pull_request:
paths:
- Package.swift
- scripts/libvlc-manifests/**
- scripts/check-libvlc-manifest.sh
- .github/workflows/vendor-manifest.yml
push:
branches: [main]

concurrency:
group: vendor-manifest-${{ github.ref }}
cancel-in-progress: true

jobs:
check:
# setup-dev.sh resolves the package with Xcode's built-in SwiftPM,
# which must understand the manifest's Swift 6.3 tools-version —
# needs Xcode 26.4+, hence macos-26 (same rationale as test.yml).
runs-on: macos-26
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
timeout-minutes: 2

- uses: maxim-lobanov/setup-xcode@v1
timeout-minutes: 5
with:
xcode-version: "26.4"

# The libvlc xcframework dominates the artifact cache.
- name: Cache libvlc xcframework
uses: actions/cache@v5
timeout-minutes: 5
with:
path: Vendor
key: libvlc-xcf-vendor-${{ hashFiles('Package.swift') }}
enableCrossOsArchive: false

# Download the latest released libvlc xcframework into Vendor/
# (setup-dev.sh resolves it via gh release).
- name: Set up Vendor + local Swift package
timeout-minutes: 5
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./scripts/setup-dev.sh

- name: Check libvlc archive-member manifests
timeout-minutes: 5
run: ./scripts/check-libvlc-manifest.sh
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,11 @@ coverage-report/
# IDE
*.swp
*~

# Device-validation harness stream config (operator-supplied, never committed)
Showcase/iOS/ValidationHarness/streams.local.json

# Local working documents (not part of the repo)
CLAUDE.md
PLAN.md
PROGRESS.md
1 change: 1 addition & 0 deletions Fixtures/DynamicHost/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.derived/
20 changes: 20 additions & 0 deletions Fixtures/DynamicHost/App/DynamicHostApp.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import FeatureA
import FeatureB
import SwiftUI

@main
struct DynamicHostApp: App {
private let single: Bool

init() {
let single = FeatureA.instanceID() == FeatureB.instanceID()
print("DYNAMICHOST-SINGLE-INSTANCE: \(single)")
self.single = single
}

var body: some Scene {
WindowGroup {
Text(single ? "Single shared VLCInstance" : "Multiple VLCInstance copies")
}
}
}
Loading
Loading