The project shipped signed Android builds and unsigned desktop binaries
with no vulnerability scanning of any kind. Nothing checked the ~500
crate Rust graph or the JS packages against an advisory feed, and nothing
checked that what we redistribute inside an MIT bundle permits it.
The first cargo-deny run found eight vulnerabilities and one
unsoundness -- bytes, four in rustls-webpki, time, two in quick-xml and
rand -- every one of them closed by a `cargo update` nobody had a reason
to run. That update is in this commit; 740 Rust tests and clippy
-D warnings pass on the new lockfile.
Two structural fixes matter as much as the gate itself:
- deny.toml scopes the graph to the targets we actually ship. Without
it the Apple targets pull in plist -> quick-xml and report two DoS
advisories against a crate that is in no binary we release. Ignoring
those by ID would silence them everywhere, including where they
would matter; scoping makes them correctly absent.
- libmpv is pinned by rev instead of branch = "master". A branch means
the revision is whatever Cargo.lock happens to hold and any
`cargo update` silently substitutes new upstream code -- in the one
dependency that is not from crates.io and that links a C library
into the player. The rev is the commit already locked, so this pins
current behaviour rather than changing it.
Licence findings are recorded rather than waved through. libmpv and
libmpv-sys are LGPL-2.1, satisfied here by dynamic linking against the
system library; deny.toml carries the two obligations that follow (keep
the linkage dynamic, ship libmpv's licence text with any bundle carrying
the .so). MPL-2.0 crates are file-level copyleft and fine unmodified.
Releases now publish SHA256SUMS (verified in-job with `sha256sum -c`
before upload) and a CycloneDX SBOM for both halves, so "does this
release contain <vulnerable crate>?" has an answer that is not "rebuild
the tag and re-resolve it".
Workflows pin jellytau-builder:2026.08 instead of :latest. While every
job said :latest, rebuilding the image changed what every build compiled
against, including rebuilds of old release tags.
Also folded in, because both were the same class of problem:
- publish-docs.yml downloaded mdBook from GitHub releases into
/usr/local/bin at job time -- a toolchain install in CI, which
CLAUDE.md explicitly forbids, and a hard dependency on GitHub's CDN
at publish time. It is in the builder image now.
- extract-traces.ts only ever read .ts/.svelte/.rs, so every
requirement implemented by *configuration* was invisible to the
matrix that measures it. DR-205, DR-206, DR-207 and DR-215 all carry
TRACES comments nothing read, and each counted as uncovered while
being covered. Coverage was really 90%, not 88%; MIN_THRESHOLD moves
to 89 accordingly. CI workflows stay excluded and there is a test
saying why: traceability-check.yml quotes "a TRACES: comment" beside
deliberately-undefined example IDs, which the extractor would read
as real traces and then fail its own dangling-ID check.
Supply-chain requirement is DR-216.
🔴 The builder image must be rebuilt and pushed
(scripts/build-builder-image.sh 2026.08) before this reaches master --
the workflows now name a tag and tools that do not exist in the registry
yet.
124 lines
4.8 KiB
YAML
124 lines
4.8 KiB
YAML
name: Publish Documentation
|
|
|
|
# Renders the markdown docs (docs/*.md) into an mdBook site, builds the Rust
|
|
# API reference with cargo doc, and force-pushes the combined output to the
|
|
# orphan `gitea-pages` branch that the Gitea Pages server serves.
|
|
#
|
|
# The published matrix is regenerated during the build, so it is never stale.
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- master
|
|
|
|
concurrency:
|
|
# Only one docs publish at a time; a newer push supersedes an in-flight run.
|
|
group: publish-docs
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
publish-docs:
|
|
name: Build & publish docs to gitea-pages
|
|
runs-on: linux/amd64
|
|
container:
|
|
image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08
|
|
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
# bun is baked into jellytau-builder (see Dockerfile.builder); no setup-bun
|
|
# action needed — fetching it stalls on this Gitea runner.
|
|
- name: Install dependencies
|
|
run: bun install
|
|
|
|
# mdBook is baked into jellytau-builder (Dockerfile.builder, MDBOOK_VERSION).
|
|
# It used to be curl'd from GitHub releases straight into /usr/local/bin
|
|
# right here, which was a toolchain install at job time — the exact thing
|
|
# CLAUDE.md's 🔴 rule forbids — and made every docs publish depend on
|
|
# GitHub's CDN answering. To move the version, bump it in the image.
|
|
- name: Confirm mdBook is present
|
|
run: mdbook --version
|
|
|
|
- name: Regenerate traceability matrix (keep published copy current)
|
|
run: bun run traces:markdown
|
|
|
|
- name: Assemble mdBook sources
|
|
run: |
|
|
set -e
|
|
# mdBook's src is docs/. Drop in the SUMMARY and the generated
|
|
# intro + API redirect pages (build artifacts, not committed).
|
|
cp docs-site/SUMMARY.md docs/SUMMARY.md
|
|
|
|
cat > docs/README.md <<'EOF'
|
|
# JellyTau Documentation
|
|
|
|
Cross-platform Jellyfin client — business logic in a Rust backend,
|
|
SvelteKit + TypeScript frontend, talking over Tauri v2 IPC.
|
|
|
|
- **[Requirements Specification](requirements.md)** — user, integration, and development requirements.
|
|
- **[Traceability Matrix](traceability.md)** — generated map from requirements to code (regenerated on every publish).
|
|
- **[Architecture](architecture/README.md)** — backend, frontend, data flow, platform backends.
|
|
- **[Rust API Reference](api/index.html)** — rustdoc for the `src-tauri` backend.
|
|
|
|
_This site is published automatically from `master` by the `publish-docs` CI job._
|
|
EOF
|
|
|
|
cat > docs/api-redirect.md <<'EOF'
|
|
# Rust API Reference
|
|
|
|
The full backend API reference is generated by `cargo doc` (rustdoc).
|
|
|
|
👉 **[Open the Rust API Reference](api/index.html)**
|
|
EOF
|
|
|
|
- name: Build mdBook site
|
|
run: mdbook build docs-site --dest-dir "$GITHUB_WORKSPACE/site"
|
|
|
|
- name: Build Rust API docs (cargo doc)
|
|
working-directory: src-tauri
|
|
# --no-deps keeps it to our own crate (fast, focused); document private
|
|
# items so internal modules/commands appear.
|
|
run: |
|
|
cargo doc --no-deps --document-private-items
|
|
# The backend modules/commands live in the LIB crate (jellytau_lib);
|
|
# the bin crate (jellytau) is a near-empty shim. Land on the lib.
|
|
echo '<meta http-equiv="refresh" content="0; url=jellytau_lib/index.html">' \
|
|
> target/doc/index.html
|
|
|
|
- name: Assemble published output
|
|
run: |
|
|
set -e
|
|
mkdir -p "$GITHUB_WORKSPACE/site/api"
|
|
cp -r src-tauri/target/doc/. "$GITHUB_WORKSPACE/site/api/"
|
|
# Disable Jekyll processing on the pages branch.
|
|
touch "$GITHUB_WORKSPACE/site/.nojekyll"
|
|
ls -la "$GITHUB_WORKSPACE/site"
|
|
|
|
- name: Push to gitea-pages branch
|
|
env:
|
|
# PAT preferred; falls back to the auto-provided token (same pattern
|
|
# as build-release.yml).
|
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
|
AUTO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
set -e
|
|
TOKEN="${GITEA_TOKEN:-$AUTO_TOKEN}"
|
|
REPO="${GITHUB_REPOSITORY}"
|
|
HOST="$(echo "$GITHUB_SERVER_URL" | sed -E 's#^https?://##')"
|
|
REMOTE="https://oauth2:${TOKEN}@${HOST}/${REPO}.git"
|
|
|
|
cd "$GITHUB_WORKSPACE/site"
|
|
git init -q
|
|
git config user.name "gitea-actions"
|
|
git config user.email "actions@gitea.tourolle.paris"
|
|
git checkout -q -b gitea-pages
|
|
git add -A
|
|
# POSIX sh has no ${VAR::N} substring expansion — cut instead.
|
|
SHORT_SHA="$(printf '%s' "$GITHUB_SHA" | cut -c1-8)"
|
|
git commit -q -m "docs: publish site from ${SHORT_SHA}"
|
|
echo "🚀 Force-pushing to gitea-pages"
|
|
git push -f "$REMOTE" gitea-pages
|