From da52667475ffa9794b8aa94790ce51b57c2b55e0 Mon Sep 17 00:00:00 2001 From: FMSMITH91 <12152698+FMSMITH91@users.noreply.github.com> Date: Sat, 8 Aug 2026 13:00:53 -0500 Subject: [PATCH 1/2] ci: measure and publish coverage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codacy carries a 60% coverage goal that has always read "not reported", because nothing ever produced a report. Measured: 51% overall. A separate, deliberately non-blocking job. It re-runs the four suites under tracing, which is slower, and the gate job should stay the fast answer — so this one only measures and never decides pass/fail. The summary lands in the job summary and coverage.xml/coverage.txt upload as artifacts. Where the misses are, for anyone reading the number: app.py 43% and ssh_manager.py 55% are mostly install/bootstrap and remote-management paths that need a real host to exercise, which is why they are low and why chasing the percentage for its own sake would be the wrong instinct. manage.py is 0% — the break-glass CLI a locked-out admin uses, and a genuine gap. auth.py 87% and models.py 90% are where this session's tests landed. Codacy upload needs a CODACY_PROJECT_TOKEN this repo does not have, so the step is present but commented out with the instructions rather than half-wired into a job that would fail on the missing secret. Verified by running the job's exact commands locally: 4 suites traced, combined, valid Cobertura XML out. Co-Authored-By: Claude Opus 5 --- .github/workflows/ci.yml | 52 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f750da7d..c604563d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,3 +62,55 @@ jobs: # Same script developers run locally via ./tools/run-tests.sh, so green here == green there. # shellcheck is pre-installed on the ubuntu runners. run: PYTHON=python bash tools/run-tests.sh + + coverage: + name: coverage + runs-on: ubuntu-latest + # Informational, and deliberately a SEPARATE job: it re-runs the suites under tracing, which is + # slower, and the gate above should stay the fast answer. Codacy has a 60% coverage goal that + # has always read as "not reported" because nothing ever produced a report. + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.12" + + - name: Install dependencies + run: python -m pip install --quiet -r requirements.txt coverage + + - name: Measure + # --parallel-mode + combine: each suite is its own process, and smoke/rbac fork threads. + # || true on the suites themselves — the gate job decides pass/fail, this one only measures. + run: | + for suite in unit template_actions smoke rbac; do + rm -f data/panel.db data/panel.db-shm data/panel.db-wal data/panel.db.backup + python -m coverage run --parallel-mode --source=. \ + --omit="./tests/*,./tools/*,./.venv/*" "tests/${suite}_test.py" >/dev/null 2>&1 || true + done + python -m coverage combine + python -m coverage xml -o coverage.xml + python -m coverage report --sort=miss > coverage.txt + { + echo '### Coverage' + echo '' + echo '```' + tail -n 20 coverage.txt + echo '```' + } >> "$GITHUB_STEP_SUMMARY" + + - name: Upload the report + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: coverage + path: | + coverage.xml + coverage.txt + + # Codacy's coverage goal only lights up once a report is uploaded, which needs a project + # token this repo does not have. To enable: add CODACY_PROJECT_TOKEN as a repository secret + # and uncomment. Left off rather than half-wired, so the job never fails on a missing secret. + # - name: Send to Codacy + # env: + # CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }} + # run: bash <(curl -Ls https://coverage.codacy.com/get.sh) report -r coverage.xml From de02bc41ef51756dd921bb3b6a4ee62b3939c336 Mon Sep 17 00:00:00 2001 From: FMSMITH91 <12152698+FMSMITH91@users.noreply.github.com> Date: Sat, 8 Aug 2026 13:10:31 -0500 Subject: [PATCH 2/2] perf: every page's script becomes a cacheable file, not inline markup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The base.html extraction covered the SHARED bundle; each page still inlined its own, re-sent on every visit and cacheable by nobody. 306KB across seven templates, now seven content-hashed static files behind the same week-long Cache-Control. remote_manage 86.6K server_detail 48.0K server_files 45.3K manage_remotes 32.0K dashboard 26.3K manage_servers 16.7K remote_firewall 9.0K First load, gzipped over the wire, at 5 servers: / 19.3 -> 11.0 KB /server/1 25.4 -> 11.0 KB /server/1/files 24.1 -> 11.5 KB /remotes 17.7 -> 10.5 KB and on any later visit the script is not sent at all. Three pages were pure lift-and-shift: dashboard, manage_servers and manage_remotes had no template syntax in their scripts whatsoever. The other four hold between one and seven server-rendered values, all of them top-level `var X = …;` — verified at brace depth 0 before splitting, because a var inside an IIFE would have stopped being a global the moment it moved. Those lines stay inline ahead of the file, exactly as base.html's do. remote_manage's trailing UPro.load(...) call stays inline AFTER it, since it calls into the code the file defines. The guard added last change earned itself immediately: my extractor wrote the island comments INSIDE the script elements, and "no Jinja tags in an inline + {% endblock %} diff --git a/templates/manage_remotes.html b/templates/manage_remotes.html index 638374f7..0b59b30d 100644 --- a/templates/manage_remotes.html +++ b/templates/manage_remotes.html @@ -248,617 +248,5 @@

{% endfor %} - + {% endblock %} \ No newline at end of file diff --git a/templates/manage_servers.html b/templates/manage_servers.html index e405420a..fa163669 100644 --- a/templates/manage_servers.html +++ b/templates/manage_servers.html @@ -255,316 +255,7 @@

Manage Game S {% endblock %} {% block scripts %} - + + {% endblock %} diff --git a/templates/remote_manage.html b/templates/remote_manage.html index 5505ed48..9b2fc119 100644 --- a/templates/remote_manage.html +++ b/templates/remote_manage.html @@ -732,1319 +732,15 @@

{{ server.name }}

{% block scripts %} +{# Server-rendered values for server_detail.js. Kept inline (and BEFORE it, so its top-level code can + read them) while the rest of the page script becomes a cacheable file. #} + {% endblock %} diff --git a/templates/server_files.html b/templates/server_files.html index 879e8253..92f53be3 100644 --- a/templates/server_files.html +++ b/templates/server_files.html @@ -290,725 +290,10 @@

Files & {% endblock %} {% block scripts %} +{# Server-rendered values for server_files.js. Kept inline (and BEFORE it, so its top-level code can + read them) while the rest of the page script becomes a cacheable file. #} + {% endblock %} diff --git a/tests/smoke_test.py b/tests/smoke_test.py index ac63813b..e119271d 100644 --- a/tests/smoke_test.py +++ b/tests/smoke_test.py @@ -1262,18 +1262,21 @@ def _detail_panels(html): c.post("/api/account/ui-order", json={"panels": {"detail_console": ["controls", "console", "players"]}, "hidden": {"detail_console": ["controls"]}}) _no_ctrl = c.get("/server/%d" % gs_id).get_data(as_text=True) + # The page's own script is a cacheable file now, so the JS assertions below have to follow the + # reference. The markup check above stays on the HTML alone — that is what it is about. + _no_ctrl_js = page_with_assets(c, "/server/%d" % gs_id) check("detail: the server page offers the same edit-mode toggle", 'data-action="toggleLayoutEdit"' in _det) check("detail: hiding Controls removes the stats canvas", 'id="stats-chart"' not in _no_ctrl) check("detail: initChart is guarded against the missing canvas", - "var canvas = document.getElementById('stats-chart');\n if (!canvas) return;" in _no_ctrl) + "var canvas = document.getElementById('stats-chart');\n if (!canvas) return;" in _no_ctrl_js) # "initChart();" (the CALL) — "function initChart() {" is a different string, so this anchors on # the bootstrap, not the definition. check("detail: the undo-a-hide handlers are defined BEFORE the bootstrap calls", - "initChart();" in _no_ctrl - and _no_ctrl.index("window.showDetailPanel = function") < _no_ctrl.index("initChart();"), + "initChart();" in _no_ctrl_js + and _no_ctrl_js.index("window.showDetailPanel = function") < _no_ctrl_js.index("initChart();"), "showDetailPanel at %s, initChart() call at %s" - % (_no_ctrl.find("window.showDetailPanel = function"), _no_ctrl.find("initChart();"))) + % (_no_ctrl_js.find("window.showDetailPanel = function"), _no_ctrl_js.find("initChart();"))) c.post("/api/account/ui-order", json={"hidden": {"detail_console": []}}) # Each page only knows its OWN regions, so the endpoint must merge rather than replace the map. # Before this was fixed, saving on the dashboard deleted the server page's layout and vice versa.