mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-07-21 20:31:21 +00:00
Some checks are pending
ship-ci / ci (push) Waiting to run
* fix(settings): enable Linux nightly AppImage auto-update in System settings
Fixes the Settings → System "App updates" panel so it actually works on
Linux, and adds Nightly as a selectable channel — previously missing
entirely, so Linux self-update couldn't be reached from this UI at all.
- The channel dropdown no longer gets permanently disabled the moment
the desktop bridge reports 'unsupported', which is the normal state
whenever the channel isn't Nightly on Linux. It stays enabled so the
user can switch to Nightly, the only way out of that state.
- Shows live download progress ("Downloading update… N%") and an
explicit button state machine (Check → grayed out while busy →
Restart now once staged), instead of a frozen "Checking…" during the
~1.5GB background download.
- Renders every status update from the triggering action's own return
value (checkNow()/setChannel()'s result) rather than a separate
follow-up getStatus() call, which can race against other state
changes and show a stale result even after a real success.
- setupAppUpdates() no longer re-syncs the channel to the backend on
every Settings-panel re-render — only once per page load — so a
redundant sync can no longer stomp an in-flight download's state.
- Routes update-flow events into the existing diagnostics.js
console-capture + contribute() snapshot API, so the user's existing
"Export Diagnostics" button now captures the full update decision
trace end to end — no new UI or log file. This diagnostic tracing is
what actually root-caused the bugs above, from real on-device
captures rather than guesswork.
Companion PR in feedBack-desktop (the underlying update engine).
Verified end-to-end on a Steam Deck: channel switch → check → live
download progress → restart button → relaunch onto the new build,
confirmed via a real Export Diagnostics capture showing a clean,
fully-accounted-for trace.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* refactor(settings): extract + unit-test the app-update status view; dedupe diag log
- Extract the status→UI state machine from renderFrom into a pure, exported
_appUpdateStatusView() (DOM-free) and cover it with tests/js — settings.js's
large module graph made importing it for a full harness impractical, so the
pure function is the testable seam. Behavior-preserving; renderFrom applies
the returned shape to the DOM exactly as before.
- Dedupe the [update-diag] renderFrom console line so the ~1.5s download poll
no longer floods the diagnostics ring buffer with byte-identical entries;
every real state/percent change still logs, and the structured contribute()
snapshot stays unconditional.
Left the 'audio_engine' diagnostics key as-is: the server export filters
client contributions to loaded plugin ids (diagnostics_bundle.py path-traversal
guard), so a dedicated key would be silently dropped from the bundle.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Byron Gamatos <xasiklas@gmail.com>
---------
Signed-off-by: Byron Gamatos <xasiklas@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Byron Gamatos <xasiklas@gmail.com>
246 lines
10 KiB
YAML
246 lines
10 KiB
YAML
name: ci
|
|
|
|
# Runs only as a reusable workflow invoked by ship-ci.yml (for PRs into main
|
|
# and release/**). It deliberately has no standalone pull_request trigger: a
|
|
# direct run would publish unprefixed "<job>" checks, but the org rulesets
|
|
# require the "ci / <job>" names produced when ship-ci.yml calls this workflow.
|
|
on:
|
|
workflow_call:
|
|
|
|
permissions:
|
|
contents: read
|
|
pull-requests: read
|
|
checks: read
|
|
|
|
jobs:
|
|
|
|
test:
|
|
name: test
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: '3.12'
|
|
cache: 'pip'
|
|
|
|
- name: Guard against print() / traceback.print_exc() in server.py, lib/, and bundled plugin routes
|
|
run: |
|
|
# git grep: tracked files only — no .pyc / __pycache__ noise from
|
|
# later pytest runs. Covers both audited patterns from #155 / #242.
|
|
# `(^|[^A-Za-z0-9_])` anchor avoids matching suffixes like `myprint(`;
|
|
# POSIX leaves `\b` undefined, so we use an explicit character class.
|
|
hits=$(git grep -nE '(^|[^A-Za-z0-9_])(print|traceback\.print_exc)[[:space:]]*\(' \
|
|
-- server.py lib/ \
|
|
$(git ls-files 'plugins/*/routes.py') || true)
|
|
if [ -n "$hits" ]; then
|
|
echo "$hits"
|
|
first=$(printf '%s\n' "$hits" | head -n1)
|
|
file=$(printf '%s' "$first" | cut -d: -f1)
|
|
line=$(printf '%s' "$first" | cut -d: -f2)
|
|
echo "::error file=${file},line=${line}::print() or traceback.print_exc() found in server.py, lib/, or a bundled plugin routes.py. Use the feedBack logger (lib/logging_setup.py) — see issues #155 / #242."
|
|
exit 1
|
|
fi
|
|
|
|
- name: Install dependencies
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install -r requirements.txt -r requirements-test.txt
|
|
|
|
- name: Run pytest
|
|
run: pytest
|
|
|
|
- name: Run JS plugin-API tests
|
|
run: node --test tests/js/*.test.js 'tests/plugins/*/js/*.test.js' 'plugins/*/tests/*.test.js'
|
|
|
|
tailwind-fresh:
|
|
# Guard that the committed static/tailwind.min.css is in sync with source.
|
|
# The Play CDN's runtime JIT was removed (feedBack-desktop#110); a prebuilt
|
|
# stylesheet only contains classes the scanner saw at build time, so stale
|
|
# CSS silently ships unstyled elements. Rebuild and fail on any diff.
|
|
name: tailwind-fresh
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
# npm ci runs third-party postinstall scripts; don't leave the token in
|
|
# git config for them (this job never pushes).
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: '20'
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
- name: Rebuild Tailwind CSS
|
|
run: bash scripts/build-tailwind.sh
|
|
|
|
- name: Verify committed static/tailwind.min.css is fresh
|
|
run: |
|
|
# Hard-fail (matches the print() guard convention) — do NOT auto-commit.
|
|
if ! git diff --quiet -- static/tailwind.min.css; then
|
|
echo "::error file=static/tailwind.min.css::static/tailwind.min.css is stale. Run 'bash scripts/build-tailwind.sh' and commit the regenerated file."
|
|
git diff -- static/tailwind.min.css
|
|
exit 1
|
|
fi
|
|
|
|
manifest-validation:
|
|
name: manifest-validation
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: '3.12'
|
|
|
|
- name: Validate plugin manifests
|
|
run: |
|
|
python - <<'EOF'
|
|
import json, sys
|
|
from pathlib import Path
|
|
|
|
errors = []
|
|
manifests = sorted(Path("plugins").glob("*/plugin.json"))
|
|
|
|
for manifest in manifests:
|
|
try:
|
|
data = json.loads(manifest.read_text())
|
|
except json.JSONDecodeError as e:
|
|
errors.append(f"{manifest}: invalid JSON — {e}")
|
|
continue
|
|
for field in ("id", "name"):
|
|
if field not in data:
|
|
errors.append(f"{manifest}: missing required field '{field}'")
|
|
pid = data.get("id", "")
|
|
if pid and pid != pid.lower():
|
|
errors.append(f"{manifest}: 'id' must be lowercase, got '{pid}'")
|
|
# The plugin loader treats each plugins/<dir> as a Python module,
|
|
# so the manifest 'id' must match its directory name.
|
|
dirname = manifest.parent.name
|
|
if pid and pid != dirname:
|
|
errors.append(f"{manifest}: 'id' ({pid!r}) must match directory name ({dirname!r})")
|
|
|
|
if errors:
|
|
for e in errors:
|
|
print(f"::error::{e}")
|
|
sys.exit(1)
|
|
print(f"Validated {len(manifests)} manifest(s) — OK")
|
|
EOF
|
|
|
|
feedpak-spec:
|
|
# Guard that core stays faithful to the feedpak format spec, which lives in
|
|
# its own repo (got-feedback/feedpak-spec) and is the contract third-party
|
|
# packers and players build against. Four surface checks: core reads/writes
|
|
# only manifest keys the spec declares (and the scanned-module list can't
|
|
# fall behind); the exception allowlist never grows, so the FEP process is
|
|
# the only way a new key lands; core ingests the spec's example packs; packs
|
|
# committed here pass the spec's reference validator. Motivated by
|
|
# #933, where a manifest key (`original_audio`) shipped in core without ever
|
|
# reaching the spec.
|
|
#
|
|
# The gate checks against the spec repo's HEAD, deliberately: the app must
|
|
# conform to the LIVING spec, always. The dev flow is self-serve — a gated
|
|
# PR opens a FEP, the spec PR merges, re-running this job goes green; no
|
|
# pin file to bump, nothing to maintain. Accepted trade-off: a BREAKING
|
|
# spec change (rare, deliberate, MAJOR per the spec's compatibility policy)
|
|
# reddens every PR here until core conforms — which is the correct
|
|
# org-wide signal that the app is out of conformance. The normal FEP is
|
|
# additive and can never redden this job.
|
|
name: feedpak-spec
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
# This job runs repository code (tools/check_spec_conformance.py) and
|
|
# never pushes; don't leave the token in git config for it.
|
|
# fetch-depth: 0 so the base branch is available — the gate must prove
|
|
# the exception allowlist didn't grow in this PR.
|
|
with:
|
|
persist-credentials: false
|
|
fetch-depth: 0
|
|
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: '3.12'
|
|
cache: 'pip'
|
|
|
|
- name: Check out feedpak-spec at HEAD
|
|
uses: actions/checkout@v4
|
|
with:
|
|
repository: got-feedback/feedpak-spec
|
|
ref: main
|
|
path: .feedpak-spec
|
|
persist-credentials: false
|
|
|
|
- name: Record the spec commit this run verified against
|
|
# HEAD-tracking means CI results can differ across time on the same
|
|
# commit. Log the exact spec SHA so a red run is reproducible.
|
|
run: git -C .feedpak-spec rev-parse HEAD
|
|
|
|
- name: Install dependencies
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install -r requirements.txt
|
|
# CI-only: the spec's reference validator needs jsonschema. Not a
|
|
# runtime dependency — this gate never runs on the serve/Docker path
|
|
# (constitution Principle I). Pinned for the same reason the spec SHA
|
|
# is: an upstream release must not turn this job red on a PR that
|
|
# changed neither this repo nor the spec.
|
|
pip install 'jsonschema==4.26.0'
|
|
|
|
- name: Fetch the base branch's exception allowlist
|
|
id: baseline
|
|
run: |
|
|
# The allowlist is closed: it grandfathers keys that predate this gate
|
|
# and may only shrink. Prove that by diffing against the base branch —
|
|
# without this, anyone could append an entry and route around the FEP
|
|
# process from inside this repo.
|
|
#
|
|
# Resolve the base rather than hardcoding `main`: ship-ci.yml also runs
|
|
# this workflow for PRs into release/** and for pushes to release/**,
|
|
# where a main baseline would diff against the wrong branch.
|
|
# PR -> the branch it merges into
|
|
# push -> the branch itself (its tip already contains the change, so
|
|
# this is a no-op; enforcement happens at PR time)
|
|
BASE="${{ github.event.pull_request.base.ref || github.ref_name }}"
|
|
echo "diffing the allowlist against origin/$BASE"
|
|
git fetch --no-tags --depth=1 origin "$BASE"
|
|
if git cat-file -e FETCH_HEAD:feedpak-spec-exceptions.yml 2>/dev/null; then
|
|
git show FETCH_HEAD:feedpak-spec-exceptions.yml > "$RUNNER_TEMP/baseline-exceptions.yml"
|
|
echo "args=--baseline-exceptions $RUNNER_TEMP/baseline-exceptions.yml" >> "$GITHUB_OUTPUT"
|
|
else
|
|
# Only true until the PR that introduces this gate lands.
|
|
echo "args=--bootstrap-allowlist" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
- name: Check feedpak spec conformance
|
|
run: python tools/check_spec_conformance.py --spec .feedpak-spec ${{ steps.baseline.outputs.args }}
|
|
|
|
lint:
|
|
# Maintainer/CI-only size + module-hygiene gate (constitution Principle I:
|
|
# dev tooling, never on the serve/Docker path — same category as
|
|
# scripts/build-tailwind.sh). max-lines WARNS (the 1,500-line size ratchet;
|
|
# non-blocking), while import-x/no-unresolved + no-cycle HARD-ERROR on the
|
|
# ES-module graphs the refactor produces. Exemptions: docs/size-exemptions.md.
|
|
name: lint
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
# npm ci runs third-party postinstall scripts; don't leave the token in
|
|
# git config for them (this job never pushes).
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: '20'
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
- name: ESLint (size norm + module hygiene)
|
|
run: npm run lint
|