From 0bc924512d8a8c47d57d2c05891454c1ee76c686 Mon Sep 17 00:00:00 2001 From: Zac Farrell Date: Tue, 9 Jun 2026 17:21:07 -0700 Subject: [PATCH 1/4] fix(ci): clean generated subtrees so removed endpoints don't linger --- .github/workflows/regenerate.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/regenerate.yml b/.github/workflows/regenerate.yml index 3794150..c479a50 100644 --- a/.github/workflows/regenerate.yml +++ b/.github/workflows/regenerate.yml @@ -44,8 +44,15 @@ jobs: https://api.github.com/repos/hotdata-dev/www.hotdata.dev/contents/api/openapi.yaml \ -o openapi.yaml - - name: Clean existing source - run: rm -rf src/ + # Remove the generator-owned subtrees before regenerating so endpoints and + # models dropped from the spec don't linger as orphaned files. The package + # lives in hotdata/, so the previous `rm -rf src/` was a no-op and removed + # APIs (e.g. the sandbox endpoints) kept shipping as dead modules. The + # hand-written modules (hotdata/_auth.py, hotdata/arrow.py) live at the + # package root and are untouched; the integration tests in tests/ (plural) + # are distinct from the generated test/ (singular) unit tests. + - name: Clean generated source + run: rm -rf hotdata/api hotdata/models docs test # pyproject.toml is hand-maintained (see .openapi-generator-ignore), so the # generator no longer stamps the version. Bump the patch version directly, From 692354ce75f8cb3afba4055a8aaed7dc9e13d0e7 Mon Sep 17 00:00:00 2001 From: Zac Farrell Date: Tue, 9 Jun 2026 17:21:23 -0700 Subject: [PATCH 2/4] ci: seed changelog entry on regen version bump --- .github/workflows/regenerate.yml | 37 ++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/.github/workflows/regenerate.yml b/.github/workflows/regenerate.yml index c479a50..d7cce05 100644 --- a/.github/workflows/regenerate.yml +++ b/.github/workflows/regenerate.yml @@ -78,6 +78,43 @@ jobs: ) echo "version=$version" >> "$GITHUB_OUTPUT" + # check-release.yml gates merges on a `## [x.y.z]` CHANGELOG section + # matching the bumped version, so without a seeded entry every regen PR + # fails that check. Insert a stub (the spec-change title under ### Changed) + # just above the most recent released section; the PR author refines the + # wording before merge. Idempotent: skips if a section for this version + # already exists. + - name: Seed changelog entry + env: + VERSION: ${{ steps.pkg.outputs.version }} + TITLE: ${{ inputs.title }} + run: | + export CHANGELOG_DATE=$(date -u +%Y-%m-%d) + python3 - <<'PY' + import os, pathlib, re + version = os.environ["VERSION"] + date = os.environ["CHANGELOG_DATE"] + title = (os.environ.get("TITLE") or "").strip() \ + or "Regenerate the client from the updated Hotdata OpenAPI spec" + path = pathlib.Path("CHANGELOG.md") + text = path.read_text() + if re.search(rf"^## \[{re.escape(version)}\]", text, re.M): + print(f"CHANGELOG already has a [{version}] section; leaving it untouched.") + raise SystemExit(0) + unreleased = re.search(r"^## \[Unreleased\]", text, re.M) + if not unreleased: + raise SystemExit("CHANGELOG.md has no '## [Unreleased]' section to anchor the new entry") + # Insert before the first released section after [Unreleased] (falling + # back to end of file) so any pending entries under [Unreleased] stay + # attributed to it rather than being absorbed by the new version. + nxt = re.search(r"^## \[", text[unreleased.end():], re.M) + insert_at = unreleased.end() + nxt.start() if nxt else len(text) + entry = f"## [{version}] - {date}\n\n### Changed\n\n- {title}\n\n" + text = text[:insert_at] + entry + text[insert_at:] + path.write_text(text) + print(f"Inserted CHANGELOG [{version}] section.") + PY + - name: Generate client run: | npx @openapitools/openapi-generator-cli generate \ From 315599900a1c2a279083edaa3a4ade92f33464a8 Mon Sep 17 00:00:00 2001 From: Zac Farrell Date: Tue, 9 Jun 2026 17:21:51 -0700 Subject: [PATCH 3/4] ci: don't gate regen PR on wheel build/import --- .github/workflows/regenerate.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/regenerate.yml b/.github/workflows/regenerate.yml index d7cce05..684f882 100644 --- a/.github/workflows/regenerate.yml +++ b/.github/workflows/regenerate.yml @@ -278,13 +278,13 @@ jobs: rm -f openapi.yaml rm -f .github/workflows/python.yml - - name: Verify built wheel installs and imports - run: | - python -m pip install --upgrade build - python -m build - python -m pip install dist/*.whl - # cd away from the source tree so the import resolves against the installed wheel. - cd /tmp && python -c "import hotdata; print(hotdata.__version__)" + # NOTE: regeneration deliberately does NOT build/import the package. A + # failure here used to abort the job before "Create PR", so a regen that + # produced valid-but-not-yet-wired output failed silently with no PR to act + # on. The PR is the artifact we want: integration-tests.yml installs the + # package (`pip install -e .`) and runs pytest on every PR to main, so any + # import/build breakage shows up as red CI on the filed PR (and auto-merge, + # gated on those checks, won't merge it) instead of a buried workflow run. - name: Check integration test scenario parity env: From 9539d8024a97598eaf90c0f61cf88a4b10700946 Mon Sep 17 00:00:00 2001 From: Zac Farrell Date: Tue, 9 Jun 2026 18:23:20 -0700 Subject: [PATCH 4/4] ci: verify wheel build/install/import in check-release --- .github/workflows/check-release.yml | 14 ++++++++++++++ .github/workflows/regenerate.yml | 9 +++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/.github/workflows/check-release.yml b/.github/workflows/check-release.yml index 00e1063..6f7a1a4 100644 --- a/.github/workflows/check-release.yml +++ b/.github/workflows/check-release.yml @@ -24,3 +24,17 @@ jobs: - name: Check release metadata run: python scripts/check-release.py + + # Build + install + import the wheel here rather than in the regen workflow. + # This job runs on any PR touching pyproject.toml/CHANGELOG.md — which every + # regen PR does (version bump + seeded changelog) — so a packaging or import + # regression surfaces as red CI on the PR instead of silently aborting the + # regen before it can open one. (Step name kept stable to preserve the + # required-check / branch-protection wiring.) + - name: Build, install, and import the wheel + run: | + python -m pip install --upgrade build + python -m build + python -m pip install dist/*.whl + # cd away from the source tree so the import resolves against the installed wheel. + cd /tmp && python -c "import hotdata; print(hotdata.__version__)" diff --git a/.github/workflows/regenerate.yml b/.github/workflows/regenerate.yml index 684f882..d4d45f5 100644 --- a/.github/workflows/regenerate.yml +++ b/.github/workflows/regenerate.yml @@ -281,10 +281,11 @@ jobs: # NOTE: regeneration deliberately does NOT build/import the package. A # failure here used to abort the job before "Create PR", so a regen that # produced valid-but-not-yet-wired output failed silently with no PR to act - # on. The PR is the artifact we want: integration-tests.yml installs the - # package (`pip install -e .`) and runs pytest on every PR to main, so any - # import/build breakage shows up as red CI on the filed PR (and auto-merge, - # gated on those checks, won't merge it) instead of a buried workflow run. + # on. The PR is the artifact we want, and breakage surfaces on it as red CI: + # integration-tests.yml installs the package (`pip install -e .`) and runs + # pytest, and check-release.yml builds + installs + imports the wheel on + # every PR that bumps pyproject.toml/CHANGELOG.md (which every regen PR + # does). Auto-merge is gated on those checks, so a broken regen can't merge. - name: Check integration test scenario parity env: