* 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>
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.
* ci(release): gate PRs on an authored release note
Second slice of the release-notes pipeline (#1256 landed the format and
generator): make the .changes/ habit survive contact with reality.
- "Release note gate" job in ci.yml, PR-only: validates every .changes/*.md,
then requires an added note (or the no-release-note label) when the PR
touches runtime code under apps/ or libs/. Tests, e2e projects, the
website, mock servers, shared testing helpers, snapshots and docs are
auto-exempt.
- Policy lives in tools/release/check-release-note-gate.mjs as a pure
function fed PR files+labels as JSON — unit-tested (10 cases) instead of
encoded in workflow bash. The failure message lists the triggering files
and names the exact fix.
- Labels are fetched live rather than from the stale event payload, so
applying the label and re-running the check works without a new push.
- The job is dependency-free Node: no pnpm install, runs in seconds.
- release-notes and release-cut skills added under .claude/skills/ and
mirrored to .codex/skills/; CLAUDE.md/AGENTS.md sections updated to point
at the gate and the skills.
The no-release-note label itself was created in the repository.
Tests: 47 passing in release-tools (10 new gate cases); gate-step shell
verified with shellcheck at the CI severity; ci.yml YAML-parse checked.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore(agents): make the release skills discoverable by Claude Code too
`.codex/skills/**` was un-ignored so Codex picks up repository skills in any
clone, but `.claude` was ignored wholesale — and Claude Code only discovers
skills under `.claude/skills/`. The release-notes and release-cut skills
therefore existed only on whichever machine authored them.
Mirror both skills into `.claude/skills/` and opt them in by name rather than
un-ignoring the directory: contributors keep personal skills there
(i18n-fill, website, …) which must stay local and out of `git status`.
CLAUDE.md/AGENTS.md updated so the "skills live under .codex/skills/" claim
does not go stale, including the requirement to keep mirrored copies in sync.
The CI gate and the CLAUDE.md/AGENTS.md section remain the load-bearing
enforcement; skills only carry the detail.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(ci): only a note this PR authored satisfies the release-note gate
Review follow-ups on #1257 (Codex P2 ×2, Greptile P1).
- Drop `renamed` from the accepted statuses. The PR files API compares
base…head, so a note created and then renamed inside the same PR still
reports as `added`; a `renamed` entry means the file already existed on the
base branch. Accepting it let a runtime-code PR pass by moving another
PR's unconsumed note, which documents nothing and gives the generator no
adding commit to resolve a PR link from.
- Require a direct child of `.changes/`. `loadNotes()` reads only the
immediate directory, so `.changes/sub/note.md` satisfied the old prefix
check while never being validated or rendered into any release surface.
Tests: renamed and nested notes now assert a failing gate (12 gate cases).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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.
* fix(lint): resolve module-boundary and prefer-inject errors
Retag workspace-shell-util as type:data-access to match its injectable
services that depend on @iptvnator/services, and convert
RemoteControlService to inject(HttpClient).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(lint): enforce max-lines 400 with generated baseline
Add a max-lines ESLint error (hard cap 400 raw lines per TypeScript
file) per the repo file-size rule. The 134 pre-existing offenders are
baselined in tools/eslint/max-lines-baseline.mjs, regenerable via
generate-max-lines-baseline.mjs; the list should only shrink.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(ci): enforce lint on PRs and guard coverage policy drift
- Add a Lint job to ci.yml running nx run-many -t lint --all, so
module-boundary tags, legacy-alias bans, and max-lines gate merges.
- Fix the root lint script (was linting only electron-backend).
- Add tools/coverage/check-coverage-policy.mjs: fails CI when a project
with a test target is missing from coverage-policy.json; wired into
coverage:ci as coverage:policy:check.
- Run Tier B/C unit tests in CI without coverage (list derived from the
policy), so website/packaging/remote-control tests run on PRs.
- Replace the hand-picked 16-project test:unit:ci list with --all.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: document CI lint enforcement and coverage policy guard
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(ci): address bot review feedback on policy guard and baseline generator
- Drive Tier B/C validation from each policy entry's validationCommand
(falling back to nx test), skipping projects with an e2e target since
the E2E workflow already runs them (Codex).
- Fail when a Tier A entry has no test target (Greptile, adapted:
checking all entries against test targets would false-positive on the
intentionally spec-less e2e/mock-server tiers).
- Guard against missing JSON array in nx show projects output (Greptile).
- Scan .tsx files in the max-lines baseline generator to match the
ESLint rule's file patterns (Greptile).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>