fix(app): resolve the playing item through the owner (#511) #738
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |