Skip to content

fix(app): resolve the playing item through the owner (#511) #738

fix(app): resolve the playing item through the owner (#511)

fix(app): resolve the playing item through the owner (#511) #738

Workflow file for this run

on:
pull_request:
push:
branches: [main]
name: Continuous Integration
permissions:
contents: read
jobs:
# Workaround for making Github Actions skip based on commit message `[skip ci]`
# Source https://gist.github.com/ybiquitous/c80f15c18319c63cae8447a3be341267
prepare:
runs-on: ubuntu-latest
if: |
!contains(format('{0} {1} {2}', github.event.head_commit.message, github.event.pull_request.title, github.event.pull_request.body), '[skip ci]')
steps:
# Debug output only. Read the payload from $GITHUB_EVENT_PATH instead of
# templating ${{ toJson(github.event) }} into the script: expression
# expansion happens before the shell runs, so attacker-controlled payload
# fields (PR title/body, commit message) could escape the heredoc and
# execute (CodeQL: actions/cache-poisoning/code-injection).
- run: |
echo "github.event_name: $GITHUB_EVENT_NAME"
echo "github.event:"
cat "$GITHUB_EVENT_PATH"
check:
name: Check (${{ matrix.name }})
runs-on: ubuntu-latest
needs: prepare
strategy:
fail-fast: false
matrix:
include:
# Default `cargo run` build.
- name: default
args: "--locked"
# The exact feature set shipped for Linux by cd.yml, so every new
# source subsystem (local-files, subsonic, internet-radio, youtube, qobuz),
# cover-art, and both DJ front doors are compiled and tested in CI.
# Keep this list in sync with cd.yml's `Build release binary` step
# whenever a feature is added there, or the release build becomes the
# first place a break shows up.
- name: all-sources
args: "--locked --no-default-features --features telemetry,tui,streaming,discord-rpc,cover-art,self-update,scripting,mcp-server,ai-dj,audio-viz,mpris,local-files,subsonic,internet-radio,youtube,qobuz"
# `mcp-server` and `ai-dj` are two front doors on the same `dj-core`
# implementation, and each has to build without the other: the
# field-level `#[cfg(feature = "ai-dj")]` on `DjState::setup` exists
# precisely so an MCP-only build still compiles. all-sources turns both
# on, so only a single-door leg can catch one leaking into the other.
# Both are slim, so they cost a fraction of the all-sources leg.
- name: mcp-only
args: "--locked --no-default-features --features telemetry,tui,mcp-server"
- name: ai-dj-only
args: "--locked --no-default-features --features telemetry,tui,ai-dj"
# Slim build used for fast local iteration (matches CLAUDE.md).
- name: slim
args: "--locked --no-default-features --features telemetry,tui"
# Headless: one of two legs without `tui`. `mod tui` is
# feature-gated, so this leg turns any `crate::tui` import from
# core/infra/cli into a compile error, which is what keeps a second
# frontend from silently re-coupling to the terminal one.
- name: headless
args: "--locked --no-default-features --features telemetry"
# Native streaming without the terminal frontend: the combination
# a second frontend or a daemon consumes. Proves the streaming startup
# and the player-event wiring type-check and pass clippy with no
# `crate::tui` in scope.
- name: headless-streaming
args: "--locked --no-default-features --features telemetry,streaming"
env:
RUSTFLAGS: "-C linker-features=-lld"
steps:
- uses: actions/checkout@master
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y -qq pkg-config libssl-dev libxcb1-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libpipewire-0.3-dev libspa-0.2-dev libasound2-dev
- uses: Swatinem/rust-cache@v2
- uses: actions-rs/cargo@v1
with:
command: check
args: ${{ matrix.args }}
test:
name: Test Suite (${{ matrix.name }})
runs-on: ubuntu-latest
needs: prepare
strategy:
fail-fast: false
matrix:
include:
- name: default
args: "--locked"
- name: all-sources
args: "--locked --no-default-features --features telemetry,tui,streaming,discord-rpc,cover-art,self-update,scripting,mcp-server,ai-dj,audio-viz,mpris,local-files,subsonic,internet-radio,youtube,qobuz"
# See the check job for why the two single-door DJ legs exist.
- name: mcp-only
args: "--locked --no-default-features --features telemetry,tui,mcp-server"
- name: ai-dj-only
args: "--locked --no-default-features --features telemetry,tui,ai-dj"
- name: slim
args: "--locked --no-default-features --features telemetry,tui"
# See the check job for why the headless leg exists.
- name: headless
args: "--locked --no-default-features --features telemetry"
env:
RUSTFLAGS: "-C linker-features=-lld"
steps:
- uses: actions/checkout@master
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
# These dependencies are required for `clipboard` and the audio backends.
- run: |
sudo apt-get update
sudo apt-get install -y -qq pkg-config libssl-dev libxcb1-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libpipewire-0.3-dev libspa-0.2-dev libasound2-dev
- uses: Swatinem/rust-cache@v2
- uses: actions-rs/cargo@v1
with:
command: test
args: ${{ matrix.args }}
# Line coverage for Codecov. One leg only: the all-sources feature set, so
# the report covers every subsystem that ships in the Linux release. Keep the
# feature list identical to the all-sources row above. The coverage build
# uses its own instrumentation flags, so it does not share the test job's
# cache. Fork pull requests have no secret and upload tokenless. A failed
# upload never turns the job red: coverage is a report, not a gate.
coverage:
name: Coverage
runs-on: ubuntu-latest
needs: prepare
env:
RUSTFLAGS: "-C linker-features=-lld"
steps:
- uses: actions/checkout@master
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
with:
components: llvm-tools-preview
- run: |
sudo apt-get update
sudo apt-get install -y -qq pkg-config libssl-dev libxcb1-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libpipewire-0.3-dev libspa-0.2-dev libasound2-dev
- uses: Swatinem/rust-cache@v2
- uses: taiki-e/install-action@cargo-llvm-cov
- name: Run tests with coverage
run: cargo llvm-cov --locked --no-default-features --features telemetry,tui,streaming,discord-rpc,cover-art,self-update,scripting,mcp-server,ai-dj,audio-viz,mpris,local-files,subsonic,internet-radio,youtube,qobuz --lcov --output-path lcov.info
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
files: lcov.info
fail_ci_if_error: false
token: ${{ secrets.CODECOV_TOKEN }}
fmt:
name: Rustfmt
runs-on: ubuntu-latest
needs: prepare
steps:
- uses: actions/checkout@master
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
components: rustfmt
- uses: Swatinem/rust-cache@v2
- uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
clippy:
name: Clippy (${{ matrix.name }})
runs-on: ubuntu-latest
needs: prepare
strategy:
fail-fast: false
matrix:
include:
- name: default
args: "--locked"
- name: all-sources
args: "--locked --no-default-features --features telemetry,tui,streaming,discord-rpc,cover-art,self-update,scripting,mcp-server,ai-dj,audio-viz,mpris,local-files,subsonic,internet-radio,youtube,qobuz"
# See the check job for why the two single-door DJ legs exist.
- name: mcp-only
args: "--locked --no-default-features --features telemetry,tui,mcp-server"
- name: ai-dj-only
args: "--locked --no-default-features --features telemetry,tui,ai-dj"
- name: slim
args: "--locked --no-default-features --features telemetry,tui"
# See the check job for why the headless leg exists.
- name: headless
args: "--locked --no-default-features --features telemetry"
# See the check job for why the headless-streaming leg exists.
- name: headless-streaming
args: "--locked --no-default-features --features telemetry,streaming"
env:
RUSTFLAGS: "-C linker-features=-lld"
steps:
- uses: actions/checkout@master
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
components: clippy
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y -qq pkg-config libssl-dev libxcb1-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libpipewire-0.3-dev libspa-0.2-dev libasound2-dev
- uses: Swatinem/rust-cache@v2
- uses: actions-rs/cargo@v1
with:
command: clippy
args: ${{ matrix.args }} -- -D warnings
# macOS is the one platform the seven Linux legs above cannot speak for: it
# picks a different playback backend (portaudio, not alsa/rodio), a different
# OS integration (`macos-media`, not `mpris`), a different visualizer capture
# (`audio-viz-cpal`, not the PipeWire `audio-viz`), and it is the only place
# the `#[cfg(target_os = "macos")]` arms are compiled at all. The decoded
# sources ship there now, so a break in any of that reaches users.
#
# Check and clippy only, like `headless-streaming`: what actually risks
# breaking here is a target-gated arm or a feature that does not exist on
# macOS, and the test suite is platform-independent logic the Linux legs
# already run. The device tests that would exercise the audio path are
# `#[ignore]`d — GitHub runners have no audio output — so a `test` job would
# buy nothing for the extra runner time. One job rather than two so the
# slow part, spinning up a macOS runner, happens once.
#
# Keep the feature list in sync with cd.yml's macOS rows, the same way the
# `all-sources` leg tracks its Linux row.
macos:
name: macOS (check + clippy)
runs-on: macos-latest
needs: prepare
env:
FEATURES: "telemetry,tui,streaming,discord-rpc,cover-art,self-update,scripting,mcp-server,ai-dj,audio-viz-cpal,portaudio-backend,macos-media,local-files,subsonic,internet-radio,youtube,qobuz"
steps:
- uses: actions/checkout@master
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- name: Install system dependencies
run: brew install openssl@3 portaudio
- uses: Swatinem/rust-cache@v2
- name: Check
run: cargo check --locked --no-default-features --features "$FEATURES"
- name: Clippy
run: cargo clippy --locked --no-default-features --features "$FEATURES" -- -D warnings
# The src/gates.rs test pins tools/gates.count to the measured counters; this
# job closes the remaining hole by comparing the file against the merge-base,
# so a PR cannot raise a coupling baseline or lower the test floor alongside
# the code that would need it. Pull requests only: pushes to main have no base
# to ratchet against.
gates-ratchet:
name: Gates ratchet
runs-on: ubuntu-latest
needs: prepare
if: github.event_name == 'pull_request'
steps:
# Pinned to an immutable SHA per review; the pre-existing jobs' @master
# refs are a separate hygiene pass.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- name: Compare tools/gates.count against merge-base
run: |
git fetch origin "${{ github.base_ref }}"
bash tools/check_gates_ratchet.sh FETCH_HEAD