Commit Graph
52 Commits
Author SHA1 Message Date
4gray e9eca1c386 chore(deps): upgrade Angular to 22.1 and Nx to 23.2 (#1603)
* chore(deps): upgrade Angular to 22.1 and Nx to 23.2

* fix(deps): complete Angular migrations after rebasing on master

* fix(ci): use the Node pin for Windows runtime refresh

* docs(deps): synchronize the workspace-shell Node requirements
2026-09-14 19:02:40 +02:00
4gray f04f67728e ci(embedded-mpv): keep Windows runtime pin available (#1495) 2026-08-29 21:24:06 +02:00
4grayandClaude Opus 5 ea4214a0d8 ci(embedded-mpv): add pinned mirrors to the Linux runtime source download (#1427)
The "Build pinned Linux Embedded MPV runtime" job failed twice on 2026-08-11
because www.freedesktop.org answered GitHub runners with HTTP 418 for the
fontconfig tarball. The Linux builder curled a single pinned URL with no
fallback, so upstream rate-limiting reddened the build.

Route downloadArchive() through the shared downloadPinnedSource() helper the
macOS builder already uses, and pin a mirror for each single-host source:
fontconfig and libdisplay-info (freedesktop-hosted) plus freetype, which the
macOS builder already mirrors. Each mirror was downloaded and verified to hash
to the existing pin. The curl hardening flags and assertArchiveMatchesPin are
unchanged, and the helper verifies every candidate against the same SHA-256,
so a mirror serving different bytes is rejected rather than used.

Unlike macOS, the Linux manifest keeps sourceUrl at the canonical pinned value
even when a mirror served the bytes: notice generation and the Snap publication
boundary compare that field against the immutable pin. A used mirror is logged
instead.

build-linux-runtime.mjs now imports the downloader, so download-pinned-source.mjs
joins the released source-archive tooling set (otherwise the archive would ship
a build script it cannot run) and the Linux runtime cache key.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 19:57:54 +02:00
dependabot[bot]and4gray 73f6eb9b17 chore(deps): bump the actions-minor-patch group with 2 updates (#1403)
* chore(deps): bump the actions-minor-patch group with 2 updates

Bumps the actions-minor-patch group with 2 updates: [pnpm/action-setup](https://github.com/pnpm/action-setup) and [github/codeql-action](https://github.com/github/codeql-action).


Updates `pnpm/action-setup` from 6.0.9 to 6.0.10
- [Release notes](https://github.com/pnpm/action-setup/releases)
- [Commits](https://github.com/pnpm/action-setup/compare/v6.0.9...v6.0.10)

Updates `github/codeql-action` from 4.37.4 to 4.37.6
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v4.37.4...v4.37.6)

---
updated-dependencies:
- dependency-name: pnpm/action-setup
  dependency-version: 6.0.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-minor-patch
- dependency-name: github/codeql-action
  dependency-version: 4.37.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* test(packaging): allow updated pnpm action

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: 4gray <serega05@gmail.com>
2026-08-11 02:43:53 +02:00
dependabot[bot]and4gray 7103f7e734 chore(deps): bump pnpm/action-setup from 4 to 6.0.9 (#1372)
* chore(deps): bump pnpm/action-setup from 4 to 6.0.9

Bumps [pnpm/action-setup](https://github.com/pnpm/action-setup) from 4 to 6.0.9.
- [Release notes](https://github.com/pnpm/action-setup/releases)
- [Commits](https://github.com/pnpm/action-setup/compare/v4...v6.0.9)

---
updated-dependencies:
- dependency-name: pnpm/action-setup
  dependency-version: 6.0.9
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* test(packaging): allow pnpm action setup v6

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: 4gray <serega05@gmail.com>
2026-08-08 09:39:52 +02:00
4gray b14ce2452b fix(packaging): add verified source mirror fallback (#1325) 2026-08-01 15:15:09 +02:00
4gray 2ac0de752f fix(skills): align repository guidance with implementation (#1315)
* docs(skills): design implementation synchronization

* docs(skills): plan implementation synchronization

* fix(release): filter internal notes from public body

* docs(release): synchronize release workflow guidance

* fix(stalker): normalize catalog series flags

* fix(stalker): preserve progress with scoped episode IDs

* fix(playback): expose strict position persistence

* docs(stalker): record series position compatibility

* test(skills): validate repository skill contracts

* fix(database): keep SQL trace values private

* docs(skills): refresh Nx and SQLite ownership

* docs(skills): align provider and UI guidance

* docs(skills): tighten validated guidance

* docs(release): require exact release pushes

* style(electron): remove trailing blank line

* fix(ci): classify repository skills coverage
2026-07-31 08:00:59 +02:00
c637a0520e chore(deps): bump softprops/action-gh-release from 2 to 3 (#1284)
* chore(deps): bump softprops/action-gh-release from 2 to 3

Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 2 to 3.
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](https://github.com/softprops/action-gh-release/compare/v2...v3)

---
updated-dependencies:
- dependency-name: softprops/action-gh-release
  dependency-version: '3'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(ci): allow softprops/action-gh-release v3 in the Snap workflow policy

The Snap supply-chain policy test pins the exact major of every action
the build workflow may use, so bumping softprops/action-gh-release in
the workflow without updating BUILD_ACTION_ALLOWLIST fails
publish-snap-workflow.test.mjs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: 4gray <serega05@gmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 08:13:12 +02:00
55f68e73c8 chore(deps): bump actions/setup-node from 4 to 7 (#1285)
* chore(deps): bump actions/setup-node from 4 to 7

Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4 to 7.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v4...v7)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(ci): allow actions/setup-node v7 in the Snap workflow policy

The Snap supply-chain policy test pins the exact major of every action
the build workflow may use, so bumping actions/setup-node in the
workflow without updating BUILD_ACTION_ALLOWLIST fails
publish-snap-workflow.test.mjs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: 4gray <serega05@gmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 08:13:00 +02:00
553f45dedc chore(deps): bump actions/cache from 4 to 6 (#1281)
* chore(deps): bump actions/cache from 4 to 6

Bumps [actions/cache](https://github.com/actions/cache) from 4 to 6.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4...v6)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(ci): allow actions/cache v6 in the Snap workflow policy

The Snap supply-chain policy test pins the exact major of every action
the build workflow may use, so bumping actions/cache in the workflow
without updating BUILD_ACTION_ALLOWLIST fails
publish-snap-workflow.test.mjs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: 4gray <serega05@gmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 08:12:18 +02:00
4gray f193232dab ci(deps): bump checkout/upload-artifact/download-artifact majors (#1264)
Supersedes #1249, #1245 and #1247, which each rewrote the full-commit pins in
publish-snap.yaml while the same SHAs are asserted in three packaging test
files — merged separately, every one of them left those tests red.

actions/checkout v4 -> v7 (docker.yml from v6), actions/upload-artifact
v4 -> v7, actions/download-artifact v4 -> v8. New pins verified against the
upstream tag refs: checkout 3d3c42e5 = v7.0.1, upload-artifact 043fb46d =
v7.0.1, download-artifact 3e5f45b2 = v8.0.1.

download-artifact v8 changes two things on the Snap publish path, both in our
favour: a digest mismatch now fails the run instead of logging a warning, and
decompression is skipped for non-zip Content-Types (our artifact is a normal
upload-artifact zip, so unchanged). checkout v7's fork-PR block only applies to
pull_request_target/workflow_run, neither of which exists here.
2026-07-26 19:54:39 +02:00
4grayandClaude Opus 5 270350c2e1 chore(release): author release notes in .changes instead of reconstructing them (#1256)
* chore(release): author release notes in .changes instead of reconstructing them

CHANGELOG.md has been frozen at 0.12.0 since 2023 while the app shipped
0.23.0, semantic-release sat in devDependencies with no config, and the real
user-facing notes were a 280-line MDX post written from memory at release
time. The gap was never version math — it was authored notes captured while
the context is still fresh.

Add a `.changes/*.md` note format (type, area, issues, screenshot; no version
field, since the release version is chosen deliberately) plus a generator that
composes the GitHub release body, the CHANGELOG.md section and a blog-post
scaffold from the accumulated notes.

Changesets was considered and rejected: it versions multiple published
packages, and this repo has exactly one private package. Its `version` step
would also rewrite CHANGELOG.md into a flatter format than the blog post and
fight the deliberate, updater-constrained version choice.

- hand-rolled frontmatter parser over a YAML engine: the schema is closed, so
  it can reject unknown keys, which is what catches typos
- PR numbers are resolved from the commit that added the note, never written
  by the author
- MDX-significant characters in note bodies are escaped so a stray `<` cannot
  break the website build
- blog scaffold ships `draft: true` with explicit TODO headings; the prose is
  editorial work, only the inventory is mechanical
- revive CHANGELOG.md with an honest pointer for 0.13.0-0.23.0 rather than
  fabricating the missing history
- drop the five unused semantic-release/conventional-changelog packages

Docs: `.changes/README.md`, plus a "Release Notes For User-Visible Changes"
section mirrored in CLAUDE.md and AGENTS.md, and a PR template checkbox for
contributors who never read either.

Tests: 26 unit tests in tools/release/release-notes.test.mjs covering parsing,
validation, grouping and all three renderers.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(release): default the notes version to package.json and harden alt escaping

Review follow-ups on the release-notes generator.

- `--version` now defaults to the root package.json version, so the
  `release🎶*` package scripts run bare instead of failing on a missing
  argument. Bumping package.json is the deliberate act that starts a release,
  which makes it the right single source of truth; `--version` remains as an
  override for dry runs before the bump. The notice goes to stderr so
  `--format github` keeps a pipeable stdout.
- Escape backslashes before apostrophes when building the MDX `alt` string
  literal. A note body ending in a backslash previously produced an
  unterminated string and would have broken the website build.
- Document that release posts are one per minor version, in the slug helper,
  the overwrite error, and `.changes/README.md` — a patch release edits the
  existing post rather than creating a second one.

Tests: +1 regression test for the alt escaping, verified to fail without the
fix (27 total, all passing).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* feat(ci): put authored notes into the tag release body, fail-closed

Wires the .changes pipeline into the release workflow (Codex review P1 on
#1256). Calling the generator from the tag build cannot work — --consume
deletes .changes/ before the tag exists — so the tag build reads what the
generator already wrote: release-meta now fills BODY from the CHANGELOG.md
section matching the tag's version via tools/release/extract-changelog-section.mjs.

generate_release_notes stays on, so GitHub's commit list renders below the
authored notes; the existing draft-metadata repair step already concatenates
RELEASE_BODY with the generated notes, so the rare duplicate-draft path keeps
the same layering unchanged.

The extractor exits non-zero when the section is missing or empty, failing
the release instead of silently shipping PR-title-only notes. A hotfix tag
cut without running release:notes:changelog therefore fails at create-release
by design; the error message names the exact commands to run.

Tests: 5 new extractor tests (32 total in release-tools, all passing);
packaging suite (247) re-run green since build-and-make.yaml is one of its
inputs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(release): escape all regex metacharacters in the changelog extractor

CodeQL flagged the version-to-RegExp interpolation in
extract-changelog-section.mjs (regex injection + incomplete escaping): only
dots were escaped, and while the CLI validates its argument as bare semver
before calling, the exported extractSection() carries no such guarantee on
its own. Escape the full metacharacter set so no caller can inject pattern
syntax, with tests covering wildcard dots, alternation, `.*` and backslashes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(release): make changelog generation idempotent per version

Codex review P2 on #1256: rerunning `release:notes:changelog` for the same
version — the normal move after correcting a note before --consume —
prepended a second section instead of replacing the first, leaving duplicate
release entries.

Extract the marker insertion into upsertChangelogSection(): it removes any
existing section for the version, then rebuilds around the marker rather than
string-replacing into it, so the blank-line count on both sides stays exact
on both the fresh-insert and replace paths. The CLI reports when a section
was replaced.

Tests: 4 new cases (insert, replace-not-duplicate, neighbours untouched,
missing marker); 37 total passing. End-to-end rerun verified: one heading,
latest date wins, extractor output unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 18:22:43 +02:00
4gray cfa602d5b1 ci: cut PR runner waste and harden workflow permissions (#1226)
Pipeline audit follow-up: reduce wasted runner time on PRs and tighten CI
security, without reducing what actually gets validated.

Runner-time waste:
- Concurrency with PR-only cancel-in-progress on CI, E2E, and docker-build,
  so a new push cancels the previous commit's still-running checks. Non-PR
  runs use the unique run_id as the group, because GitHub keeps at most one
  pending run per group even with cancel-in-progress: false — a shared ref
  group could silently drop a queued master run.
- paths-ignore for docs-only changes (Markdown, docs/, .plans/, .codex/,
  .claude/) on the Electron build matrix and the E2E suites; E2E also skips
  apps/website/**. The build workflow keeps apps/website/** because its Linux
  job builds the website to verify AppStream assets. Tag pushes are
  unaffected: GitHub does not evaluate paths filters for tags.
- PRs lint affected projects only; master pushes keep the full run-many.
  Lint-global inputs (eslint.config.mjs, tools/eslint/**) now mark all 41
  lint projects affected, including the run-commands targets database and
  packaging, so the max-lines baseline cannot be widened without lint.

Hardening:
- Explicit least-privilege permissions on CI, E2E, and build-and-make; the
  create-release job keeps its job-level contents: write. The repository
  default workflow token was switched to read-only.
- New actionlint job (image pinned by digest, shellcheck at warning+), with
  the shared-anchor false positive suppressed in .github/actionlint.yaml.
  Fixed one real finding: unquoted $GITHUB_OUTPUT.
- .github/dependabot.yml: weekly cadence, minor+patch grouped per ecosystem
  (npm, GitHub Actions, Docker), majors stay individual PRs.

Docs updated: CLAUDE.md, docs/architecture/nx-workspace-boundaries.md, and
docs/architecture/validation-map.md now describe affected-lint on PRs and the
E2E path-filter exceptions.
2026-07-25 14:37:40 +02:00
4grayandClaude Fable 5 ec09778f36 feat(about): show build commit next to the app version (#1208)
* feat(about): show build commit next to the app version

Settings > About now renders "<version> (<short-sha>)" with the full
SHA in the tooltip, so bug reports from test and nightly builds
identify the exact commit. The commit is injected at CI build time into
apps/web/src/environments/build-commit.ts (same placeholder pattern as
the TMDB key inject); PR builds use the real head SHA instead of the
ephemeral merge commit. Local/dev builds keep the plain version.

The semver version itself deliberately stays untouched: a "-sha"
suffix would flip electron-updater into prerelease mode and leak into
installer/artifact version fields.

Requested by WolfganP in #1202.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* style(settings): keep relative import after monorepo alias imports

Addresses Greptile feedback on #1208.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(docker): inject build commit into published PWA images

The Docker/PWA build path bypassed the Electron workflow's inject step,
so published images showed the plain version in About. Pass the commit
as a build arg and run the inject script before the PWA build; the
script no-ops when BUILD_COMMIT is empty, leaving local docker builds
unchanged.

Addresses Codex feedback on #1208.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 09:44:08 +02:00
4grayandClaude Fable 5 29e624b0dd ci(release): make test draft releases traceable and self-cleaning (#1202)
* ci(release): make test draft releases traceable and self-cleaning

Every PR and master build created a draft named "Release v<version>"
with tag test-<github.sha>, so 70+ identical drafts piled up and PR
drafts were untraceable (for pull_request events github.sha is the
ephemeral merge-commit SHA that resolves to nothing in the repo).

- Title test drafts as "v<ver> — PR #<n> @ <sha> [test]" /
  "v<ver> — master @ <sha> [test]"; tag releases keep "Release v<ver>"
- Prepend a context header (PR, head commit, workflow run links) to the
  auto-generated release notes
- Use the PR head SHA and pass target_commitish so generated notes
  actually cover the PR commits
- Use stable tags (test-pr-<n>, test-master) so action-gh-release
  updates one rolling draft in place instead of creating a new one per
  push
- Mark all non-tag drafts as prerelease
- Cancel superseded in-progress PR builds via a concurrency group
- Delete a PR's rolling draft when the PR closes (new workflow)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci(release): close review-bot race windows in draft release flow

- Move concurrency from workflow level to job level: cancelling a whole
  run could interrupt action-gh-release mid-asset-replacement and leave
  the rolling draft incomplete. Build slots still cancel superseded PR
  work (matrix-aware groups); the release job gets its own serializing,
  never-cancelling group.
- Re-check the live PR state in the release job right before touching
  the draft, so a build that outlives its PR cannot recreate the draft
  after cleanup deleted it.
- In the cleanup workflow, cancel still-running builds of the closed PR
  (dead work anyway) and wait for them to settle before deleting.
- Emit an explicit empty `body=` output for tag builds instead of a
  blank-line heredoc.

Addresses Codex and Greptile review feedback on #1202.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci(release): grant actions:write so PR-close cleanup can cancel builds

gh run cancel needs the actions scope; with only contents: write the
cancellation 403s silently and the settle-poll burns its full window.
Also skip the cleanup job for fork PRs entirely: they never get a
draft and their token is read-only regardless of the permissions block.

Addresses Greptile P1 / Codex P2 follow-up on #1202.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci(release): re-assert rolling draft title after asset upload

action-gh-release@v2 updates name/body/target_commitish on the normal
draft-reuse path, but in a rare race (release listing transiently
missing the draft) it uploads assets to the canonical oldest draft
without refreshing its metadata. PATCH the title and commitish on the
release id the action actually used, so the draft title always names
the current head SHA; the body is left alone to preserve generated
notes.

Addresses Codex round-2 feedback on #1202.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci(release): prune stale assets before updating a rolling draft

The release action only replaces same-name assets, so a PR that bumps
the app version would leave old-version installers beside the new set
in its rolling draft. Delete all existing assets of the matched draft
before the upload; the action re-uploads the full current set right
after. Published releases are never touched.

Addresses Codex round-3 feedback on #1202.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci(release): rebuild full draft metadata after asset upload

Extend the post-upload metadata step to also rebuild the body (context
header + notes from the same generate-notes API the action uses), not
just title/commitish. The rolling draft now ends up with correct
metadata regardless of which internal action-gh-release path ran,
including the rare canonicalize-duplicate fallback. If notes
generation fails, the body is left as the action set it.

Addresses Codex round-4 feedback on #1202.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci(release): only cancel pull_request runs when cleaning up a closed PR

A manually dispatched build on the same head branch is not the PR's
work; filter the cancellation list by event so PR-close cleanup cannot
abort it.

Addresses Codex round-5 feedback on #1202.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci(release): guard PR-close cleanup against close-reopen races

Re-check the live PR state at the start of the cleanup job and again
right before deleting the draft, so a PR that is reopened while the
cleanup is queued or waiting keeps its rolling draft and its fresh
reopened-run builds are not cancelled.

Addresses Codex round-6 feedback on #1202.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci(release): keep tag_name when patching rolling draft metadata

PATCHing a draft release without tag_name makes GitHub drop the
pending tag (the draft turns into untagged-<hash>), so the next run
cannot find the rolling draft by tag and creates a duplicate — observed
live on this PR's own drafts. Include tag_name in both PATCH payloads
of the metadata step.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 07:59:53 +02:00
4gray c266eaa680 fix(packaging): preserve Flatpak Electron ELF for Zypak (#1205)
Fixes the launcher/Zypak regression reported in #1203. The additional GPU/video.js behavior remains tracked separately in that issue.
2026-07-18 22:42:45 +02:00
4gray 8fdac824fd feat(packaging): ship Linux embedded MPV frame-copy runtime (#1200)
* docs: design Linux frame-copy packaging

* docs: plan Linux frame-copy packaging

* feat(packaging): define Linux frame-copy profiles

* fix(packaging): reject inherited profile names

* feat(embedded-mpv): validate staged Linux runtime

* fix(embedded-mpv): require Linux source packages

* fix(embedded-mpv): harden Linux runtime staging

* feat(embedded-mpv): build LGPL Linux runtime

* fix(embedded-mpv): pin Linux runtime inputs

* feat(embedded-mpv): build relocatable Linux helper

* fix(embedded-mpv): require bundled Linux runtime

* fix(embedded-mpv): make Linux runtime portable

* feat(packaging): ship Linux frame-copy artifacts

* fix(embedded-mpv): verify Linux helper linkage

* fix(packaging): enforce Linux frame-copy isolation

* fix(embedded-mpv): pin Linux display data

* docs(embedded-mpv): document Linux frame-copy packaging

* feat(embedded-mpv): probe Linux frame-copy runtime

* test(embedded-mpv): smoke packaged Linux frame-copy

* docs(embedded-mpv): clarify Linux system runtime baseline

* fix(embedded-mpv): harden Linux runtime capability gate

* ci: verify Linux frame-copy packages

* test(embedded-mpv): harden packaged Linux smoke

* test(embedded-mpv): preserve packaged GL mode

* test(packaging): harden Linux package probes

* fix(embedded-mpv): enable private Snap shared memory

* fix(embedded-mpv): sanitize Linux helper environment

* fix(packaging): enforce private Snap memory semantics

* fix(packaging): reject ambiguous Snap memory metadata

* fix(embedded-mpv): prioritize trusted Snap GL

* fix(packaging): reject advanced Snap YAML semantics

* fix(packaging): reject arbitrary Snap YAML aliases

* feat(packaging): ship Linux runtime license notices

* docs(embedded-mpv): document Linux runtime distribution

* fix(packaging): parse Snap trailing comments safely

* fix(release): gate Snap publish on public source release

* fix(packaging): strip VCS metadata from source bundle

* docs(packaging): clarify Linux source release gate

* test(embedded-mpv): smoke missing bundled libmpv

* style(embedded-mpv): format final validation inputs

* fix(e2e): satisfy fixture index signature typing

* fix(ci): declare fontconfig gperf generator

* fix(embedded-mpv): hash runtime cache identities

* fix(packaging): harden Linux frame-copy delivery

* fix(packaging): tighten runtime delivery gates

* fix(ci): decouple Linux runtime matrix

* fix(packaging): harden Linux frame-copy delivery

* fix(packaging): validate Linux frame-copy runtimes

* fix(packaging): scope Snap Electron library checks

* feat(packaging): ship Linux frame-copy runtimes

* fix(packaging): improve Linux runtime smoke diagnostics

* fix(packaging): expose bounded helper probe details

* test(packaging): trace Snap EGL probe failures

* fix(packaging): prefer core22 ABI in Snap helper

* fix(packaging): bound helper probe capture

* fix(packaging): harden Linux frame-copy releases

* fix(packaging): canonicalize libplacebo submodule identity

* fix(packaging): make source archive inspection portable

* fix(packaging): harden Snap release verification
2026-07-18 17:28:22 +02:00
4gray 59e08fd2d6 feat(embedded-mpv): add Windows frame-copy support (#1175)
Port the embedded mpv frame-copy pipeline to Windows with WGL rendering and named shared memory. Includes packaging validation, platform gates, tests, and architecture documentation.
2026-07-15 21:27:56 +02:00
4grayandClaude Fable 5 7d75d989e8 feat(embedded-mpv): Linux port of the frame-copy rendering engine (headless EGL) (#1171)
* feat(embedded-mpv): Linux frame-copy helper via headless EGL

Port the frame-copy engine's native layer to Linux (PORTING.md items 1-4):

- frame_helper_gl.h: platform GlContext abstraction. macOS keeps the CGL
  path (moved verbatim); Linux acquires an EGL display in order
  surfaceless-Mesa -> default display -> GBM render node, binds a 3.2 core
  desktop-GL context surfaceless (1x1 pbuffer fallback), and hands mpv
  eglGetProcAddress. The helper's own GL calls link against glvnd
  libOpenGL, so no display server is required.
- frame_shm.h: portable frame_shm_now_ns() (CLOCK_MONOTONIC) shared by the
  helper and the reader addon, replacing the macOS-only
  clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW); producer and consumer stay on
  the same clock.
- embedded_mpv_frame_reader.c: real implementation now also on __linux__
  (the code was already POSIX apart from the clock call).
- binding.gyp: OS==linux executable branch for iptvnator_mpv_helper linking
  system libmpv (-lmpv) + EGL/OpenGL/gbm, with rpaths for $ORIGIN/lib and
  the build-time library dir. The in-process addon still does not link
  libmpv - the ban only binds in-process, the helper is out of process.
- build-embedded-mpv.js: system-dev fallback on Linux (LIBMPV_INCLUDE_DIR
  or /usr/include) so a distro libmpv-dev install builds without staging a
  vendored runtime; a pre-set LINUX_NATIVE_LIBRARY_DIR now wins over the
  vendored lib dir.

Verified on Ubuntu 25.04 / i7-1165G7 (Iris Xe): lavfi smoke per PORTING.md
(idle->loading->playing snapshots at 4 Hz, aspect-fit generation bump
g1 1280x720 -> g2 960x720 for a 4:3 source), reader probe 60 fps at
1080p60 with 0 torn reads, clean quit with no leaked processes or shm.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(embedded-mpv): enable the frame-copy engine gates on Linux

Flip the TypeScript side of the Linux port (PORTING.md item 5). A shared
dependency-free predicate, isFrameCopyPlatformSupported() (linux any-arch,
darwin arm64-only), now backs all four gates so they cannot drift:

- main.ts: the persisted Settings toggle promotes to the env flag on Linux
  too (this runs before window creation and controls the sandbox relax).
- EmbeddedMpvNativeService.isFrameCopyEngineActive/isFrameCopyAvailable.
- EmbeddedMpvFrameCopyAdapter.isSupported.

getSupport() ordering: the frame-copy branch moves above the Linux-only
native-engine prerequisites - the X11/Xwayland display-server check and
the system-mpv-on-PATH probe only bind the --wid native engine, while the
frame-copy helper renders offscreen (headless EGL) and links libmpv
itself. createSession() also skips resolving the native window handle for
frame-copy sessions, which the adapter ignores anyway, so native-Wayland
sessions no longer trip the window-handle assertion.

Settings copy: the i18n frame-copy description now says macOS (Apple
Silicon) and Linux in all 18 languages; stale macOS-only doc comments in
the settings/support interfaces updated alongside.

Tests: platform-gate matrix for the adapter (darwin arm64/x64, linux
x64/arm64, win32) and service specs covering Linux activation under
native Wayland, macOS arm64 staying active, macOS x64 staying native, and
the skipped window handle for frame-copy sessions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore(packaging): CI + package guards for the Linux frame-copy helper

- build-and-make.yaml: install libegl-dev/libgl-dev/libgbm-dev on the
  Linux runner (the helper's EGL backend needs them now that the helper
  target builds on Linux), and verify the built helper exists and DOES
  link libmpv - the inverse of the addon's no-libmpv rule, which still
  holds and stays validated.
- electron-after-pack.cjs: strip iptvnator_mpv_helper from packaged Linux
  apps. It links the build host's system libmpv, which end-user systems
  cannot be assumed to have; the support probe treats the missing helper
  as frame-copy-unavailable (dev-build-only engine until the
  bundled-runtime staging milestone).
- frame_helper_gl.h: log the chosen EGL display tier to stderr (the
  adapter mirrors helper stderr), so bring-up problems on exotic setups
  are diagnosable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(embedded-mpv): document the Linux frame-copy port

- architecture doc: frame-copy section covers Linux (EGL display tiers,
  build deps, package strip), Linux support matrix notes the frame-copy
  exception to the X11 + system-mpv requirements, Linux measured baseline.
- RESULTS.md: Ubuntu 25.04 / i7-1165G7 (Iris Xe) measurement rows via the
  production helper + reader probe; viewport-size claim reproduced.
- PORTING.md: Linux marked done with pointers to what changed; Windows
  remains the open port and its perf gate the open decision.
- CLAUDE.md + tools/embedded-mpv/README.md: platform scope, Linux dev
  build requirements, system-headers fallback, helper strip.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(embedded-mpv): commit the Linux frame-copy measurement probe

linux-frame-probe.mjs reproduces the RESULTS.md Linux rows: spawns the
production helper, attaches the frame-reader addon to the announced shm
generation, and reports new-frame fps, copy wall time, produce->copy age,
torn reads and pixel spread. Usage documented in RESULTS.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(embedded-mpv): address multi-agent review findings on the Linux port

Confirmed findings (each verified by 3 adversarial reviewers):

- CI would fail to link the helper: -lOpenGL needs the unversioned glvnd
  libOpenGL.so, shipped only by libopengl-dev, which neither the runner
  images nor the previous apt line provide. Added to the workflow and to
  every documented Linux build-dep list.
- The new 'test -x' dist guard could never pass: webpack's dist asset
  copy drops file modes (helper arrives as 0644). The guard is now
  'test -f'; electron-after-pack.cjs restores the execute bit on packaged
  helpers (also fixes packaged-macOS spawns); the support probe now
  requires X_OK, so a mode-stripped helper reads as frame-copy-unavailable
  and falls back to native instead of failing spawn with EACCES.
- The Settings frame-copy toggle was unreachable in exactly the Linux
  states the port targets: the native-Wayland and missing-system-mpv
  unsupported payloads omitted frameCopyAvailable, and toggle visibility
  derives solely from it. Both returns now advertise availability.

Also from review:

- build-embedded-mpv.js keeps the old graceful-skip contract when the new
  system-dev fallback finds libmpv-dev but the GL/EGL/gbm dev stack is
  missing (previously such machines skipped; a hard electron-build
  failure was a regression).
- createSession derives the window-handle skip from the dispatched addon
  instead of re-evaluating the engine gate, so the two cannot disagree.
- The render thread logs the GL renderer string (surfaceless Mesa can
  silently pick llvmpipe on non-Mesa-primary systems; now diagnosable —
  verified 'Mesa Intel Iris Xe' on this machine).
- Specs pin the new semantics: frameCopyAvailable advertised while native
  is unsupported (Wayland / no mpv), frame-copy supported without a
  system mpv, and the handle-skip test disposes its session through the
  owning adapter.
- Docs: PORTING.md file map reflects the frame_helper_gl.h seam for the
  Windows porter; helper-strip removal correctly gated on milestone 4
  (bundled libmpv), not milestone 3; RESULTS.md preamble notes the
  RAW->MONOTONIC clock change; stale '(macOS)' scope comments updated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(embedded-mpv): address Greptile/Codex review comments

- Sandbox gate requires a usable helper (Greptile P1, security): the
  main.ts env promotion now also probes for an executable
  iptvnator_mpv_helper before relaxing the window sandbox — a stale
  opt-in on packaged Linux (helper deliberately stripped) or after a
  cleaned native build no longer costs a sandboxless launch for an
  engine that cannot activate. Helper discovery (addon candidate paths +
  X_OK probe) moved into embedded-mpv-frame-copy-platform.util.ts,
  shared by main.ts and the service; the service keeps thin instance
  wrappers so tests can stub per scenario. New util spec pins the
  platform matrix, candidate resolution, and the execute-bit semantics.
- Stale frame-copy artifacts on skipped builds (Codex P2): cleanOutput()
  now also removes iptvnator_mpv_helper and
  embedded_mpv_frame_reader.node, so a failed/skipped rebuild cannot
  leave a previous helper advertising frame-copy support against a
  runtime the build just declared unavailable.
- Multiarch default lib dir (Greptile P1, partially refuted): -l
  resolution never depended on our -L (the compiler's built-in search
  paths include the Debian/Ubuntu multiarch dir — proven by the green CI
  run linking with a nonexistent -L dir), but the system-dev fallback
  now defaults to /usr/lib/<multiarch-triple> when present so the -L
  flag and the helper's baked rpath point somewhere real.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(embedded-mpv): harden Linux frame-copy port

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 20:42:50 +02:00
4grayandClaude Fable 5 dc05a2566e feat(tmdb): opt-in TMDB metadata enrichment for Xtream and Stalker portals (#1123)
* feat(tmdb): opt-in TMDB metadata enrichment for Xtream and Stalker portals

Adds an opt-in TMDB integration (Settings > Metadata) that enriches
detail views with a field-level merge — the provider stays authoritative
for stream data, TMDB fills editorial fields when the match is confident.

Enrichment:
- Movie/series details: plot, cast (avatar chips), director, genres,
  rating, poster/backdrop, official YouTube trailers
- Confidence-gated matching: provider tmdb_id trusted; otherwise
  normalized-title search with year gate (±1; series accept earlier
  premieres), season-suffix stripping, Cyrillic search-language override,
  and language-prefix fallback variants
- Lazy season/episode enrichment: real episode names, overviews, stills
- "Similar" rail (Xtream): TMDB recommendations matched to the catalog
- Actor pages per portal with full filmography, availability filter and
  an Electron-only "All portals" scope backed by a batched DB_MATCH_TITLES
  worker op over the trigram FTS index

Infrastructure:
- SQLite cache table tmdb_metadata (details, search verdicts, seasons,
  persons; per-language, TTL-guarded), in-memory fallback for the PWA
- Settings: enable toggle, own-API-key override with a live "check key"
  button; TMDB attribution in Settings and About
- Embedded key stays an empty placeholder; CI injects TMDB_API_KEY via
  tools/tmdb/inject-tmdb-key.mjs when the secret is configured
- normalizeTitle shared between renderer and DB worker
- CSP: allow YouTube embeds (frame-src was 'none'; trailers never worked)

Fixes and refactors along the way:
- fix(stalker): Advanced Search sent bare get_ordered_list requests and
  skipped the auth handshake when isFullStalkerPortal was missing on the
  active-playlist meta — full portals answered "Authorization failed."
  and search looked empty; now mirrors the catalog request shape and
  routes through makeAuthenticatedRequest with URL-based detection
- fix(stalker): TMDB fields survive info re-normalization; detail views
  prefer the store copy patched by async enrichment over stale snapshots
- refactor(xtream): split oversized vod/serial detail components into
  component-scoped playback services; detail routes re-initialize on
  route param changes (router reuses them for detail-to-detail nav)
- i18n: all new keys translated across the 18 locales

Docs: docs/architecture/tmdb-metadata-enrichment.md + CLAUDE.md updates.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(tmdb): provide route params observable to inline collection details, linearize regexes

The global-collection inline detail host builds a fake ActivatedRoute for
VodDetailsRouteComponent/SerialDetailsComponent with only snapshot.params.
Since the detail components now read route.params via toSignal() (detail->
detail re-init), the missing observable crashed component construction and
the content hero never rendered — broke dashboard-activation, favorites and
recent Electron E2E on all platforms. Provide the params observable
alongside the snapshot and assert it in the component spec.

Also resolves both CodeQL js/polynomial-redos alerts: bracket-stripping in
normalizeTitle now excludes opening delimiters inside the classes, and
youtubeEmbedUrl extracts watch?v= ids with a linear two-pass match instead
of "watch\?.*v=". Combining-diacritics range rewritten as explicit \u
escapes (greptile note).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(tmdb): surface TMDB-only VOD score in the rating badge, drop youtube.com from CSP

Review follow-ups on PR #1123: the Xtream VOD detail badge renders
rating_imdb, but the merge wrote the TMDB score only into `rating`, so a
TMDB-only score was never displayed (Codex P2) — fill rating_imdb when the
provider left it empty, mirroring the Stalker merge. All trailer iframes
are normalized to youtube-nocookie.com, so the extra youtube.com frame-src
allowance was dead surface (greptile) — removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(tmdb): resolve confirmed review findings — matching correctness, race guards, cache schema

Fixes the confirmed findings from the PR #1123 code review:

- Stalker search: setSelectedContentType now runs BEFORE setSelectedItem,
  so the TMDB enrichment gate in the selection hook no longer sees the
  content type of the previously open tab (wrong/no enrichment after
  ITV -> search -> movie).
- Title normalization is now two-tier (normalizeTitleKeys): the exact
  normalized form keeps a trailing year, the base form strips it and
  remembers the tag. Year stripping is anchored to the end of the title
  ("2001: A Space Odyssey" keeps its year) and language-prefix stripping
  is UPPERCASE-only ("It: Chapter Two" is no longer amputated).
- All catalog matching (similar rail, actor pages, DB worker
  DB_MATCH_TITLES) compares exact forms first and only accepts
  year-stripped matches when the stripped tag is year-compatible (+-1)
  with the TMDB year — "Blade Runner" (1982) can no longer claim a
  catalog "Blade Runner 2049". CatalogTitleMatch carries the stripped
  trailingYear so the renderer can apply the guard to worker matches.
- mergedBackdrops tolerates a plain-string backdrop_path; enrichment
  merge+patch blocks are wrapped in try/catch so a malformed provider
  payload can no longer become an unhandled rejection.
- loadGlobalMatches (both actor routes) guards against actor->actor
  navigation races — a slow match for the previous person no longer
  overwrites the current one's results.
- tmdb_metadata media_type CHECK widened to ('movie','tv','person') and
  person rows now use the honest 'person' type (TmdbCacheMediaType).
  Pre-release dev DBs with the narrow CHECK are rebuilt in place — the
  table is a pure cache, so the migration is a self-healing
  drop-and-recreate keyed off sqlite_master.

Docs updated (tmdb-metadata-enrichment.md, CLAUDE.md). New regression
coverage: title-normalization.util.spec.ts, two-tier cases in
tmdb-similar.util.spec.ts and title-match.operations.spec.ts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 17:07:46 +02:00
4gray b1119189e2 feat(updater): add GitHub releases desktop updater
Adds the GitHub Releases desktop updater with release notes, startup notification, packaging metadata, tests, and CI fixes for Electron E2E.
2026-06-28 22:21:20 +02:00
4gray 1073ce5350 ci(electron): require embedded mpv in windows artifacts 2026-06-14 22:09:31 +02:00
4gray 4094a36fef Harden Linux embedded MPV packaging (#1043) 2026-06-13 17:24:13 +02:00
4grayand4gray 7775553a6b ci: skip draft release for fork PRs
Co-authored-by: 4gray <fourgray@proton.me>
2026-06-12 15:02:42 +02:00
4gray 06700b318a feat(electron): add embedded mpv support for windows and linux (#1031)
Adds experimental embedded MPV support for Windows and Linux, expands packaging/runtime validation, and updates CI coverage for the cross-platform native addon.
2026-06-09 08:58:38 +02:00
4gray f3c0cd3820 ci: clarify embedded mpv cache inputs 2026-05-22 02:16:32 +03:00
4gray 0b19155469 ci: cache embedded mpv runtime 2026-05-22 02:02:06 +03:00
4gray 31bddbd70f ci: build embedded mpv artifacts on master
Entire-Checkpoint: 5b514fe72836
2026-05-02 09:02:05 +02:00
4gray 6aa4f2521b build(embedded-mpv): enhance macOS build process and validation for embedded MPV integration
Entire-Checkpoint: c6e522b4276c
2026-04-27 11:06:12 +02:00
4gray 0b5089853f ci(build): adjust conditions for macOS Embedded MPV artifact in CI workflow
Entire-Checkpoint: c6e522b4276c
2026-04-27 00:46:54 +02:00
4gray 9f873e6bed feat(player): add embedded-mpv player for macOS as experimental feature
- Introduced tooling for building and staging the macOS `libmpv` runtime for IPTVnator's embedded MPV player.
- Added `build-macos-runtime.mjs` for building an LGPL-compatible runtime from source.
- Created `stage-macos-runtime.mjs` for staging the built runtime artifacts.
- Implemented validation for the packaged embedded MPV runtime in `electron-after-pack.cjs` and `embedded-mpv-macos.cjs`.
- Updated packaging scripts to ensure the embedded MPV runtime is correctly integrated and validated during the build process.
- Added README files to document the expected layout and usage for the embedded MPV runtime artifacts.

Entire-Checkpoint: c6e522b4276c
2026-04-27 00:32:14 +02:00
4gray d90a0a8f89 build: update CI configuration for Linux packaging targets and profiles 2026-04-13 08:46:14 +02:00
4gray b449e3796b build: update Snap packaging to use core22 base for compatibility 2026-04-13 08:06:48 +02:00
4gray f39a51895e ci(flatpak): enhance Flatpak validation checks for metainfo and launcher integrity
Entire-Checkpoint: 21b9b9f90f23
2026-04-12 12:44:22 +02:00
4gray 1136e9abac ci(flatpak): add Flatpak support with metainfo and asset validation
Entire-Checkpoint: 21b9b9f90f23
2026-04-12 12:13:05 +02:00
4gray e0b3c74fed ci(packaging): implement Linux launcher sandbox fix and verification process
Entire-Checkpoint: 21b9b9f90f23
2026-04-12 11:47:07 +02:00
4gray 2bfa557438 ci(workflow): add verification step for packaged worker layout 2026-03-28 21:41:24 +01:00
4gray 367c9507a5 ci(macOS): enhance code signing process with CSC_NAME variable and verification steps 2026-03-15 10:55:30 +01:00
4gray ec84eea924 feat(macOS): add notarization support and entitlements for Electron app 2026-03-13 07:36:09 +01:00
4gray 0b05c07462 chore: remove pnpm version specification in workflows 2026-02-19 23:13:07 +01:00
4gray 61d34aef7a chore: switch to pnpm 2026-02-19 23:08:36 +01:00
4gray 4979d3f876 fix(ci): enable tag-based Snap publish and remove trailing spaces 2026-01-13 22:55:32 +01:00
4gray 96e2a57d0c ci: run workflows on master branch instead of nx
Update GitHub Actions workflow triggers to use the master branch
for push and pull_request events. Replace the previous nx branch
restriction so CI (build, make and e2e tests) runs on master and tag
events as intended.

This enables workflows to execute for changes merged to master and
aligns branch configuration across workflows.
2026-01-12 21:58:29 +01:00
4gray 54b8d3a18b ci: move env variable to step header 2025-12-14 13:03:13 +01:00
4gray d7bfd2648c feat(ci): add GitHub Action job to publish Snap on tags
Add a publish-snap job to the CI workflow that runs after build and
only triggers for version tags (refs/tags/v*). The job downloads the
linux build artifacts, installs snapcraft, locates the generated .snap
file and uploads it to the Snapcraft Store releasing to the stable
channel. It validates the presence of a snap file and fails if none is
found.

This enables automated Snap distribution for tagged releases using the
SNAPCRAFT_STORE_CREDENTIALS secret.
2025-12-14 12:31:00 +01:00
4gray 568a5feac3 fix(ci): use different runners for macos arm and intel arch
Update GitHub Actions workflow to support multiple OS runners and CPU
architectures, standardize matrix keys, and adjust platform-specific
steps and artifact names.

- Replace simple os matrix with include entries specifying os, runner,
  and arch to allow macOS x64/arm64 separation and explicit runners for
  linux/windows.
- Use runner matrix value for runs-on and change timeout/strategy
  accordingly.
- Adjust conditional checks to use normalized os values (linux, macos,
  windows) rather than platform-specific runner labels.
- Install Linux system deps only when os == 'linux' and keep Flatpak
  setup for electron-builder.
- Add a macOS-specific make step that passes the architecture to the
  electron make command, while keeping a combined make step for
  non-macOS builds.
- Upload macOS artifacts per architecture and rename artifact paths to
  include arch; update release files list to include both macOS x64 and
  arm64 artifact patterns.

These changes enable reliable, architecture-aware macOS builds,
prevent native module conflicts across runners, and ensure artifacts
are labeled and uploaded correctly.
2025-12-12 22:22:31 +01:00
4gray 4020de1af9 ci: enhance build workflow with Flatpak setup
Added Flatpak configuration steps to the build workflow.
2025-11-26 17:28:28 +01:00
4gray ae6e97db91 ci: install Linux dependencies in CI workflow
Added steps to install Linux system dependencies for builds.
2025-11-26 17:12:39 +01:00
4gray 12a513802a fix(electron): simplify production load and CI frontend build
Remove unused url.format import and switch from loadURL with a
formatted file: URL to loadFile for production. This simplifies code,
avoids manual file URL construction, and makes the production path
loading more robust.
Also update CI job to build the frontend via the NX command
(npx nx build web --skip-nx-cache) instead of npm run build:frontend,
aligning the workflow with the monorepo's canonical build command.
2025-11-23 19:14:01 +01:00
4gray fbed100fb0 ci: make release step create draft prereleases for PRs
Update the GitHub release action to mark releases from pull 
request events as prerelease and to set a safe tag name when the
 is not triggered by a tag ref.

- Set prerelease to true when github.event_name == 'pull_request'
  so draft releases created during PR workflows are marked as
  prereleases.
- Use the tag name from github.ref_name when the workflow is
  triggered by a tag; otherwise generate a deterministic test tag
  "test-<sha>" to avoid using an invalid or empty tag name.

This prevents accidental full releases from PR runs and ensures the
action always has a valid tag_name value.
2025-11-08 23:02:15 +01:00