release #23
Workflow file for this run
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: release | |
| # Builds the end-user installer and attaches it to a GitHub release. | |
| # | |
| # Only the INSTALLER is built here. The device binaries it embeds (cinder-home, cinder-probe, the | |
| # setuid helpers, the .UPG) are committed under cinder-home/dist/ because building them needs a | |
| # glibc-2.23 + libc++-3.9.0 cross toolchain matched to the player's own runtime — see | |
| # cinder-home/build.sh. Reproducing that on a hosted runner would be a lot of machinery to | |
| # maintain for no gain, so the ARM side is built by a maintainer and committed. | |
| # | |
| # That means: BUILD AND COMMIT dist/ BEFORE TAGGING, or the release ships whatever was last | |
| # committed. The installer's build.rs fails loudly if a payload file is missing, so a stale or | |
| # incomplete dist/ breaks the build here rather than shipping a broken .exe. | |
| on: | |
| push: | |
| tags: ["v*"] | |
| workflow_dispatch: | |
| inputs: | |
| channel: | |
| description: "Build channel to embed" | |
| required: false | |
| default: stable | |
| type: choice | |
| options: [stable, dev] | |
| permissions: | |
| contents: write | |
| env: | |
| CINDER_CHANNEL: ${{ github.event.inputs.channel || 'stable' }} | |
| jobs: | |
| build: | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| # ── the payload must be the one tools/release.sh VERIFIED ──────────────────────────────── | |
| # | |
| # This used to be an existence check, and that was the hole (docs/SHORTCOMINGS.md item 4 / | |
| # D4: the integrity guard is "real, correct, and opt-in"). tools/release.sh rebuilds the | |
| # stable channel from source and refuses to tag unless every committed byte matches — but | |
| # `git tag && git push --tags` reaches this workflow directly without it, and a stale dist/ | |
| # then ships as an installer full of last week's binaries, green tick and all. | |
| # | |
| # The runner cannot repeat that comparison — it needs the glibc-2.23 cross toolchain, which | |
| # is the whole reason dist/ is committed rather than built here. So release.sh leaves a | |
| # manifest of what it verified and this checks the manifest still describes the tree AND was | |
| # written for THIS tag; a tag cut without the script finds one naming the previous version. | |
| # | |
| # Via a SCRIPT, deliberately: the same rule the shellcheck gate is under. A check a | |
| # contributor cannot run locally is one that fails for the first time during a release. | |
| # The tag argument is passed ONLY on a tag push. On a manual workflow_dispatch GITHUB_REF_NAME | |
| # is a BRANCH name, which can never equal the tag the manifest records — so this step failed | |
| # every dispatch run, and the dispatch path exists precisely to build a test .exe without | |
| # cutting a release. With no argument the script still verifies every payload hash; it only | |
| # skips the "verified for THIS tag" assertion, which is meaningless off a tag. | |
| - name: Payload matches the verified manifest | |
| shell: bash | |
| run: | | |
| if [ "${GITHUB_REF_TYPE}" = tag ]; then | |
| tools/verify_payload_manifest.sh "${GITHUB_REF_NAME}" | |
| else | |
| echo "manual run — checking payload hashes, skipping the tag assertion" | |
| tools/verify_payload_manifest.sh | |
| fi | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Test | |
| working-directory: installer | |
| run: cargo test | |
| - name: Build | |
| working-directory: installer | |
| run: cargo build --release | |
| - name: Stage artifact | |
| working-directory: installer | |
| run: | | |
| Copy-Item target/release/cinder-installer.exe cinder-installer-windows-x64.exe | |
| Get-ChildItem cinder-installer-windows-x64.exe | |
| - uses: actions/upload-artifact@v7 | |
| with: | |
| name: cinder-installer-windows-x64.exe | |
| path: installer/cinder-installer-windows-x64.exe | |
| if-no-files-found: error | |
| # The Linux build sends the same vendor SCSI command the Windows build does (SG_IO rather than | |
| # SCSI pass-through), so both finish the install; it needs root, and says so when it does not | |
| # have it (installer/src/console.rs, print_trigger_failed). | |
| # | |
| # It shipped in v0.1.2 and then quietly stopped: the v0.1.3 rework moved to the Sony-updater | |
| # handoff and dropped this job, while the README went on promising a Linux build for six weeks. | |
| # It is back rather than the promise being withdrawn, because ci.yml has been building AND | |
| # testing it on every push the whole time — the only things ever missing were publishing it and | |
| # an honest exit code. | |
| build-linux: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - name: Test | |
| working-directory: installer | |
| run: cargo test --release | |
| - name: Build | |
| working-directory: installer | |
| run: cargo build --release | |
| - name: Stage artifact | |
| working-directory: installer | |
| run: | | |
| cp target/release/cinder-installer cinder-installer-linux-x64 | |
| ls -l cinder-installer-linux-x64 | |
| - uses: actions/upload-artifact@v7 | |
| with: | |
| name: cinder-installer-linux-x64 | |
| path: installer/cinder-installer-linux-x64 | |
| if-no-files-found: error | |
| release: | |
| needs: [build, build-linux] | |
| runs-on: ubuntu-latest | |
| if: startsWith(github.ref, 'refs/tags/') | |
| # Job-level permissions REPLACE the workflow's, so contents: write is restated. id-token lets | |
| # the job ask GitHub's OIDC issuer for the short-lived identity Sigstore signs with; | |
| # attestations lets it store the result against this repository. | |
| permissions: | |
| contents: write | |
| id-token: write | |
| attestations: write | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/download-artifact@v8 | |
| with: | |
| merge-multiple: true | |
| path: dist | |
| - name: List what is being published, with checksums | |
| run: | | |
| cp cinder-home/dist/stable/cinder_home_install.upg dist/cinder-home-install.upg | |
| cp cinder-home/dist/stable/cinder_home_uninstall.upg dist/cinder-home-uninstall.upg | |
| ls -lR dist | |
| # A published binary nobody can verify is a binary nobody should run. | |
| ( cd dist && sha256sum -- * | tee SHA256SUMS ) | |
| # BUILD PROVENANCE (docs/SHORTCOMINGS.md D7). SHA256SUMS proves a download matches what this | |
| # job published; it cannot prove this workflow, from this commit, produced it — anyone able to | |
| # replace a file could replace the sums beside it. An attestation is signed through Sigstore | |
| # with the run's own OIDC identity (repository, workflow file, commit), so it can be checked | |
| # with `gh attestation verify <file> -R superwilso/Cinder` and cannot be minted outside a run. | |
| # | |
| # One step over SHA256SUMS rather than one per build job: every published file, the two | |
| # .UPGs included, is covered by the same list the release body inlines. It says nothing about | |
| # where the committed ARM payload came from — that is D4's manifest, checked above. | |
| - name: Attest build provenance for every published file | |
| uses: actions/attest-build-provenance@v4 | |
| with: | |
| subject-checksums: dist/SHA256SUMS | |
| # The sums are INLINED INTO THE RELEASE PAGE, not merely attached. Verifying a download | |
| # against a checksum file fetched from beside it is close to circular: whoever could swap | |
| # one could swap the other. The release body is the rendered record of what the workflow | |
| # built, so that is where the hashes belong. `SHA256SUMS` stays attached as a copy for | |
| # `sha256sum -c`. | |
| # | |
| # tools/render_release_notes.sh is the same renderer this step runs, so the body can be | |
| # previewed locally instead of being discovered after publication — the rule this repo | |
| # keeps relearning (see tools/shell_check.sh). | |
| - name: Compose the release notes, with the checksums inlined | |
| run: tools/render_release_notes.sh dist/SHA256SUMS RELEASE_BODY.md | |
| - uses: softprops/action-gh-release@v3 | |
| with: | |
| files: dist/* | |
| # NOT a draft. It was, and that was the bug: the workflow ran, built the installer, | |
| # attached them — and left the release invisible until someone opened GitHub and pressed | |
| # Publish. "Automated" has to mean the artefacts are actually reachable at the end. | |
| # A tag with a suffix (v1.2.3-rc1) still ships, but marked pre-release. | |
| draft: false | |
| prerelease: ${{ contains(github.ref_name, '-') }} | |
| generate_release_notes: true | |
| fail_on_unmatched_files: true | |
| body_path: RELEASE_BODY.md |