fix(build): make the release actually buildable, and check it before tagging
🏗️ Build and Test JellyTau / Run Tests (pull_request) Successful in 18m41s
🏗️ Build and Test JellyTau / Supply Chain (pull_request) Successful in 31s
Traceability Validation / Check Requirement Traces (pull_request) Successful in 9s
🏗️ Build and Test JellyTau / Android Compile Check (pull_request) Successful in 4m5s
🏗️ Build and Test JellyTau / Run Tests (pull_request) Successful in 18m41s
🏗️ Build and Test JellyTau / Supply Chain (pull_request) Successful in 31s
Traceability Validation / Check Requirement Traces (pull_request) Successful in 9s
🏗️ Build and Test JellyTau / Android Compile Check (pull_request) Successful in 4m5s
Preparing v0.10.0 meant building the release locally first. It did not build. Two separate defects were sitting on master, both invisible to every gate this project has, for the same reason: nothing in build-and-test.yml runs `tauri build`. Only a tag does. So the first time anyone would have discovered either was a failed release. **Tauri plugin versions had drifted apart.** Tauri refuses to build when a plugin's Rust crate and npm package are on different minor versions: tauri-plugin-log (v2.8.0) : @tauri-apps/plugin-log (v2.9.0) tauri-plugin-updater (v2.9.0) : @tauri-apps/plugin-updater (v2.10.1) Introduced by the updater and diagnostics work in this same branch -- `cargo add` took what the pinned toolchain allowed while `bun add` took latest, and the caret ranges let them separate. cargo check, clippy, cargo test and svelte-check all passed. Matching upward pulled wry 0.53.5 -> 0.54.2 along with wasm-bindgen, web-sys and webkit2gtk: the webview layer, which on Linux is the video playback path. That is not a change to make while cutting a release, so the npm packages are pinned down to the crates instead -- exactly, not by caret, since the caret is what allowed the drift. The upgrade is worth doing deliberately, with a playback check, and ci-operations.md says so. CI now runs `tauri info`, which performs the same comparison without building. Verified by reintroducing the mismatch and watching it fail. **The AppImage target had never been built.** It was added earlier in this branch because the release notes had advertised an AppImage for months while tauri.conf.json never produced one. It does not work out of the box: linuxdeploy carries its own `strip`, too old to parse the .relr.dyn section modern toolchains emit, and it fails on every bundled library -- strip: libzstd.so.1: unknown type [0x13] section `.relr.dyn' failed to bundle project `failed to run linuxdeploy` Ubuntu 23.10+ links with -z pack-relative-relocs by default, so the CI builder image fails exactly as a modern Arch host does. NO_STRIP=true is linuxdeploy's documented escape hatch. The resulting 153 MB AppImage was verified to be well-formed and to actually start. Without this the release would have failed at the Linux build step -- the artifact check added earlier refuses to publish when no AppImage is produced, which is the behaviour we want, but it would have refused a tagged build rather than a local one. Also: the traceability extractor now reads the tooling shell scripts that carry TRACES comments. DR-207, DR-213 and DR-220 all had them and were counted as uncovered because only .ts/.svelte/.rs were scanned. Listed individually rather than globbing scripts/*.sh -- most implement nothing, and adding one should be a decision. DR-221.
This commit is contained in:
@@ -107,6 +107,28 @@ jobs:
|
|||||||
bunx svelte-kit sync
|
bunx svelte-kit sync
|
||||||
bun run check
|
bun run check
|
||||||
|
|
||||||
|
# Tauri refuses to build when a plugin's Rust crate and npm package are on
|
||||||
|
# different minor versions. Nothing here runs `tauri build` -- that only
|
||||||
|
# happens on a tag -- so a mismatch introduced on master stayed invisible
|
||||||
|
# until the release build, which is where it was found: v0.10.0 prep hit
|
||||||
|
# `tauri-plugin-log (v2.8.0) : @tauri-apps/plugin-log (v2.9.0)`. `cargo
|
||||||
|
# check`, clippy, the tests and svelte-check had all passed.
|
||||||
|
#
|
||||||
|
# `tauri info` performs the same comparison the bundler does, without a
|
||||||
|
# build. Grepping its output is crude, but the alternative is discovering
|
||||||
|
# this at tag time again.
|
||||||
|
- name: Check Tauri plugin versions match
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
if bunx tauri info 2>&1 | tee /tmp/tauri-info.txt | grep -q "version mismatched"; then
|
||||||
|
echo "::error::A Tauri plugin's Rust crate and npm package versions disagree."
|
||||||
|
echo "::error::The release build will refuse to start. Align them in"
|
||||||
|
echo "::error::src-tauri/Cargo.toml and package.json (both are pinned exactly)."
|
||||||
|
grep -A6 "version mismatched" /tmp/tauri-info.txt || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "✅ Tauri plugin crate/package versions agree."
|
||||||
|
|
||||||
# Coverage rather than a bare `bun run test`: same suite, plus the
|
# Coverage rather than a bare `bun run test`: same suite, plus the
|
||||||
# thresholds in vitest.config.ts, so a large untested module or a deleted
|
# thresholds in vitest.config.ts, so a large untested module or a deleted
|
||||||
# test fails here instead of being noticed months later.
|
# test fails here instead of being noticed months later.
|
||||||
|
|||||||
@@ -158,6 +158,17 @@ jobs:
|
|||||||
- name: Build for Linux
|
- name: Build for Linux
|
||||||
run: bun run tauri build
|
run: bun run tauri build
|
||||||
env:
|
env:
|
||||||
|
# linuxdeploy's bundled `strip` cannot parse the `.relr.dyn` section
|
||||||
|
# modern toolchains emit, and fails on every bundled library:
|
||||||
|
# strip: libzstd.so.1: unknown type [0x13] section `.relr.dyn'
|
||||||
|
# failed to bundle project `failed to run linuxdeploy`
|
||||||
|
# Ubuntu 23.10+ links with -z pack-relative-relocs by default, so this
|
||||||
|
# image hits it. Skipping strip is linuxdeploy's documented escape
|
||||||
|
# hatch; the cost is a larger AppImage. Found by building the target
|
||||||
|
# locally before tagging -- nothing in CI builds the app, so a release
|
||||||
|
# would have been the first time anyone discovered the AppImage target
|
||||||
|
# does not work.
|
||||||
|
NO_STRIP: "true"
|
||||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,11 @@
|
|||||||
"name": "jellytau",
|
"name": "jellytau",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tauri-apps/api": "^2",
|
"@tauri-apps/api": "^2",
|
||||||
"@tauri-apps/plugin-log": "^2.9.0",
|
"@tauri-apps/plugin-log": "2.8.0",
|
||||||
"@tauri-apps/plugin-opener": "^2",
|
"@tauri-apps/plugin-opener": "^2",
|
||||||
"@tauri-apps/plugin-os": "^2.3.2",
|
"@tauri-apps/plugin-os": "^2.3.2",
|
||||||
"@tauri-apps/plugin-process": "^2.3.1",
|
"@tauri-apps/plugin-process": "^2.3.1",
|
||||||
"@tauri-apps/plugin-updater": "^2.10.1",
|
"@tauri-apps/plugin-updater": "2.9.0",
|
||||||
"hls.js": "^1.6.15",
|
"hls.js": "^1.6.15",
|
||||||
"svelte-dnd-action": "^0.9.69",
|
"svelte-dnd-action": "^0.9.69",
|
||||||
},
|
},
|
||||||
@@ -281,7 +281,7 @@
|
|||||||
|
|
||||||
"@tauri-apps/cli-win32-x64-msvc": ["@tauri-apps/cli-win32-x64-msvc@2.9.6", "", { "os": "win32", "cpu": "x64" }, "sha512-ldWuWSSkWbKOPjQMJoYVj9wLHcOniv7diyI5UAJ4XsBdtaFB0pKHQsqw/ItUma0VXGC7vB4E9fZjivmxur60aw=="],
|
"@tauri-apps/cli-win32-x64-msvc": ["@tauri-apps/cli-win32-x64-msvc@2.9.6", "", { "os": "win32", "cpu": "x64" }, "sha512-ldWuWSSkWbKOPjQMJoYVj9wLHcOniv7diyI5UAJ4XsBdtaFB0pKHQsqw/ItUma0VXGC7vB4E9fZjivmxur60aw=="],
|
||||||
|
|
||||||
"@tauri-apps/plugin-log": ["@tauri-apps/plugin-log@2.9.0", "", { "dependencies": { "@tauri-apps/api": "^2.11.0" } }, "sha512-Ql8okrnsguk0eDq1GvRfttFV5KaeW/7vcao6bdbkXCRJ1+2sWE15ZJvJVEKVANrOKy1mRngqC3IFIAP+wP5qSw=="],
|
"@tauri-apps/plugin-log": ["@tauri-apps/plugin-log@2.8.0", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-a+7rOq3MJwpTOLLKbL8d0qGZ85hgHw5pNOWusA9o3cf7cEgtYHiGY/+O8fj8MvywQIGqFv0da2bYQDlrqLE7rw=="],
|
||||||
|
|
||||||
"@tauri-apps/plugin-opener": ["@tauri-apps/plugin-opener@2.5.2", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-ei/yRRoCklWHImwpCcDK3VhNXx+QXM9793aQ64YxpqVF0BDuuIlXhZgiAkc15wnPVav+IbkYhmDJIv5R326Mew=="],
|
"@tauri-apps/plugin-opener": ["@tauri-apps/plugin-opener@2.5.2", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-ei/yRRoCklWHImwpCcDK3VhNXx+QXM9793aQ64YxpqVF0BDuuIlXhZgiAkc15wnPVav+IbkYhmDJIv5R326Mew=="],
|
||||||
|
|
||||||
@@ -289,7 +289,7 @@
|
|||||||
|
|
||||||
"@tauri-apps/plugin-process": ["@tauri-apps/plugin-process@2.3.1", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-nCa4fGVaDL/B9ai03VyPOjfAHRHSBz5v6F/ObsB73r/dA3MHHhZtldaDMIc0V/pnUw9ehzr2iEG+XkSEyC0JJA=="],
|
"@tauri-apps/plugin-process": ["@tauri-apps/plugin-process@2.3.1", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-nCa4fGVaDL/B9ai03VyPOjfAHRHSBz5v6F/ObsB73r/dA3MHHhZtldaDMIc0V/pnUw9ehzr2iEG+XkSEyC0JJA=="],
|
||||||
|
|
||||||
"@tauri-apps/plugin-updater": ["@tauri-apps/plugin-updater@2.10.1", "", { "dependencies": { "@tauri-apps/api": "^2.10.1" } }, "sha512-NFYMg+tWOZPJdzE/PpFj2qfqwAWwNS3kXrb1tm1gnBJ9mYzZ4WDRrwy8udzWoAnfGCHLuePNLY1WVCNHnh3eRA=="],
|
"@tauri-apps/plugin-updater": ["@tauri-apps/plugin-updater@2.9.0", "", { "dependencies": { "@tauri-apps/api": "^2.6.0" } }, "sha512-j++sgY8XpeDvzImTrzWA08OqqGqgkNyxczLD7FjNJJx/uXxMZFz5nDcfkyoI/rCjYuj2101Tci/r/HFmOmoxCg=="],
|
||||||
|
|
||||||
"@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="],
|
"@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="],
|
||||||
|
|
||||||
|
|||||||
Vendored
+23
@@ -80,6 +80,29 @@ docker run --rm gitea.tourolle.paris/dtourolle/jellytau-builder:2026.09 \
|
|||||||
toolchain inside the job — a toolchain install in CI. Bump both, rebuild, push,
|
toolchain inside the job — a toolchain install in CI. Bump both, rebuild, push,
|
||||||
then merge.
|
then merge.
|
||||||
|
|
||||||
|
## Tauri plugin versions are pinned in pairs
|
||||||
|
|
||||||
|
Every Tauri plugin exists twice: a Rust crate in `src-tauri/Cargo.toml` and an
|
||||||
|
npm package in `package.json`. **The Tauri CLI refuses to build when the two are
|
||||||
|
on different minor versions** — not a warning, a hard stop before compilation.
|
||||||
|
|
||||||
|
Both sides are therefore pinned *exactly* (`"2.8.0"`, not `"^2.8.0"`). A caret
|
||||||
|
range is what let them drift apart in the first place: `bun add` took the latest
|
||||||
|
npm package while cargo held an older crate, and nothing noticed until a release
|
||||||
|
build refused to start.
|
||||||
|
|
||||||
|
Nothing in `build-and-test.yml` runs `tauri build` — that happens only on a tag —
|
||||||
|
so this class of breakage used to be invisible until release day. The
|
||||||
|
`Check Tauri plugin versions match` step runs `tauri info`, which performs the
|
||||||
|
same comparison without building.
|
||||||
|
|
||||||
|
To upgrade a plugin, move **both** sides together and re-run that step. Expect
|
||||||
|
the Rust side to be the constraint: a newer plugin crate may pull a large
|
||||||
|
transitive upgrade (bumping `tauri-plugin-log` to 2.9.0 also moved `wry`,
|
||||||
|
`wasm-bindgen`, `web-sys` and `webkit2gtk`), which touches the webview and
|
||||||
|
therefore video playback. That is a change to make deliberately, with a full
|
||||||
|
build and a playback check — not one to slip into a release.
|
||||||
|
|
||||||
## Secrets
|
## Secrets
|
||||||
|
|
||||||
Managed with the `tea` CLI (`tea actions secrets list`) or the repo settings UI.
|
Managed with the `tea` CLI (`tea actions secrets list`) or the repo settings UI.
|
||||||
|
|||||||
@@ -412,6 +412,7 @@ Internal architecture, components, and application logic.
|
|||||||
| DR-218 | Persistent, redacted logging and a diagnostics export. `tauri-plugin-log` replaces the `env_logger` stdout-only init, giving a rotating 5 MB file, a webview target in dev, and — the single largest gain — logcat on Android, where `env_logger`'s stdout went nowhere. **Redaction runs in the log formatter, not at export**: a credential in a file on the device is already a disclosure, so stripping it on the way out would be too late; the exporter redacts a second time to cover files written by older builds. `api_key`/`X-Emby-Token`/`Authorization`/`"AccessToken"`/`Token="…"` all reduce to `[REDACTED]` while host, item ids and filenames are deliberately kept — a bundle scrubbed of those is one nobody can debug from. The server URL is reduced to scheme and host, dropping any embedded `user:pass@`. The panic hook chains to the previous hook rather than replacing it, because `utils/lock.rs` installs a silencing hook around tests that provoke poisoned locks on purpose. The chosen level persists to disk and is re-applied at startup, since reproducing a bug usually means restarting into it. The frontend facade keeps its untouched `console.*` pass-through (DR-204) and additionally forwards a stringified copy at info and above, so one file holds both halves of the app in order — which is what makes a race between them legible after the fact | Tooling | UR-078 | Done |
|
| DR-218 | Persistent, redacted logging and a diagnostics export. `tauri-plugin-log` replaces the `env_logger` stdout-only init, giving a rotating 5 MB file, a webview target in dev, and — the single largest gain — logcat on Android, where `env_logger`'s stdout went nowhere. **Redaction runs in the log formatter, not at export**: a credential in a file on the device is already a disclosure, so stripping it on the way out would be too late; the exporter redacts a second time to cover files written by older builds. `api_key`/`X-Emby-Token`/`Authorization`/`"AccessToken"`/`Token="…"` all reduce to `[REDACTED]` while host, item ids and filenames are deliberately kept — a bundle scrubbed of those is one nobody can debug from. The server URL is reduced to scheme and host, dropping any embedded `user:pass@`. The panic hook chains to the previous hook rather than replacing it, because `utils/lock.rs` installs a silencing hook around tests that provoke poisoned locks on purpose. The chosen level persists to disk and is re-applied at startup, since reproducing a bug usually means restarting into it. The frontend facade keeps its untouched `console.*` pass-through (DR-204) and additionally forwards a stringified copy at info and above, so one file holds both halves of the app in order — which is what makes a race between them legible after the fact | Tooling | UR-078 | Done |
|
||||||
| DR-219 | Release notes are the reviewed CHANGELOG entry, not a generated draft. Every release from v0.0.1 to v0.9.1 published the same ~1,050 bytes of generic install instructions whose "What's New" section said "See CHANGELOG.md" — a link that does not resolve from a release page. Thirty-five releases, byte-identical, telling a reader nothing about what changed. The workflow now publishes the `## <version>` section of CHANGELOG.md and fails the release if that section is absent, since notes that say nothing are worse than a build that waits for two sentences. `release:notes` is printed into the job log as a drafting aid but is deliberately *not* published: CLAUDE.md calls its output "a reviewed draft, not a final changelog", and publishing it unreviewed proved why — a range containing a repo-wide formatting sweep resolved to nearly the entire requirement matrix and produced notes claiming one release had added the whole application. The script now skips cosmetic commits (`chore(format)`, `chore(deps)`, `style`) when deriving a range's files, and says how many it skipped rather than silently reporting a smaller set | Tooling | - | Done |
|
| DR-219 | Release notes are the reviewed CHANGELOG entry, not a generated draft. Every release from v0.0.1 to v0.9.1 published the same ~1,050 bytes of generic install instructions whose "What's New" section said "See CHANGELOG.md" — a link that does not resolve from a release page. Thirty-five releases, byte-identical, telling a reader nothing about what changed. The workflow now publishes the `## <version>` section of CHANGELOG.md and fails the release if that section is absent, since notes that say nothing are worse than a build that waits for two sentences. `release:notes` is printed into the job log as a drafting aid but is deliberately *not* published: CLAUDE.md calls its output "a reviewed draft, not a final changelog", and publishing it unreviewed proved why — a range containing a repo-wide formatting sweep resolved to nearly the entire requirement matrix and produced notes claiming one release had added the whole application. The script now skips cosmetic commits (`chore(format)`, `chore(deps)`, `style`) when deriving a range's files, and says how many it skipped rather than silently reporting a smaller set | Tooling | - | Done |
|
||||||
| DR-220 | A release ships only its own artifacts. `src-tauri/target/*/release/bundle/` is not versioned, cargo never cleans it, and the CI runner reuses the target directory — so the copy step's `bundle/**/*-setup.exe` glob collected every installer ever built there. Every release from v0.1.0 to v0.8.2 shipped its predecessors': sixteen Windows installers on v0.8.2, thirteen of them stale, and a download list on v0.5.0 reaching back to 0.1.0. It went unnoticed for eight months because there was nothing to notice — the upload loop reported success, the files were real, and the page looked busy rather than wrong. It stopped only when an unrelated cache change wiped the runner's target dir, leaving the defect dormant rather than fixed. Both desktop builds now clear the bundle directory first, so a stale file cannot exist to be copied — filtering the copy by version would have hidden it instead. `scripts/check-release-artifacts.sh` is the backstop for the next route nobody predicts: it runs before the SBOM, the checksums and the upload, and refuses to publish when any artifact's embedded version disagrees with the tag | Tooling | - | Done |
|
| DR-220 | A release ships only its own artifacts. `src-tauri/target/*/release/bundle/` is not versioned, cargo never cleans it, and the CI runner reuses the target directory — so the copy step's `bundle/**/*-setup.exe` glob collected every installer ever built there. Every release from v0.1.0 to v0.8.2 shipped its predecessors': sixteen Windows installers on v0.8.2, thirteen of them stale, and a download list on v0.5.0 reaching back to 0.1.0. It went unnoticed for eight months because there was nothing to notice — the upload loop reported success, the files were real, and the page looked busy rather than wrong. It stopped only when an unrelated cache change wiped the runner's target dir, leaving the defect dormant rather than fixed. Both desktop builds now clear the bundle directory first, so a stale file cannot exist to be copied — filtering the copy by version would have hidden it instead. `scripts/check-release-artifacts.sh` is the backstop for the next route nobody predicts: it runs before the SBOM, the checksums and the upload, and refuses to publish when any artifact's embedded version disagrees with the tag | Tooling | - | Done |
|
||||||
|
| DR-221 | The release path is exercised before a tag exists. Nothing in `build-and-test.yml` runs `tauri build` — only a tag does — so a whole class of breakage was invisible until release day, and two instances of it were sitting on master at once. Tauri refuses to build when a plugin's Rust crate and npm package differ by minor version, which the updater and logging work had introduced (`tauri-plugin-log 2.8.0` against `@tauri-apps/plugin-log 2.9.0`) while `cargo check`, clippy, the tests and `svelte-check` all passed; both sides are now pinned exactly rather than by caret, since a caret is what let them separate, and CI runs `tauri info` to compare them without building. The AppImage target had never once been built: linuxdeploy carries a `strip` too old to parse the `.relr.dyn` section modern toolchains emit, so bundling failed on every library — and Ubuntu 23.10+ links with `-z pack-relative-relocs` by default, so the builder image fails the same way a modern Arch host does. `NO_STRIP=true` is linuxdeploy's documented escape hatch; the cost is a larger, unstripped bundle. Both were found by building the target locally before tagging rather than by publishing a release that could not build | Tooling | - | Done |
|
||||||
| DR-198 | The webview runs under a real Content-Security-Policy, and the asset protocol is scoped to the one directory it still serves. `csp` was `null`, which disables CSP entirely: any script that reached the web layer — through a future `{@html}`, a dependency, or a devtools paste — would have inherited the whole IPC surface, and with it the user's session. `script-src 'self'` (Tauri injects a nonce for SvelteKit's inline bootstrap script at build time, so no `'unsafe-inline'` is needed) plus `object-src`/`frame-src 'none'` and `base-uri 'self'` is the part that is genuinely restrictive. `img-src`/`media-src`/`connect-src` cannot be: the Jellyfin origin is typed in by the user at run time and is commonly plain `http` on a LAN, so they allow `http:`/`https:` — a wide grant for *data*, but one that still bars `file:`, `filesystem:` and scripting schemes, and leaves `script-src` untouched. `style-src` keeps `'unsafe-inline'` because Svelte compiles `style="…"` attributes (including `app.html`'s `display: contents` wrapper) into markup; this is safe only while no `<style>` element survives into `index.html`, since a nonce there would make Tauri's injection outrank — and therefore void — `'unsafe-inline'`. `worker-src blob:` and `media-src blob:` are hls.js: it demuxes in a worker built from a blob and attaches MSE through `URL.createObjectURL`. `asset:` and `http://asset.localhost` are the same protocol under the two naming schemes `convertFileSrc` emits (custom scheme on Linux/macOS, `http` host on Windows/Android); `ipc:`/`http://ipc.localhost` is the invoke transport, which would otherwise be blocked by `connect-src`. A run-time CSP naming the server origin exactly was rejected: Tauri computes the header from immutable config when it serves the HTML, so it would mean rebuilding config and reloading the webview on every server change, for a policy the user can already point anywhere. The asset-protocol scope narrows from `$APPDATA/**` to `$APPDATA/thumbnails/**` — since DR-137 moved downloaded media to the loopback server, `imageCache` is the only `convertFileSrc` caller left, so the database and the encrypted-token fallback file no longer sit inside the grant | Security | UR-012, UR-071 | Done |
|
| DR-198 | The webview runs under a real Content-Security-Policy, and the asset protocol is scoped to the one directory it still serves. `csp` was `null`, which disables CSP entirely: any script that reached the web layer — through a future `{@html}`, a dependency, or a devtools paste — would have inherited the whole IPC surface, and with it the user's session. `script-src 'self'` (Tauri injects a nonce for SvelteKit's inline bootstrap script at build time, so no `'unsafe-inline'` is needed) plus `object-src`/`frame-src 'none'` and `base-uri 'self'` is the part that is genuinely restrictive. `img-src`/`media-src`/`connect-src` cannot be: the Jellyfin origin is typed in by the user at run time and is commonly plain `http` on a LAN, so they allow `http:`/`https:` — a wide grant for *data*, but one that still bars `file:`, `filesystem:` and scripting schemes, and leaves `script-src` untouched. `style-src` keeps `'unsafe-inline'` because Svelte compiles `style="…"` attributes (including `app.html`'s `display: contents` wrapper) into markup; this is safe only while no `<style>` element survives into `index.html`, since a nonce there would make Tauri's injection outrank — and therefore void — `'unsafe-inline'`. `worker-src blob:` and `media-src blob:` are hls.js: it demuxes in a worker built from a blob and attaches MSE through `URL.createObjectURL`. `asset:` and `http://asset.localhost` are the same protocol under the two naming schemes `convertFileSrc` emits (custom scheme on Linux/macOS, `http` host on Windows/Android); `ipc:`/`http://ipc.localhost` is the invoke transport, which would otherwise be blocked by `connect-src`. A run-time CSP naming the server origin exactly was rejected: Tauri computes the header from immutable config when it serves the HTML, so it would mean rebuilding config and reloading the webview on every server change, for a policy the user can already point anywhere. The asset-protocol scope narrows from `$APPDATA/**` to `$APPDATA/thumbnails/**` — since DR-137 moved downloaded media to the loopback server, `imageCache` is the only `convertFileSrc` caller left, so the database and the encrypted-token fallback file no longer sit inside the grant | Security | UR-012, UR-071 | Done |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ know how something *works*, read
|
|||||||
|
|
||||||
**Next free requirement ids** (always re-check
|
**Next free requirement ids** (always re-check
|
||||||
[requirements.md](../requirements.md) before allocating): **UR-079**,
|
[requirements.md](../requirements.md) before allocating): **UR-079**,
|
||||||
**IR-033**, **DR-221**. Three specs below suggested ids that have since been
|
**IR-033**, **DR-222**. Three specs below suggested ids that have since been
|
||||||
taken by other work; each carries a ⚠️ note at the top.
|
taken by other work; each carries a ⚠️ note at the top.
|
||||||
|
|
||||||
## Partially implemented
|
## Partially implemented
|
||||||
|
|||||||
+2
-2
@@ -56,11 +56,11 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tauri-apps/api": "^2",
|
"@tauri-apps/api": "^2",
|
||||||
"@tauri-apps/plugin-log": "^2.9.0",
|
"@tauri-apps/plugin-log": "2.8.0",
|
||||||
"@tauri-apps/plugin-opener": "^2",
|
"@tauri-apps/plugin-opener": "^2",
|
||||||
"@tauri-apps/plugin-os": "^2.3.2",
|
"@tauri-apps/plugin-os": "^2.3.2",
|
||||||
"@tauri-apps/plugin-process": "^2.3.1",
|
"@tauri-apps/plugin-process": "^2.3.1",
|
||||||
"@tauri-apps/plugin-updater": "^2.10.1",
|
"@tauri-apps/plugin-updater": "2.9.0",
|
||||||
"hls.js": "^1.6.15",
|
"hls.js": "^1.6.15",
|
||||||
"svelte-dnd-action": "^0.9.69"
|
"svelte-dnd-action": "^0.9.69"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -26,7 +26,25 @@ bun run build
|
|||||||
|
|
||||||
# --bundles overrides tauri.conf.json bundle.targets so this script controls
|
# --bundles overrides tauri.conf.json bundle.targets so this script controls
|
||||||
# exactly which Linux formats are produced (never NSIS here).
|
# exactly which Linux formats are produced (never NSIS here).
|
||||||
bun run tauri build --bundles "$BUNDLES"
|
# TRACES: | DR-221
|
||||||
|
#
|
||||||
|
# 🔴 NO_STRIP=true is required for the AppImage bundle.
|
||||||
|
#
|
||||||
|
# linuxdeploy (which Tauri downloads and runs to build the AppImage) carries its
|
||||||
|
# own `strip`, and that copy is too old to parse the `.relr.dyn` section modern
|
||||||
|
# toolchains emit for RELR relocations. It fails on essentially every bundled
|
||||||
|
# library:
|
||||||
|
#
|
||||||
|
# strip: libzstd.so.1: unknown type [0x13] section `.relr.dyn'
|
||||||
|
# failed to bundle project `failed to run linuxdeploy-x86_64.AppImage`
|
||||||
|
#
|
||||||
|
# Ubuntu 23.10+ links with -z pack-relative-relocs by default, so the CI builder
|
||||||
|
# image hits this exactly as a modern Arch host does. Skipping the strip step is
|
||||||
|
# linuxdeploy's own documented escape hatch; the cost is an unstripped, larger
|
||||||
|
# AppImage (~153 MB for a build that bundles libmpv and its ffmpeg stack).
|
||||||
|
#
|
||||||
|
# Remove this only after confirming a linuxdeploy release that understands RELR.
|
||||||
|
NO_STRIP=true bun run tauri build --bundles "$BUNDLES"
|
||||||
|
|
||||||
BUNDLE_ROOT="src-tauri/target/release/bundle"
|
BUNDLE_ROOT="src-tauri/target/release/bundle"
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ bun run build
|
|||||||
# from tauri.conf.json (bundle.targets includes "nsis"), which is not subject to
|
# from tauri.conf.json (bundle.targets includes "nsis"), which is not subject to
|
||||||
# that CLI validation — the bundler then picks nsis once it knows the target is
|
# that CLI validation — the bundler then picks nsis once it knows the target is
|
||||||
# Windows.
|
# Windows.
|
||||||
|
# TRACES: | DR-221
|
||||||
|
#
|
||||||
# 🔴 Clear the bundle output before building.
|
# 🔴 Clear the bundle output before building.
|
||||||
#
|
#
|
||||||
# The bundle directory is not versioned and is never cleaned by cargo, and the
|
# The bundle directory is not versioned and is never cleaned by cargo, and the
|
||||||
|
|||||||
@@ -49,6 +49,11 @@ describe("isTracedSourceFile", () => {
|
|||||||
expect(isTracedSourceFile("src-tauri/deny.toml")).toBe(true);
|
expect(isTracedSourceFile("src-tauri/deny.toml")).toBe(true);
|
||||||
expect(isTracedSourceFile("src-tauri/rust-toolchain.toml")).toBe(true);
|
expect(isTracedSourceFile("src-tauri/rust-toolchain.toml")).toBe(true);
|
||||||
expect(isTracedSourceFile("scripts/hooks/pre-commit")).toBe(true);
|
expect(isTracedSourceFile("scripts/hooks/pre-commit")).toBe(true);
|
||||||
|
// Shell tooling is listed individually, not globbed: most scripts/*.sh
|
||||||
|
// implement nothing, and adding one should be a decision.
|
||||||
|
expect(isTracedSourceFile("scripts/check-release-artifacts.sh")).toBe(true);
|
||||||
|
expect(isTracedSourceFile("scripts/build-desktop-linux.sh")).toBe(true);
|
||||||
|
expect(isTracedSourceFile("scripts/logcat.sh")).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("does not scan CI workflows, whose comments discuss TRACES in prose", () => {
|
it("does not scan CI workflows, whose comments discuss TRACES in prose", () => {
|
||||||
|
|||||||
@@ -95,6 +95,14 @@ const TOOLING_FILES = new Set([
|
|||||||
"scripts/hooks/pre-commit",
|
"scripts/hooks/pre-commit",
|
||||||
"src-tauri/deny.toml",
|
"src-tauri/deny.toml",
|
||||||
"src-tauri/rust-toolchain.toml",
|
"src-tauri/rust-toolchain.toml",
|
||||||
|
// Shell tooling that implements a requirement. Named individually rather than
|
||||||
|
// globbing scripts/*.sh: most of these scripts implement nothing, and the
|
||||||
|
// point of the list is that adding a file is a decision.
|
||||||
|
"scripts/install-hooks.sh",
|
||||||
|
"scripts/check-release-artifacts.sh",
|
||||||
|
"scripts/build-desktop-linux.sh",
|
||||||
|
"scripts/build-windows-cross.sh",
|
||||||
|
"scripts/restore-ownership.sh",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/** Directory names that never contain hand-written traced source. */
|
/** Directory names that never contain hand-written traced source. */
|
||||||
|
|||||||
Reference in New Issue
Block a user