From 6e3399322ac1f41a7942821e04b056eb908eeb9d Mon Sep 17 00:00:00 2001 From: CI-12 dev agent <1+admin@noreply.localhost> Date: Fri, 25 Sep 2026 01:57:04 +0200 Subject: [PATCH 1/8] CI-12: CI grades test_freeze.py against the real hwsim at a pinned admin/goodwe - .gitea/infra-pin: the one place the admin/goodwe commit is pinned. - checks.yml: checks out admin/goodwe at the pin as the sibling test_freeze.py looks for; any failure fails the job. Tests run with GOODWE_REQUIRE_HWSIM=1, and `CI: ` lines are echoed on PASS so the log shows which transport ran. - test_freeze.py: GOODWE_REQUIRE_HWSIM refuses the stub; a local stub run prints a STUB USED line. - infra-pin.yml: a separate, non-required check that goes red naming both SHAs when admin/goodwe master has moved past the pin. - README: when and how to bump the pin. Co-Authored-By: Claude Opus 5.5 --- .gitea/infra-pin | 2 ++ .gitea/workflows/checks.yml | 36 +++++++++++++++++++++--------- .gitea/workflows/infra-pin.yml | 38 ++++++++++++++++++++++++++++++++ README.md | 5 +++++ goodwe_controller/test_freeze.py | 15 +++++++++++-- 5 files changed, 83 insertions(+), 13 deletions(-) create mode 100644 .gitea/infra-pin create mode 100644 .gitea/workflows/infra-pin.yml diff --git a/.gitea/infra-pin b/.gitea/infra-pin new file mode 100644 index 0000000..97dc92a --- /dev/null +++ b/.gitea/infra-pin @@ -0,0 +1,2 @@ +# The admin/goodwe commit CI tests against. Bump: set to the full SHA of admin/goodwe master HEAD (README.md, "CI and the infra pin"). +a783602ea131d49bc0c904333c1ccb101aac02d0 diff --git a/.gitea/workflows/checks.yml b/.gitea/workflows/checks.yml index 44709c0..56427fd 100644 --- a/.gitea/workflows/checks.yml +++ b/.gitea/workflows/checks.yml @@ -488,6 +488,26 @@ jobs: fi echo "OK: the \`on:\` block of the merged checks.yml carries no event filter." + # CI-12: the infra repo at the ONE pinned commit in .gitea/infra-pin (read + # from the merged tree), as the sibling test_freeze.py looks for. Any + # failure here fails the job; the tests step below refuses the stub. + - name: check out admin/goodwe at the pin in .gitea/infra-pin + env: + URL: ${{ github.server_url }}/admin/goodwe.git + TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -eu + PIN=$(grep -v '^#' "$WS/goodwe-addon/.gitea/infra-pin" | tr -d '[:space:]') + echo "$PIN" | grep -Eq '^[0-9a-f]{40}$' || { echo "FAIL: .gitea/infra-pin holds '$PIN', not a full 40-hex SHA."; exit 1; } + rm -rf "$WS/goodwe" && mkdir -p "$WS/goodwe" && cd "$WS/goodwe" + git init -q . + git remote add origin "$(echo "$URL" | sed "s#://#://x-access-token:${TOKEN}@#")" + git fetch -q origin "$PIN" || { echo "FAIL: could not fetch admin/goodwe at $PIN."; exit 1; } + git -c advice.detachedHead=false checkout -qf FETCH_HEAD + test "$(git rev-parse HEAD)" = "$PIN" + test -f sim/hwsim.py || { echo "FAIL: admin/goodwe@$PIN has no sim/hwsim.py."; exit 1; } + echo "infra: admin/goodwe @ $(git log --oneline -1)" + - name: build the test image (shipped deps, from apk - see .gitea/ci/Dockerfile) run: | set -eu @@ -508,16 +528,9 @@ jobs: # shorter timeout - a 60 s limit was tried during CI-01 and reported # exactly the false hang the docstring predicts. # - # ⚠️ WHAT THIS RUN DOES NOT COVER, stated because a pass here would - # otherwise over-claim. test_freeze.py's third section wants the infra - # repo's `sim/hwsim.py` to drive a real HTTP meter through the real - # transport. admin/goodwe is PRIVATE (checked via the API) and this - # workflow's GITHUB_TOKEN is minted for THIS repository only, so hwsim is - # absent here and `find_hwsim()` returns None. The rest of the file runs - # and passes. It does so SILENTLY - the file prints nothing to say the - # hwsim section was skipped - so "test_freeze.py: PASS" below is a weaker - # claim than it looks. That belongs to whoever owns test_freeze.py next; - # naming it here is the least this gate can do about it. + # test_freeze.py part 3 runs against the real `sim/hwsim.py` from the + # checkout above; GOODWE_REQUIRE_HWSIM=1 makes it fail rather than use its + # stub (CI-12). Lines a test prints with a `CI: ` prefix are echoed on PASS. # ⚠️ PYTHONDONTWRITEBYTECODE=1 IS BELT AND BRACES, NOT A FIX FOR A LIVE # HOLE. TEST-15 measured this runner's actual exposure and found none: the # checkout below is bind mounted `:ro`, so the interpreter physically @@ -533,7 +546,7 @@ jobs: - name: the add-on's own tests run: | set -eu - docker run --rm -e PYTHONDONTWRITEBYTECODE=1 -v "$WS:/w:ro" goodwe-addon-checks:1 sh -eu -c ' + docker run --rm -e PYTHONDONTWRITEBYTECODE=1 -e GOODWE_REQUIRE_HWSIM=1 -v "$WS:/w:ro" goodwe-addon-checks:1 sh -eu -c ' cd /w/goodwe-addon/goodwe_controller set -- test_*.py if [ "$1" = "test_*.py" ]; then @@ -546,6 +559,7 @@ jobs: start=$(date +%s) if python3 "$f" > /tmp/out 2>&1; then echo "PASS $f ($(( $(date +%s) - start ))s)" + grep "^CI: " /tmp/out || true else echo "FAIL $f ($(( $(date +%s) - start ))s)" tail -40 /tmp/out diff --git a/.gitea/workflows/infra-pin.yml b/.gitea/workflows/infra-pin.yml new file mode 100644 index 0000000..5d4e4e9 --- /dev/null +++ b/.gitea/workflows/infra-pin.yml @@ -0,0 +1,38 @@ +# CI-12: goes red when admin/goodwe master has moved past .gitea/infra-pin, so +# the pin cannot go stale silently. Deliberately NOT a required check: it +# reports; `checks / unit` stays the only gate. How to bump: README.md, +# "CI and the infra pin". +name: infra-pin + +on: + pull_request: + push: + branches: [release/1.0] + +jobs: + fresh: + runs-on: [self-hosted, goodwe-rig] + timeout-minutes: 30 # seconds of work; the rest is queueing for the shared slot + steps: + - name: compare .gitea/infra-pin with admin/goodwe master + env: + SELF: ${{ github.server_url }}/${{ github.repository }}.git + INFRA: ${{ github.server_url }}/admin/goodwe.git + TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -eu + auth() { echo "$1" | sed "s#://#://x-access-token:${TOKEN}@#"; } + D=$(mktemp -d) && cd "$D" + git init -q . && git remote add origin "$(auth "$SELF")" + # The pin of the commit this event is about (the PR head, or the push). + git fetch -q --depth 1 origin "$GITHUB_SHA" + PIN=$(git show FETCH_HEAD:.gitea/infra-pin | grep -v '^#' | tr -d '[:space:]') + HEAD=$(git ls-remote "$(auth "$INFRA")" refs/heads/master | cut -f1) + cd / && rm -rf "$D" + test -n "$HEAD" || { echo "FAIL: could not read admin/goodwe master."; exit 1; } + if [ "$PIN" != "$HEAD" ]; then + echo "STALE: .gitea/infra-pin is $PIN but admin/goodwe master is $HEAD." + echo " Test against $HEAD and bump the pin (README.md, \"CI and the infra pin\")." + exit 1 + fi + echo "OK: .gitea/infra-pin $PIN is admin/goodwe master HEAD." diff --git a/README.md b/README.md index c341fb3..d2a9799 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,11 @@ has the mechanism, the measurement, and what the flags do not do. They are not u each assertion corresponds to a rule whose absence produced an observed failure on real hardware, and the comments in `control.py` say which. +### CI and the infra pin + +CI (`.gitea/workflows/checks.yml`) runs every `test_*.py`, with `admin/goodwe` checked out next to this repo at the commit in `.gitea/infra-pin`, so `test_freeze.py` grades against the real `sim/hwsim.py`. If that checkout fails, the job fails. The local stub is used only in local runs, and it prints `STUB USED` when it is. +**Bump the pin** whenever `infra-pin / fresh` is red, which happens once `admin/goodwe` master has moved past the pin. Set `.gitea/infra-pin` to the new master SHA (`git ls-remote /admin/goodwe.git refs/heads/master`) in a PR, and let `checks / unit` grade the tests against it. + ## Compatibility **Check the serial number, not the model name.** Characters 6-8 of the serial are diff --git a/goodwe_controller/test_freeze.py b/goodwe_controller/test_freeze.py index 63f30e3..a5e0f00 100644 --- a/goodwe_controller/test_freeze.py +++ b/goodwe_controller/test_freeze.py @@ -19,7 +19,8 @@ shipped `meter_freeze_s` itself: a check that calls the predicate directly would not be testing the thing that ships. Uses the rig's own `sim/hwsim.py --fault freeze` when the infra repo is next to this one (or `$GOODWE_SIM`), and an equivalent local stub - otherwise. Both are exercised; the run says which one it used. + otherwise. Both are exercised; the run says which one it used. CI sets + `GOODWE_REQUIRE_HWSIM=1`, which refuses the stub (CI-12). 4. THE WIRING: the real `Controller.cycle()`, asserted on the service call that leaves the process; @@ -600,6 +601,15 @@ hw = find_hwsim() # that cannot fail, from the other end. PART3_TRANSPORT = f"the rig hwsim at {hw}" if hw else "the local stub" print(f"frozen meter over HTTP - using {PART3_TRANSPORT}") +# CI-12: CI sets GOODWE_REQUIRE_HWSIM=1, so a missing infra checkout fails here +# instead of passing on the stub. Local runs may still use the stub, and say so. +if not hw: + if os.environ.get("GOODWE_REQUIRE_HWSIM"): + print("FAIL: GOODWE_REQUIRE_HWSIM is set but sim/hwsim.py was not found " + "(sibling ../goodwe or $GOODWE_SIM) - refusing to fall back to the stub") + sys.exit(1) + print("STUB USED: part 3 ran against the local stub, NOT the infra repo's " + "sim/hwsim.py - not a CI-grade result") if hw: def argv_for(mode): return [HWSIM_DRIVER, "8791" if mode == "freeze" else "8792", mode, @@ -1263,7 +1273,8 @@ check("a Controller built with the key absent lands on the SHIPPED hold, so " _ctl_c.freeze_wd.cfg.hold_s == SHIPPED_HOLD_S) print() -print(f"part 3 (the live HTTP transport) ran against: {PART3_TRANSPORT}") +# `CI: ` lines are echoed into the CI log even on a pass (checks.yml). +print(f"CI: test_freeze part 3 (the live HTTP transport) ran against: {PART3_TRANSPORT}") if fails: print(f"{len(fails)} FAILED: {', '.join(fails)}") sys.exit(1) -- 2.54.0 From fe8004723584edd15e05e76ba8549172ab2e3470 Mon Sep 17 00:00:00 2001 From: glenn schrooyen Date: Fri, 25 Sep 2026 16:08:55 +0200 Subject: [PATCH 2/8] CI-12 round 2: INFRA_READ_TOKEN for admin/goodwe; the pin tracks release/1.0 - checks.yml's infra checkout and infra-pin.yml's admin/goodwe ls-remote use the INFRA_READ_TOKEN secret; the self fetch keeps GITHUB_TOKEN. - infra-pin.yml compares against admin/goodwe refs/heads/release/1.0; STALE message, comments and README follow. - test_control.py / test_alloc.py: drop the claim that CI cannot reach admin/goodwe. - .gitea/infra-pin is DELIBERATELY one release/1.0 merge behind (d84ac01) for the "infra-pin / fresh goes red" proof; the next commit sets it to HEAD. Co-Authored-By: Claude Opus 5.5 --- .gitea/infra-pin | 4 ++-- .gitea/workflows/checks.yml | 3 ++- .gitea/workflows/infra-pin.yml | 18 ++++++++++-------- README.md | 3 ++- goodwe_controller/test_alloc.py | 8 +++----- goodwe_controller/test_control.py | 12 +++++------- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/.gitea/infra-pin b/.gitea/infra-pin index 97dc92a..be5adc0 100644 --- a/.gitea/infra-pin +++ b/.gitea/infra-pin @@ -1,2 +1,2 @@ -# The admin/goodwe commit CI tests against. Bump: set to the full SHA of admin/goodwe master HEAD (README.md, "CI and the infra pin"). -a783602ea131d49bc0c904333c1ccb101aac02d0 +# The admin/goodwe commit CI tests against. Bump: set to the full SHA of admin/goodwe release/1.0 HEAD (README.md, "CI and the infra pin"). +d84ac01b09c53af5de8029c1125bd4d4cfc7b3cd diff --git a/.gitea/workflows/checks.yml b/.gitea/workflows/checks.yml index 56427fd..70b15f5 100644 --- a/.gitea/workflows/checks.yml +++ b/.gitea/workflows/checks.yml @@ -494,7 +494,8 @@ jobs: - name: check out admin/goodwe at the pin in .gitea/infra-pin env: URL: ${{ github.server_url }}/admin/goodwe.git - TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Read access to admin/goodwe only; this job's own token cannot see it. + TOKEN: ${{ secrets.INFRA_READ_TOKEN }} run: | set -eu PIN=$(grep -v '^#' "$WS/goodwe-addon/.gitea/infra-pin" | tr -d '[:space:]') diff --git a/.gitea/workflows/infra-pin.yml b/.gitea/workflows/infra-pin.yml index 5d4e4e9..8c2dafb 100644 --- a/.gitea/workflows/infra-pin.yml +++ b/.gitea/workflows/infra-pin.yml @@ -1,5 +1,6 @@ -# CI-12: goes red when admin/goodwe master has moved past .gitea/infra-pin, so -# the pin cannot go stale silently. Deliberately NOT a required check: it +# CI-12: goes red when admin/goodwe release/1.0 has moved past .gitea/infra-pin. +# It runs on add-on activity only; admin/goodwe's own `addon-pin` workflow +# covers a move of release/1.0 there. Deliberately NOT a required check: it # reports; `checks / unit` stays the only gate. How to bump: README.md, # "CI and the infra pin". name: infra-pin @@ -14,25 +15,26 @@ jobs: runs-on: [self-hosted, goodwe-rig] timeout-minutes: 30 # seconds of work; the rest is queueing for the shared slot steps: - - name: compare .gitea/infra-pin with admin/goodwe master + - name: compare .gitea/infra-pin with admin/goodwe release/1.0 env: SELF: ${{ github.server_url }}/${{ github.repository }}.git INFRA: ${{ github.server_url }}/admin/goodwe.git TOKEN: ${{ secrets.GITHUB_TOKEN }} + INFRA_TOKEN: ${{ secrets.INFRA_READ_TOKEN }} # admin/goodwe is private run: | set -eu - auth() { echo "$1" | sed "s#://#://x-access-token:${TOKEN}@#"; } + auth() { echo "$1" | sed "s#://#://x-access-token:${2:-$TOKEN}@#"; } D=$(mktemp -d) && cd "$D" git init -q . && git remote add origin "$(auth "$SELF")" # The pin of the commit this event is about (the PR head, or the push). git fetch -q --depth 1 origin "$GITHUB_SHA" PIN=$(git show FETCH_HEAD:.gitea/infra-pin | grep -v '^#' | tr -d '[:space:]') - HEAD=$(git ls-remote "$(auth "$INFRA")" refs/heads/master | cut -f1) + HEAD=$(git ls-remote "$(auth "$INFRA" "$INFRA_TOKEN")" refs/heads/release/1.0 | cut -f1) cd / && rm -rf "$D" - test -n "$HEAD" || { echo "FAIL: could not read admin/goodwe master."; exit 1; } + test -n "$HEAD" || { echo "FAIL: could not read admin/goodwe release/1.0."; exit 1; } if [ "$PIN" != "$HEAD" ]; then - echo "STALE: .gitea/infra-pin is $PIN but admin/goodwe master is $HEAD." + echo "STALE: .gitea/infra-pin is $PIN but admin/goodwe release/1.0 is $HEAD." echo " Test against $HEAD and bump the pin (README.md, \"CI and the infra pin\")." exit 1 fi - echo "OK: .gitea/infra-pin $PIN is admin/goodwe master HEAD." + echo "OK: .gitea/infra-pin $PIN is admin/goodwe release/1.0 HEAD." diff --git a/README.md b/README.md index d2a9799..2004b50 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,8 @@ on real hardware, and the comments in `control.py` say which. ### CI and the infra pin CI (`.gitea/workflows/checks.yml`) runs every `test_*.py`, with `admin/goodwe` checked out next to this repo at the commit in `.gitea/infra-pin`, so `test_freeze.py` grades against the real `sim/hwsim.py`. If that checkout fails, the job fails. The local stub is used only in local runs, and it prints `STUB USED` when it is. -**Bump the pin** whenever `infra-pin / fresh` is red, which happens once `admin/goodwe` master has moved past the pin. Set `.gitea/infra-pin` to the new master SHA (`git ls-remote /admin/goodwe.git refs/heads/master`) in a PR, and let `checks / unit` grade the tests against it. +The pin tracks `admin/goodwe` **release/1.0**, not master. Two non-required checks go red when release/1.0 has moved past the pin: `infra-pin / fresh` here, on any add-on PR or push to release/1.0, and `addon-pin / fresh` in `admin/goodwe`, on every push to its release/1.0. +**Bump the pin** whenever either is red. Set `.gitea/infra-pin` to the new release/1.0 SHA (`git ls-remote /admin/goodwe.git refs/heads/release/1.0`) in a PR, and let `checks / unit` grade the tests against it. CI reads `admin/goodwe` with the Actions secret `INFRA_READ_TOKEN`. ## Compatibility diff --git a/goodwe_controller/test_alloc.py b/goodwe_controller/test_alloc.py index 517d826..fcda789 100644 --- a/goodwe_controller/test_alloc.py +++ b/goodwe_controller/test_alloc.py @@ -10,12 +10,10 @@ IN FULL. ⚠️ WHAT THIS FILE CAN AND CANNOT CLAIM, SAID BEFORE THE FIRST CHECK. The two-master rig lives in admin/goodwe (TEST-19, `sim/sitesim.py`), where `compute()` closes the loop against `plantsim.Plant` with the firmware's own -encoder on the wire. That repository is PRIVATE and this repository's CI token -is minted for this one, so the rig is not reachable from the gate - and a test -file that needed it would either fail the gate or, like test_freeze.py's hwsim -section, pass SILENTLY having checked nothing. +encoder on the wire. CI checks admin/goodwe out at `.gitea/infra-pin` (CI-12), +but this file does not import the rig. -So the split is deliberate: +The split: * `tools/alloc_sim.py` drives the REAL rig, and its output is quoted in the pull request. It is where "no limit cycle" is a claim about the shipped diff --git a/goodwe_controller/test_control.py b/goodwe_controller/test_control.py index 46a3085..14cec8e 100644 --- a/goodwe_controller/test_control.py +++ b/goodwe_controller/test_control.py @@ -990,13 +990,11 @@ check("⭐ RESTORING slew_w 1000 AT max_w 5000 IS FASTER THAN THE PLANT, and by # # ⚠️ THE PLANT BELOW IS MODELLED HERE AND IS NOT THE RIG. admin/goodwe's # `plantsim.Plant` is the battery model this project grades control changes -# against, and this repository's CI cannot reach it - that repository is private -# and this job's token is minted for this one (see .gitea/workflows/checks.yml). -# So the rig evidence lives in `tools/slew_sim.py`, is quoted in the pull -# request and in DECISIONS/SAFETY-39-slew-against-the-rail.md, and what runs -# HERE is a first-order lag with the time constant that reproduces §5.10's -# measured ~18 s to the deadband. test_alloc.py makes the same split for the -# same reason and says so in the same words. +# against. CI checks admin/goodwe out at `.gitea/infra-pin` (CI-12), but this +# file does not import `plantsim`. The rig evidence lives in `tools/slew_sim.py`, +# is quoted in the pull request and in DECISIONS/SAFETY-39-slew-against-the-rail.md, +# and what runs HERE is a first-order lag with the time constant that reproduces +# §5.10's measured ~18 s to the deadband. test_alloc.py makes the same split. # # ⚠️ AND IT CANNOT SHOW A BATTERY ABOVE ITS RATING, WHICH IS SAID BEFORE THE # RESULT RATHER THAN AFTER IT. Neither this model nor `plantsim.Plant` lets the -- 2.54.0 From d0891a6bca65dfa03b60d563767a12a5e1609bb4 Mon Sep 17 00:00:00 2001 From: glenn schrooyen Date: Fri, 25 Sep 2026 16:10:47 +0200 Subject: [PATCH 3/8] CI-12: .gitea/infra-pin to admin/goodwe release/1.0 HEAD (56a933e), restoring the proof pin Co-Authored-By: Claude Opus 5.5 --- .gitea/infra-pin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/infra-pin b/.gitea/infra-pin index be5adc0..08db2c9 100644 --- a/.gitea/infra-pin +++ b/.gitea/infra-pin @@ -1,2 +1,2 @@ # The admin/goodwe commit CI tests against. Bump: set to the full SHA of admin/goodwe release/1.0 HEAD (README.md, "CI and the infra pin"). -d84ac01b09c53af5de8029c1125bd4d4cfc7b3cd +56a933e1466f3fa2d8a3ca5d12c9161d9f037195 -- 2.54.0 From c493a6d67ad9966e9f830cb5d3a4a560d53019ab Mon Sep 17 00:00:00 2001 From: glenn schrooyen Date: Fri, 25 Sep 2026 17:27:06 +0200 Subject: [PATCH 4/8] CI-12: test_tariff.py no longer says CI cannot see the infra repo Co-Authored-By: Claude Opus 5.5 --- goodwe_controller/test_tariff.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/goodwe_controller/test_tariff.py b/goodwe_controller/test_tariff.py index 9511151..4f40ea0 100644 --- a/goodwe_controller/test_tariff.py +++ b/goodwe_controller/test_tariff.py @@ -90,9 +90,9 @@ guard has to improve on, which is the useful thing a 2-day replay can say. The two days are committed here as `scenarios/p1-net-.jsonl.gz` (`{"t": iso8601, "w": signed_watts}` per line, derived from the HA recorder -export) for the reason `scenarios/p1-age-2026-08-26.jsonl.gz` already is: the -infra repo is private, CI's token is minted for this repository only, and a -regression fixture that CI cannot see is not a regression fixture. +export), as `scenarios/p1-age-2026-08-26.jsonl.gz` already is. When they were +committed, CI could not see the infra repo; since CI-12 it checks admin/goodwe +out at `.gitea/infra-pin`, but these fixtures stay here with the tests. The recorder's window rolls: 2026-08-01 leaves it around 2026-09-15, which is why the committed copy is the artefact and not the API call. -- 2.54.0 From 54cc5263fb535f770675fe39c297d6c8f4a767b3 Mon Sep 17 00:00:00 2001 From: glenn schrooyen Date: Fri, 25 Sep 2026 17:36:36 +0200 Subject: [PATCH 5/8] THROWAWAY (CI-12 proof 2): pin one release/1.0 merge behind (d84ac01); reverted next Co-Authored-By: Claude Opus 5.5 --- .gitea/infra-pin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/infra-pin b/.gitea/infra-pin index 08db2c9..be5adc0 100644 --- a/.gitea/infra-pin +++ b/.gitea/infra-pin @@ -1,2 +1,2 @@ # The admin/goodwe commit CI tests against. Bump: set to the full SHA of admin/goodwe release/1.0 HEAD (README.md, "CI and the infra pin"). -56a933e1466f3fa2d8a3ca5d12c9161d9f037195 +d84ac01b09c53af5de8029c1125bd4d4cfc7b3cd -- 2.54.0 From c63eda0b0f94c20807f8601920512b6b5a0ea75a Mon Sep 17 00:00:00 2001 From: glenn schrooyen Date: Fri, 25 Sep 2026 17:45:36 +0200 Subject: [PATCH 6/8] CI-12: restore .gitea/infra-pin to admin/goodwe release/1.0 HEAD (56a933e) This reverts commit 54cc5263fb535f770675fe39c297d6c8f4a767b3 (the proof-2 throwaway). Co-Authored-By: Claude Opus 5.5 --- .gitea/infra-pin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/infra-pin b/.gitea/infra-pin index be5adc0..08db2c9 100644 --- a/.gitea/infra-pin +++ b/.gitea/infra-pin @@ -1,2 +1,2 @@ # The admin/goodwe commit CI tests against. Bump: set to the full SHA of admin/goodwe release/1.0 HEAD (README.md, "CI and the infra pin"). -d84ac01b09c53af5de8029c1125bd4d4cfc7b3cd +56a933e1466f3fa2d8a3ca5d12c9161d9f037195 -- 2.54.0 From 841424463abecab129e1cf77728a49a897784246 Mon Sep 17 00:00:00 2001 From: glenn schrooyen Date: Fri, 25 Sep 2026 18:01:15 +0200 Subject: [PATCH 7/8] CI-12 review: INFRA_READ_TOKEN via an env-set header, not a remote URL The long-lived token no longer lands in $WS/goodwe/.git/config (mounted into the tests' container, left behind by a failed job) or in git's argv. A missing secret (fork PR) fails with its own message. README: addon-pin is not re-run after a bump. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LyR88rXETvwhTgx6yHE39p --- .gitea/workflows/checks.yml | 7 +++++-- .gitea/workflows/infra-pin.yml | 8 +++++--- README.md | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.gitea/workflows/checks.yml b/.gitea/workflows/checks.yml index 70b15f5..5db6bb5 100644 --- a/.gitea/workflows/checks.yml +++ b/.gitea/workflows/checks.yml @@ -500,10 +500,13 @@ jobs: set -eu PIN=$(grep -v '^#' "$WS/goodwe-addon/.gitea/infra-pin" | tr -d '[:space:]') echo "$PIN" | grep -Eq '^[0-9a-f]{40}$' || { echo "FAIL: .gitea/infra-pin holds '$PIN', not a full 40-hex SHA."; exit 1; } + test -n "$TOKEN" || { echo "FAIL: secret INFRA_READ_TOKEN is not available to this run (a fork PR?)."; exit 1; } rm -rf "$WS/goodwe" && mkdir -p "$WS/goodwe" && cd "$WS/goodwe" git init -q . - git remote add origin "$(echo "$URL" | sed "s#://#://x-access-token:${TOKEN}@#")" - git fetch -q origin "$PIN" || { echo "FAIL: could not fetch admin/goodwe at $PIN."; exit 1; } + # The token goes in a header via env: not in argv, and not in .git/config, + # which is mounted into the tests' container and outlives a failed job. + GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=http.extraHeader GIT_CONFIG_VALUE_0="Authorization: token $TOKEN" \ + git fetch -q "$URL" "$PIN" || { echo "FAIL: could not fetch admin/goodwe at $PIN."; exit 1; } git -c advice.detachedHead=false checkout -qf FETCH_HEAD test "$(git rev-parse HEAD)" = "$PIN" test -f sim/hwsim.py || { echo "FAIL: admin/goodwe@$PIN has no sim/hwsim.py."; exit 1; } diff --git a/.gitea/workflows/infra-pin.yml b/.gitea/workflows/infra-pin.yml index 8c2dafb..133ef90 100644 --- a/.gitea/workflows/infra-pin.yml +++ b/.gitea/workflows/infra-pin.yml @@ -23,13 +23,15 @@ jobs: INFRA_TOKEN: ${{ secrets.INFRA_READ_TOKEN }} # admin/goodwe is private run: | set -eu - auth() { echo "$1" | sed "s#://#://x-access-token:${2:-$TOKEN}@#"; } D=$(mktemp -d) && cd "$D" - git init -q . && git remote add origin "$(auth "$SELF")" + git init -q . && git remote add origin "$(echo "$SELF" | sed "s#://#://x-access-token:${TOKEN}@#")" # The pin of the commit this event is about (the PR head, or the push). git fetch -q --depth 1 origin "$GITHUB_SHA" PIN=$(git show FETCH_HEAD:.gitea/infra-pin | grep -v '^#' | tr -d '[:space:]') - HEAD=$(git ls-remote "$(auth "$INFRA" "$INFRA_TOKEN")" refs/heads/release/1.0 | cut -f1) + test -n "$INFRA_TOKEN" || { echo "FAIL: secret INFRA_READ_TOKEN is not available to this run (a fork PR?)."; exit 1; } + # Header via env, as in checks.yml: the token stays out of argv. + HEAD=$(GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=http.extraHeader GIT_CONFIG_VALUE_0="Authorization: token $INFRA_TOKEN" \ + git ls-remote "$INFRA" refs/heads/release/1.0 | cut -f1) cd / && rm -rf "$D" test -n "$HEAD" || { echo "FAIL: could not read admin/goodwe release/1.0."; exit 1; } if [ "$PIN" != "$HEAD" ]; then diff --git a/README.md b/README.md index 2004b50..53438fc 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ on real hardware, and the comments in `control.py` say which. ### CI and the infra pin CI (`.gitea/workflows/checks.yml`) runs every `test_*.py`, with `admin/goodwe` checked out next to this repo at the commit in `.gitea/infra-pin`, so `test_freeze.py` grades against the real `sim/hwsim.py`. If that checkout fails, the job fails. The local stub is used only in local runs, and it prints `STUB USED` when it is. -The pin tracks `admin/goodwe` **release/1.0**, not master. Two non-required checks go red when release/1.0 has moved past the pin: `infra-pin / fresh` here, on any add-on PR or push to release/1.0, and `addon-pin / fresh` in `admin/goodwe`, on every push to its release/1.0. +The pin tracks `admin/goodwe` **release/1.0**, not master. Two non-required checks go red when release/1.0 has moved past the pin: `infra-pin / fresh` here, on any add-on PR or push to release/1.0, and `addon-pin / fresh` in `admin/goodwe`, on every push to its release/1.0. Nothing re-runs `addon-pin` after a bump, so it stays red on that admin/goodwe commit; `infra-pin / fresh` green on the bump is the all-clear. **Bump the pin** whenever either is red. Set `.gitea/infra-pin` to the new release/1.0 SHA (`git ls-remote /admin/goodwe.git refs/heads/release/1.0`) in a PR, and let `checks / unit` grade the tests against it. CI reads `admin/goodwe` with the Actions secret `INFRA_READ_TOKEN`. ## Compatibility -- 2.54.0 From 8ff42ea85df53cb78171e4f2c74d7b4476f85a58 Mon Sep 17 00:00:00 2001 From: glenn schrooyen Date: Fri, 25 Sep 2026 18:10:28 +0200 Subject: [PATCH 8/8] CI-12: .gitea/infra-pin to admin/goodwe release/1.0 HEAD (2d026df, goodwe#56 merged) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LyR88rXETvwhTgx6yHE39p --- .gitea/infra-pin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/infra-pin b/.gitea/infra-pin index 08db2c9..fa4f56f 100644 --- a/.gitea/infra-pin +++ b/.gitea/infra-pin @@ -1,2 +1,2 @@ # The admin/goodwe commit CI tests against. Bump: set to the full SHA of admin/goodwe release/1.0 HEAD (README.md, "CI and the infra pin"). -56a933e1466f3fa2d8a3ca5d12c9161d9f037195 +2d026dfa7c425f462c6969b2f7cdfddc17b17a81 -- 2.54.0