Skip to content

chore(ci): contribute 1.7.14 release-toolchain follow-up (v-prefixed release tags + HACS validation resilience + 1.7.14.1 stamp)#206

Merged
jleinenbach merged 5 commits into
BSkando:1.7from
jleinenbach:1.7
Jul 18, 2026
Merged

chore(ci): contribute 1.7.14 release-toolchain follow-up (v-prefixed release tags + HACS validation resilience + 1.7.14.1 stamp)#206
jleinenbach merged 5 commits into
BSkando:1.7from
jleinenbach:1.7

Conversation

@jleinenbach

Copy link
Copy Markdown
Collaborator

Summary

Small follow-up to #205 (the 1.7.13 -> 1.7.14 contribution). This bundles the three commits the jleinenbach fork's 1.7 line has accumulated since #205 merged, all CI/release-toolchain plus one version stamp -- no runtime behaviour change for Home Assistant users.

GitHub shows a clean diff over these three commits (the two 1.7 branches share the #205 merge base). The only custom_components/**/*.py change is the version literal in const.py (1.7.14.0 -> 1.7.14.1); everything else is .github/workflows/**, script/stamp_version.py, pyproject.toml, and tests.

  • Type: build (release/CI toolchain) + test
  • Scope/Area: release tag stamping, HACS CI validation resilience, version stamp
  • Linked fork PRs: #1205, #1206 (plus the 1.7.14.1 stamp commit)

Note for reviewers: the two 1.7 branches share a merge base (the merged #205 tip), so this is a clean 3-commit diff, not a full tree diff. There are no reverts and no user-facing file deletions. Every bundled fork PR was individually reviewed (Codex) and CI-green on the fork before merge.


Motivation

Two toolchain gaps surfaced after #205, plus the routine version stamp:

  1. BSkando cuts releases with a v-prefixed tag (v1.7.14), but the fork's release-stamp automation rejected v-prefixed tags and only accepted the bare X.Y.Z form. On BSkando's v1.7.14 release this made the stamp step fail (that is the run you flagged). (#1205)
  2. HACS's CI validation started failing on an unchanged hacs.json -- an external, transient regression in hacs/action (upstream HACS manifest validation failing incorrectly hacs/integration#5252), not a defect in this repo. The CI job now tolerates exactly that transient signature without weakening real validation. (#1206)
  3. The routine 1.7.14.1 version stamp that keeps the three version literals in lockstep.

Change Log (human-readable)

Fixed

  • Accept v-prefixed release tags in release stamping (#1205, CI only). The release-stamp / release automation now strips a leading v at the workflow boundary, so BSkando's vX.Y.Z tag format (e.g. v1.7.14) drives the stamp correctly. The SSOT version regex is untouched; the v is normalised only where the tag enters the workflow. This is the fix for the failed stamp run on the v1.7.14 release.

Changed

  • Tolerate transient HACS validation regression (#1206, CI only). The HACS validation job now runs the primary check, and only on failure re-runs it once with the two known-transient checks (hacsjson, integration_manifest) isolated. Classification: primary green -> pass; primary red + isolated re-run green -> the two transient checks were the only failure (real file validity is independently covered by hassfest + the test job) -> tolerated with a warning; primary red + re-run still red -> a real defect -> the job fails. The isolation is not permanent (on healthy HACS the primary run passes all seven checks and the fallback is skipped). A mutation-sharp guard test locks the structure; a companion hacs.json field-type test makes the tolerance provably safe (rejects locally exactly the schema class HACS's hacsjson validator rejects). This is a resilience guard against the external HACS manifest validation failing incorrectly hacs/integration#5252 wave, not a change to hacs.json.

Changed (version stamp)

  • 1.7.14.1 version stamp (dev/CI). The three version literals (const.py / manifest.json / pyproject.toml) move 1.7.14.0 -> 1.7.14.1 in lockstep (invariant anchored in custom_components/googlefindmy/AGENTS.md).

Removed

  • Nothing user-facing. No files are deleted in this range.

Compatibility

  • No breaking changes, no runtime behaviour change. The only custom_components/**/*.py edit is the version literal. No schema change, no data migration.

Version notation (heads-up)

The fork emits a four-part PEP 440 version (1.7.14.1), so the on-disk files read 1.7.14.1. BSkando's release tag is the v-prefixed three-part form (v1.7.14). These two conventions are exactly why #1205 is needed: the stamp automation must accept the v1.7.14 tag while the code literal stays four-part. Branch and shipped HACS asset always carry the stamped value.


Tests

  • test_stamp_version.py extended for the v-prefix normalisation (#1205); test_hacs_validation.py extended with a mutation-sharp guard-structure test and a hacs.json field-type test (#1206). Key regressions are mutation-sharp (they turn red when the fix is reverted).
  • Provenance note (honest): this PR bundles already-merged, individually CI-green and Codex-reviewed fork commits; no new code was added in this bundling step.

A note on the HACS check on this PR

If GitHub shows the HACS validation as red on this PR, it is the external, transient hacs/integration#5252 regression (unchanged hacs.json, same Docker image digest green minutes earlier), not a defect introduced here -- and it is exactly what #1206 makes the fork's own CI resilient to. It is non-blocking. It tends to self-heal upstream within a couple of days.


Risk & Rollback

  • Risk level: low. CI/toolchain only plus a version stamp; no runtime footprint beyond the version literal.
  • Rollback: a full revert of the three-commit range restores the prior toolchain. No reverts, no user-facing deletions, no schema change to unwind.

github-actions Bot and others added 3 commits July 18, 2026 09:15
* fix(ci): accept v-prefixed release tags in release stamping

BSkando tags releases vX.Y.Z while jleinenbach tags X.Y.Z[.N]; the shared
release-stamp.yml guard rejected any leading `v`, so publishing v1.7.14 on
BSkando failed at "Validate published tag" and never ran the stamp, commit,
push or HACS ZIP upload. The published release shipped with no
googlefindmy.zip asset (broken HACS installs).

Strip an optional single leading `v` at the workflow boundary
(VERSION="${TAG_NAME#v}") and validate/stamp that v-less VERSION; the full
TAG_NAME (with any `v`) still addresses the git tag and release asset
(checkout ref, rev-list refs/tags, gh release upload). VERSION_RE stays
version-only, a version literal never carries a `v`. release.yml's REAL_TIP
gate strips `v` too so a genuine v-prefixed line tip is recognised as a
version rather than blanked as foreign.

Adds mutation-sharp tests for the tag->version mapping and locks the
REAL_TIP strip into the workflow; the byte-parity guard test is unaffected
(the regex pattern is unchanged, only its input variable).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* style(test): apply ruff format to test_stamp_version

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
) (#1206)

* ci: tolerate transient HACS validation regression (hacs/integration#5252)

The HACS validation job intermittently fails on hacsjson and
integration_manifest with "invalid 'hacs.json' file" / "Got None" while
the files are byte-identical to a green run and the action image digest is
unchanged. This is the known upstream regression hacs/integration#5252
(the action fetches the raw files as None from the GitHub contents API).

Run HACS first with continue-on-error; only on failure re-run it with
exactly those two transient-prone validators isolated. If everything else
passes, the failure was the known transient signature and is tolerated
(job green + warning); any other failing check still exits non-zero. The
isolation is not permanent (fallback runs only on failure). The real
validity of both files stays enforced independently: manifest.json by the
hassfest job (needs: hassfest) and hacs.json by the test job. ci-success
and release.yml are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(ci): harden HACS transient-guard test via YAML parsing

Address the terminal diff-review follow-ups (P2/P3): replace the
whitespace-fragile string search with structural YAML parsing of the
hacs job steps, and assert the continue-on-error placement explicitly
(primary + fallback have it, the classifier does not) so the guard
cannot regress into a blanket-ignore or a non-blocking classifier.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(hacs): validate hacs.json field types so transient guard cannot mask real schema errors

The ci.yml transient-HACS guard classifies a run 'transient' when only
hacsjson + integration_manifest fail, relying on the local test job to catch a
genuine hacs.json schema error. test_hacs_metadata_matches_manifest only checked
allowed keys, not their types, so a non-boolean zip_release/content_in_root could
slip through green (Codex #1206 P2). Add explicit field-type validation covering
the exact class HACS's hacsjson validator rejects; drop the duplicate path header.

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7114812858

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/ci.yml
Comment on lines +171 to +173
elif [ "$RETRY" = "success" ]; then
echo "::warning ::HACS validation failed only on the transient-prone checks (hacsjson, integration_manifest) and passed once they were isolated; treating as the known transient upstream regression (hacs/integration#5252). File validity is still enforced by the hassfest and test jobs."
echo "status=transient" >> "$GITHUB_OUTPUT"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require the exact HACS transient signature

When the first HACS run fails because of a real hacsjson or integration_manifest problem that hassfest/local tests do not mirror (for example a HACS-only manifest or hacs.json requirement), the fallback reruns with both validators ignored and this branch marks the job successful solely because the remaining checks pass. That turns genuine HACS metadata regressions into green CI, so the classifier should inspect the original failure signature/logs or otherwise reproduce all ignored validators locally before setting status=transient.

Useful? React with 👍 / 👎.

…ard cannot mask real regressions (#1207)

The ci.yml transient guard (#1206) ignores hacsjson + integration_manifest in
its fallback and tolerates the job as transient when the remaining checks pass.
test_hacs_metadata_field_types only reproduced hacs.json field *types*, so a
*missing* required key (which HACS also rejects) could slip through green.

Reproduce both ignored validators locally in the test job (the ci-success gate):
- test_hacs_metadata_requires_name: hacs.json must declare a non-empty name.
- test_manifest_satisfies_hacs_integration_contract: manifest.json must carry the
  six keys HACS's integration_manifest validator requires (domain, documentation,
  issue_tracker, name, version as non-empty strings; codeowners as a non-empty
  list of strings). Source: hacs.xyz/docs/publish/integration.

Closes the N2 residual of PLAN_GFMY_HACS_TRANSIENT_GUARD (Codex #206 P2).
@jleinenbach

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a58e74b578

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/ci.yml
uses: hacs/action@main
with:
category: integration
ignore: topics issues hacsjson integration_manifest

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use a documented HACS check name

When the upstream #5252 failure occurs, the first run reports both hacsjson and integration_manifest, but I checked the HACS Action docs for with.ignore: they document a space-separated list of ignorable checks and list archived, brands, description, hacsjson, images, information, issues, and topics, not integration_manifest. Because this retry relies on ignoring integration_manifest, the retry will still run that failing validator in the transient scenario and RETRY remains failure, so the classifier exits 1 instead of making CI resilient.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the careful read. I verified this against the HACS validation source (not the docs page), and integration_manifest is in fact a valid, effective ignore token, so the surgical retry does isolate it.

Chain of evidence (hacs/integration main, which is exactly what hacs/action@main runs via docker://ghcr.io/hacs/action:main):

  • The ignore filter keys off the check slug, and the slug is the check module basename:
    custom_components/hacs/validate/base.py:
    @property
    def slug(self) -> str:
        return self.__class__.__module__.rsplit(".", maxsplit=1)[-1]
  • The integration-manifest check lives in custom_components/hacs/validate/integration_manifest.py, so its slug is exactly integration_manifest.
  • The manager applies the ignore list uniformly over every validator by slug:
    custom_components/hacs/validate/manager.py:
    and validator.slug not in os.getenv("INPUT_IGNORE", "").split(" ")
  • GitHub Actions maps with.ignore to INPUT_IGNORE, so ignore: topics issues hacsjson integration_manifest yields INPUT_IGNORE="topics issues hacsjson integration_manifest", whose split contains integration_manifest, and the not in check excludes that validator from execution.

The list under https://hacs.xyz/docs/publish/action/ is an incomplete enumeration (it also omits license, which is likewise a real check module validate/license.py). The authoritative set of ignorable slugs is the set of module basenames under validate/, and integration_manifest is one of them. The #5252 error label "integration_manifest ... Got None" is that very slug.

So in the transient scenario the retry (ignore: ... hacsjson integration_manifest) skips both transient-prone validators; if only those two failed, hacs_surgical passes and the classifier reports transient (green with a warning). A failure on any other check keeps the retry red and the classifier exits 1. No change needed here.

@jleinenbach
jleinenbach merged commit 93787ad into BSkando:1.7 Jul 18, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant