feat: support lyric extension endpoints in mx::api (#394) #503
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
| name: CI | |
| on: | |
| pull_request: | |
| push: | |
| branches: [main] | |
| # Cancel an in-flight run when a newer push supersedes it. The group is keyed | |
| # on the ref, so a new push to a PR cancels that PR's prior run without | |
| # touching other PRs or master. Limited to pull_request events: each master | |
| # push still runs to completion rather than aborting mid-build. | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| # Pin the Docker build-volume name so CI's bind-backed "mx-build" volume | |
| # (created per-job below) is the one the Makefile actually mounts. Without | |
| # this the Makefile computes a CURDIR-hashed name (for worktree isolation) | |
| # that doesn't match the pre-created volume. | |
| env: | |
| DOCKER_VOLUME: mx-build | |
| # Coverage is intentionally absent here -- the instrumented core/api builds are | |
| # expensive and run only on demand from .github/workflows/coverage.yaml (the | |
| # "Run workflow" button or a `/coverage` PR comment). | |
| # | |
| # Every Linux job runs its make targets inside the pinned mx-sdk Docker | |
| # toolchain (ccache baked into the image); the macOS and Windows jobs build | |
| # natively to catch AppleClang and MSVC fallout, kept incremental across runs by | |
| # a content-addressed compiler cache -- ccache on macOS, sccache on Windows. | |
| jobs: | |
| # Cheap up-front diff check so the gen job (generator/audit tests, Go/C | |
| # corert, the ratcheted quality/lint gates, and the gen-quality PR comment) | |
| # only runs -- and only spends a PR comment -- when gen/ actually changed. A | |
| # gen/ change that's md-only (README/AGENTS/DESIGN docs) doesn't move any of | |
| # that, so it's excluded too. | |
| changes: | |
| name: "Detect gen/ changes" | |
| runs-on: ubuntu-latest | |
| outputs: | |
| gen: ${{ steps.filter.outputs.gen }} | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - uses: dorny/paths-filter@v3 | |
| id: filter | |
| with: | |
| # predicate-quantifier 'every' requires a changed file to match ALL | |
| # patterns to count -- i.e. be under gen/ AND not markdown. Without | |
| # it the patterns are OR'd, and the `!gen/**/*.md` rule then matches | |
| # every non-gen-markdown path in the repo, so the filter fired on | |
| # essentially every PR (the gen job ran even for pure Makefile/C++ | |
| # changes). See picomatch negation semantics. | |
| predicate-quantifier: 'every' | |
| filters: | | |
| gen: | |
| - 'gen/**' | |
| - '!gen/**/*.md' | |
| # The deep gate: every C++ suite -- the mx::core substrate (corert + unit) | |
| # and the mx::api product surface (mxtest + examples + corpus round-trip) -- | |
| # built once through the pinned mx-sdk toolchain. This is the one place the | |
| # core suites run, so it is the authoritative C++ correctness check. | |
| test-linux: | |
| name: "linux: test-all" | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v5 | |
| # Container-driver builder (needed to export the gha layer cache) plus | |
| # the runtime-token export so the Makefile auto-detects | |
| # ACTIONS_RUNTIME_TOKEN and pushes/pulls the Docker layer cache. Keeps | |
| # the toolchain-install layer warm across runs. | |
| - uses: docker/setup-buildx-action@v3 | |
| - uses: crazy-max/ghaction-github-runtime@v3 | |
| # Back the mx-build volume with a workspace path so actions/cache can | |
| # persist the ccache dir it holds. Not circular: Docker resolves this | |
| # backing dir host-side, and the volume shadows /workspace/build inside | |
| # the container. | |
| - name: Create bind-backed build volume | |
| run: | | |
| mkdir -p build/docker | |
| docker volume create --driver local \ | |
| --opt type=none --opt o=bind \ | |
| --opt device="$GITHUB_WORKSPACE/build/docker" mx-build | |
| - name: Cache ccache | |
| uses: actions/cache@v4 | |
| with: | |
| path: build/docker/.ccache | |
| key: ccache-${{ runner.os }}-test-${{ github.sha }} | |
| restore-keys: ccache-${{ runner.os }}-test- | |
| # corert file counts are pinned by an in-suite counts case (see CoreRoundtripTest.cpp). | |
| - name: All C++ suites (core roundtrip + unit, api mxtest + examples + roundtrip) | |
| run: make test-all | |
| # mx::api cross-compiled to WebAssembly via the pinned mx-sdk emsdk install | |
| # (#386) -- denigma (rpatters1/denigma) depends on this path staying green. | |
| emscripten: | |
| name: emscripten | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - uses: docker/setup-buildx-action@v3 | |
| - uses: crazy-max/ghaction-github-runtime@v3 | |
| - name: Create bind-backed build volume | |
| run: | | |
| mkdir -p build/docker | |
| docker volume create --driver local \ | |
| --opt type=none --opt o=bind \ | |
| --opt device="$GITHUB_WORKSPACE/build/docker" mx-build | |
| # ccache for mx's own object files; EM_CACHE (below) is Emscripten's | |
| # separate cache for its compiled system libraries. | |
| - name: Cache ccache | |
| uses: actions/cache@v4 | |
| with: | |
| path: build/docker/.ccache | |
| key: ccache-${{ runner.os }}-wasm-${{ github.sha }} | |
| restore-keys: ccache-${{ runner.os }}-wasm- | |
| - name: Cache EM_CACHE | |
| uses: actions/cache@v4 | |
| with: | |
| path: build/docker/.emcache | |
| key: emcache-${{ runner.os }}-wasm-${{ github.sha }} | |
| restore-keys: emcache-${{ runner.os }}-wasm- | |
| - name: mx::api under Node (wasm) | |
| run: make wasm-test | |
| # The api product surface, built natively with AppleClang. macOS is a | |
| # portability check on the product tier only (the core suites run on Linux in | |
| # test-linux); this is the only C++ job off the pinned toolchain, by design, | |
| # to keep finding AppleClang fallout. | |
| # | |
| # Incremental across runs via ccache (content-addressed, like the mx-sdk image | |
| # bakes in for Linux). This replaces an earlier build/-dir cache keyed on | |
| # hashFiles('src/**') -- that key was busted by every source change, so it gave | |
| # nothing on exactly the PRs that recompile. | |
| test-macos: | |
| runs-on: macos-latest | |
| # No Docker on the macOS runners: MX_RUNNING_IN_DOCKER=1 flips the Makefile | |
| # to its direct-invocation branch so the build runs natively. CMake reads the | |
| # *_COMPILER_LAUNCHER env vars as launcher defaults (the Makefile passes no -D | |
| # launcher flag), the same mechanism the mx-sdk image uses for Linux. | |
| env: | |
| MX_RUNNING_IN_DOCKER: 1 | |
| CMAKE_C_COMPILER_LAUNCHER: ccache | |
| CMAKE_CXX_COMPILER_LAUNCHER: ccache | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: ccache | |
| uses: hendrikmuhs/ccache-action@v1.2.18 | |
| with: | |
| key: ${{ runner.os }}-api | |
| max-size: 500M | |
| - name: api suite (mxtest + examples) | |
| run: make api-test | |
| - name: api corpus round-trip (regression mode) | |
| run: make api-roundtrip | |
| # The api product surface, built natively with the real MSVC toolchain to keep | |
| # finding Windows portability fallout. Windows has no make/Unix tools, so this | |
| # job drives CMake directly. | |
| # | |
| # Generator is Ninja, not the Visual Studio generator: the VS generator ignores | |
| # CMAKE_<LANG>_COMPILER_LAUNCHER, so sccache (the MSVC-capable compiler cache) | |
| # can't hook in under it. Ninja honors the launcher and is faster besides. | |
| # ilammy/msvc-dev-cmd puts cl.exe + the VS-bundled ninja on PATH via vcvars. | |
| # Debug info is forced to Embedded (/Z7, not the default /Zi): sccache can't | |
| # cache the shared-PDB writes /Zi makes, so /Z7 is what lets the object cache | |
| # actually hit. Ninja is single-config, so binaries land in build/api/ (no | |
| # Debug/ subdir). | |
| test-windows: | |
| runs-on: windows-latest | |
| # Git Bash so multi-command run steps stop on first failure (set -e) and | |
| # forward-slash paths + .exe invocation work uniformly. | |
| defaults: | |
| run: | |
| shell: bash | |
| steps: | |
| - uses: actions/checkout@v5 | |
| # Puts cl.exe, link.exe, and the VS-bundled ninja on PATH -- exported to | |
| # $GITHUB_ENV, so the later bash steps inherit the MSVC environment. | |
| - uses: ilammy/msvc-dev-cmd@v1 | |
| - name: sccache | |
| uses: hendrikmuhs/ccache-action@v1.2.18 | |
| with: | |
| variant: sccache | |
| key: ${{ runner.os }}-msvc | |
| max-size: 1G | |
| - name: Configure (MSVC, Ninja, MX_API) | |
| run: >- | |
| cmake -S . -B build/api -G Ninja | |
| -DCMAKE_BUILD_TYPE=Debug | |
| -DMX_API=on | |
| -DCMAKE_C_COMPILER=cl | |
| -DCMAKE_CXX_COMPILER=cl | |
| -DCMAKE_C_COMPILER_LAUNCHER=sccache | |
| -DCMAKE_CXX_COMPILER_LAUNCHER=sccache | |
| -DCMAKE_POLICY_DEFAULT_CMP0141=NEW | |
| -DCMAKE_MSVC_DEBUG_INFORMATION_FORMAT=Embedded | |
| - name: Build | |
| run: >- | |
| cmake --build build/api | |
| --target mx mxtest mxtest-api-roundtrip mxread mxwrite mxhide | |
| --parallel | |
| - name: api examples | |
| run: | | |
| build/api/mxread.exe | |
| build/api/mxwrite.exe build/api/example.musicxml | |
| build/api/mxhide.exe | |
| - name: mxtest suite (api/impl/file/control) | |
| run: build/api/mxtest.exe | |
| - name: api corpus round-trip (regression mode) | |
| run: >- | |
| build/api/mxtest-api-roundtrip.exe regression | |
| data src/private/mxtest/api/roundtrip-baseline.txt | |
| # Proves mx compiles as a Swift package under AppleClang/SPM on every PR -- | |
| # the path komp takes when it depends on a sibling mx via `.package(path:)`. | |
| # Runs no test suites; the binary xcframework release arm is follow-up work. | |
| # | |
| # SwiftPM has no compiler-launcher hook, so incremental builds come from caching | |
| # its .build tree: llbuild tracks inputs by content signature, so a restored | |
| # tree rebuilds only what changed. The per-commit key suffix makes each run save | |
| # an updated tree while restore-keys pulls the newest prior one. | |
| swift: | |
| runs-on: macos-latest | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Cache SwiftPM build | |
| uses: actions/cache@v4 | |
| with: | |
| path: .build | |
| key: ${{ runner.os }}-spm-${{ hashFiles('Package.swift', 'Package.resolved') }}-${{ github.sha }} | |
| restore-keys: | | |
| ${{ runner.os }}-spm-${{ hashFiles('Package.swift', 'Package.resolved') }}- | |
| ${{ runner.os }}-spm- | |
| - name: Swift toolchain | |
| run: swift --version | |
| - name: Build the Mx Swift package (local-checkout source mode) | |
| run: swift build --product Mx | |
| # Repo-wide, compiles no C++: the clang-format gate plus a generated-output | |
| # drift check (regenerate every target, then fail on any tracked-file diff). | |
| # Always runs -- both apply to the whole repo, not just gen/. | |
| quality: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - uses: docker/setup-buildx-action@v3 | |
| - uses: crazy-max/ghaction-github-runtime@v3 | |
| - name: Create bind-backed build volume | |
| run: | | |
| mkdir -p build/docker | |
| docker volume create --driver local \ | |
| --opt type=none --opt o=bind \ | |
| --opt device="$GITHUB_WORKSPACE/build/docker" mx-build | |
| - name: Cache ccache | |
| uses: actions/cache@v4 | |
| with: | |
| path: build/docker/.ccache | |
| key: ccache-${{ runner.os }}-quality-${{ github.sha }} | |
| restore-keys: ccache-${{ runner.os }}-quality- | |
| - name: fmt-check (clang-format, all C++ under src/) | |
| run: make fmt-check | |
| # The committed generated output must match the generator: regenerate | |
| # every target, then any tracked-file drift fails the build. | |
| - name: Regenerate all targets | |
| run: make gen | |
| - name: Generated output drift check | |
| run: git diff --exit-code | |
| # Generator + audit gates, gated on gen/ changes (see the `changes` job): the | |
| # generator/audit Python tests, the Go and C corert suites (they exercise the | |
| # generator's emitted targets), and the ratcheted quality/lint gates. Also | |
| # stages the gen-quality report as a PR comment, posted by pr-comment.yaml. | |
| gen: | |
| runs-on: ubuntu-latest | |
| needs: changes | |
| if: needs.changes.outputs.gen == 'true' | |
| # Read-only token: this job builds untrusted fork PR code, so it never | |
| # requests write. The gen-quality comment is posted by the pr-comment.yaml | |
| # companion workflow (workflow_run) from the base-repo context instead. | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - uses: docker/setup-buildx-action@v3 | |
| - uses: crazy-max/ghaction-github-runtime@v3 | |
| - name: Create bind-backed build volume | |
| run: | | |
| mkdir -p build/docker | |
| docker volume create --driver local \ | |
| --opt type=none --opt o=bind \ | |
| --opt device="$GITHUB_WORKSPACE/build/docker" mx-build | |
| - name: Cache ccache | |
| uses: actions/cache@v4 | |
| with: | |
| path: build/docker/.ccache | |
| key: ccache-${{ runner.os }}-gen-${{ github.sha }} | |
| restore-keys: ccache-${{ runner.os }}-gen- | |
| - name: gen unit tests (parser/IR/plates/press) | |
| run: make gen-test | |
| - name: audit tool unit tests (incl. failure classifier) | |
| run: make audit-test | |
| - name: Go corert suite | |
| run: make test-go | |
| - name: C corert suite | |
| run: make test-c | |
| # Static-analysis gates on the gen/ generator. Each fails the build below | |
| # its floor (GEN_QUALITY_FLOOR / GEN_LINT_FLOOR in the Makefile). | |
| - name: gen-quality gate | |
| run: make gen-quality | |
| - name: gen-lint gate | |
| run: make gen-lint | |
| # Surface the score in the run summary. always() so a gate failure still | |
| # reports why. | |
| - name: gen-quality report | |
| if: always() | |
| run: | | |
| { | |
| echo '### gen-quality `gen/`' | |
| echo '' | |
| cat data/testOutput/gen-quality/report.md 2>/dev/null || echo '(no report produced)' | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| # Fork PRs run with a read-only token, so this job can't post the comment | |
| # itself. Stage the body + PR number as an artifact; pr-comment.yaml posts | |
| # it after CI completes. always() so a gate failure still reports why. | |
| # Guarded on the report file existing so the comment-sending job gets no | |
| # artifact -- and gracefully posts nothing -- if gen-quality crashed | |
| # before writing it. | |
| - name: Stage gen-quality PR comment | |
| if: always() && github.event_name == 'pull_request' | |
| run: | | |
| if [ ! -f data/testOutput/gen-quality/report.md ]; then | |
| echo 'No gen-quality report to stage (gate produced nothing).' | |
| exit 0 | |
| fi | |
| mkdir -p pr-comment | |
| { | |
| echo '### gen-quality `gen/`' | |
| echo '' | |
| cat data/testOutput/gen-quality/report.md | |
| echo '' | |
| echo 'Commit `${{ github.sha }}`.' | |
| } > pr-comment/body.md | |
| echo '${{ github.event.pull_request.number }}' > pr-comment/number | |
| - name: Upload gen-quality PR comment | |
| if: always() && github.event_name == 'pull_request' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: pr-comment-gen | |
| path: pr-comment/ | |
| if-no-files-found: ignore |