ci: build, check and publish the music data file on new master data - #1
Merged
Merged
Conversation
.github/workflows/music-data.yml runs `nnnotes music-data --decoded-master` on moenotes-masterdata-sync's decoded master data, the way the story site workflow reads it, and publishes the file for the chart data page into the story site's bucket under music-data/: music-data.json, jackets/, archive/<master version>/<sha256>.json and the build marker build.json. It reuses the story site's triggers (repository_dispatch masterdata-updated, a daily schedule, workflow_dispatch with force and dry_run), its plan/build jobs, secrets, bucket and helpers (story_site.py, apk.sh); no new secret, no master key. plan compares the master data snapshot, the pinned deck commit, the last nnnotes commit and the script's recipe with the published build.json and ends there when nothing changed. build checks the file before anything is uploaded: the JSON Schema, provenance against the snapshot and its manifest, no fewer songs or charts, complete deck statistics and play scenario fields, finite numbers, references and texts, BGM lengths, 0.8 to 2 times the published size, and a smoke test with the chart data page's own modules (MUSIC_DATA_PLAYER_REF, to be set). The jackets and the archive copy go first, music-data.json next, build.json last, each read back by SHA-256. The gate self-test (test_music_data.py) runs in every build and locally in seconds. The workflow needs the fork synced with upstream nnnotes (music-data with the play scenarios and --decoded-master): .github/MUSIC_DATA.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A publishing switch, the repository variable MUSIC_DATA_PUBLISH (unset: off): unless it is `true` every run of music-data.yml, whatever its trigger, is a dry run. It builds, runs every gate and the page smoke test and lists what it would upload; the publish step gets no bucket key, and `music_data.py publish` itself refuses to upload. MUSIC_DATA.md says how to turn it on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A workflow that keeps the chart data page's
music-data.jsoncurrent: on new master data it runsnnnotes music-dataon moenotes-masterdata-sync's decoded master data, checks the file with quality gates and publishes it into the story site's bucket undermusic-data/. It followsstory-site.ymlstep for step. Docs:.github/MUSIC_DATA.md.Published layout (
https://storage.bdon.moe/moenotes/music-data/):music-data.json(no-cache),jackets/<jacket>.webp,archive/<master version>/<sha256>.json(immutable, every published file),build.json(the build marker: SHA-256, counts, inputs, gate results, run). Nothing is deleted.Files (all under
.github/, so the fork still syncs with upstream as before):workflows/music-data.yml: jobsplan→buildscripts/music_data.py:plan,master,build,check(the gates),publishscripts/music_data_smoke.mjs: the page smoke test (ournotes-playerexamples/songs/catalog.js+ranking.jsin Node.js)scripts/songs_page.sh: sparse checkout ofexamples/songsatMUSIC_DATA_PLAYER_REF(nothing built)scripts/test_music_data.py: gate self-test (runs in every build; locally in ~2 s)MUSIC_DATA.mdWhat is reused from the story site
repository_dispatch: masterdata-updated+ daily schedule +workflow_dispatchforce,dry_runconcurrency: story-site, no cancelconcurrency: music-data, no cancelplanjob (cheap) →buildjob only when there is workplancompares master version / resource version / client version / deck commit (rust/Cargo.lock) / last nnnotes commit ofsrc rust pyproject.toml/ recipe with the publishedbuild.json; nothing changed → the run endsvars.X || 'default'env blockSTORY_S3_*variables, plusMUSIC_DATA_*MASTERDATA_BASE_URLdecoded master, SHA-256 againstindex.jsonstory_site.master_index/fetch_table),MasterManifest.jsonincluded; no master keyapk.sh)provenance.clientSTORY_S3_*+ boto3 (story_site.Bucket)MUSIC_DATA_S3_PREFIXmusic-data.json→build.json, each read back by SHA-256uv pip install --system -e .from this checkout.[test](jsonschema, pytest);Swatinem/rust-cache@v2as upstream CI (the install buildsnnnotes._deck)build.json, the publishedmusic-data.jsonas the gates' baseline)Not in the workflow: the Cloudflare Pages preview (no Cloudflare secret).
Gates (any failure: nothing is published)
nnnotes' own cross-checks (the command writes no file otherwise); JSON Schema (
docs/schema/music-data.schema.json); provenance against the snapshot and its manifest (version, every table's SHA-256, every decoded table read, deck commit =rust/Cargo.lock, exporter = installed nnnotes, APK version); songs and charts not fewer than published; deck statistics complete; play scenario fields complete (offSeedsexactly one,rankBonusPercentsfive ints,scorePerfect/rangeWeights/rankCheckon every seed,rangeScorePerfecton every seed range; nullrangeWeightsor kinds are warnings); no NaN / infinity (outside master rows); references and texts; BGM lengths; size 0.8–2× the published file; the page smoke test; SHA-256 read-back after upload.Checked locally (no build was run here): the self-test passes (60 tests, 3.8 s with the real files below);
checkon a real TW file with the scenario fields (master0b21c9f4…, deckd4abb52) against the real decoded snapshot passes every gate; the earlier file without the scenario fields is stopped (scenarios: 2736 failures, page: 3, provenance: deck commit ≠ pin).Settings
Secrets (existing, the story site's; none new):
NNNOTES_BUNDLE_KEY,NNNOTES_BUNDLE_NONCE_SEED,NNNOTES_SERVERS_TW_CDN,PLAYFETCH_CREDENTIALS,STORY_S3_ACCESS_KEY,STORY_S3_SECRET_KEY.Variables:
MUSIC_DATA_PLAYER_REF(required, no default yet), optionalMUSIC_DATA_PLAYER_REPOSITORY(empty-sekai/ournotes-player),MUSIC_DATA_S3_PREFIX(music-data),MUSIC_DATA_MASTERDATA_REGION(hk-tw-mo); shared with the story site:STORY_S3_ENDPOINT,STORY_S3_BUCKET,MASTERDATA_BASE_URL,PLAYFETCH_VERSION,STORY_APK_PACKAGE.Publishing switch
Nothing is uploaded unless the repository variable
MUSIC_DATA_PUBLISHistrue(unset: off). Off, every run (schedule,masterdata-updated,workflow_dispatchwith or withoutdry_run) is a dry run: it builds, runs every gate and the smoke test and lists what it would upload; the publish step gets no bucket key andmusic_data.py publishrefuses to upload by itself. The variable stays unset for now.Preconditions
a03591e, deck feat: chart-stats scenarios: Gekisou off, every rank, the Perfect play empty-sekai/ournotes-deck#1d4abb52): merged.--decoded-masterfeat(music-data): read decoded master data with its manifest (--decoded-master) MetaSekaiLab/nnnotes#6 (12df2a6): merged.12df2a6: chore: sync with upstream nnnotes main (12df2a6) #2 (merge commitfdb7073);Deck modelworkflow disabled in this fork.MUSIC_DATA_PLAYER_REF=0107ae769e8f8ecb938af4084c4487abb6be638e(ournotes-player main): set.MUSIC_DATA_PUBLISH: not set (dry runs only).Generated with Claude Code