6 Commits
Author SHA1 Message Date
flan 22e6499a8e Check the signed-in markup from a scheduled workflow
Validate / hassfest (push) Skipped
Validate / HACS (push) Skipped
Upstream compatibility / compat (push) Successful in 8s
Validate / pytest (push) Successful in 11s
Move the authenticated drift check into the repository as
tools/compat_auth.py, run daily by .github/workflows/compat-auth.yml on the
maintainer's forge only. Credentials come from FH_EMAIL and FH_PASSWORD, the
output is pass/fail labels only because the run log is public, and a failed
run pushes the report to ntfy. Exit 5 means all hold, 10 drift, anything
else that it could not run.

Point compat.yml, tools/compat.py and the docs at it, add a README section,
and cut 0.5.1.
2026-09-21 19:01:13 +00:00
flan e31f84e40b Run hassfest and HACS only where they can work
Validate / hassfest (push) Skipped
Validate / HACS (push) Skipped
Validate / pytest (push) Successful in 10s
Validate / hassfest (pull_request) Skipped
Validate / HACS (pull_request) Skipped
Validate / pytest (pull_request) Successful in 9s
Both jobs fail on the Gitea mirror for reasons no secret or input changes.

hassfest is a Docker-container action that bind-mounts $GITHUB_WORKSPACE. The
Gitea runner runs the job inside a container against a separate docker-in-docker
daemon, so the path resolves on the daemon's filesystem, not the job's; docker
creates an empty directory and mounts that, and hassfest then accurately reports
that it found no integrations in the empty tree it was given. It takes no token,
so credentials were never the issue.

HACS asks the github.com API about github.repository, which on Gitea is
flan/ha-freshharvest -- a slug that exists only on Gitea. That is the 401. The
action has no input to point the lookup at the sudolulo mirror.

The same commit that fails here passes on github.com/sudolulo/ha-freshharvest,
which mirrors every push, so the validation still happens -- it just happens
where it is capable of running. The guard is "not Gitea" rather than "is
GitHub" so an unexpected server_url runs the checks instead of dropping them.

pytest is untouched and still gates every push on both forges.
2026-08-04 14:22:06 +00:00
flan 70c67901b8 Make the README user-facing, and stop the compat job writing to a mirror
Upstream compatibility / compat (push) Successful in 7s
Validate / pytest (push) Successful in 7s
Validate / HACS (push) Failing after 16s
Validate / hassfest (push) Failing after 6s
The compat badge was red on bookkeeping, not on compatibility: the check passed
16/16 and the run then failed trying to commit the refreshed matrix and open a
PR. That step should never have existed here — GitHub is a read-only mirror of
Gitea, so anything a bot pushes is clobbered by the next sync. It now publishes
the matrix to the run summary and the workflow only needs contents:read, so the
badge means what it says.

The README had grown into an implementation document. Endpoints, markup traps,
the login handshake and the drift-detection design move to docs/internals.md;
what is left is installation, entities, events, the dashboard, requirements and
troubleshooting.
2026-08-03 21:10:01 +00:00
flan fa09ac6d5d Make CI green: install yarl, and give the repo topics
Validate / hassfest (push) Failing after 5s
Validate / pytest (push) Successful in 9s
Validate / HACS (push) Failing after 17s
pytest could not even collect: actions.py imports yarl for URL joining, which
Home Assistant ships but a bare CI python does not. The suite passed locally
only because the venv had picked it up as a transitive dependency.

The HACS check wants repository topics, which are GitHub-side metadata and so
cannot come from the canonical Gitea repo; set on the mirror directly.
2026-08-03 20:41:25 +00:00
flan 213c16d98e Add the write-action layer and a daily upstream compatibility check
Upstream compatibility / compat (push) Failing after 7s
Validate / hassfest (push) Failing after 25s
Validate / HACS (push) Failing after 17s
Validate / pytest (push) Successful in 9s
actions.py covers skip, donate, cart add/remove, subscriptions and vacation
holds. Every mutating endpoint on the site is guarded by rotating per-render
tokens, so each action re-derives them from a live page rather than storing
anything; skip additionally compares the date the server states in its
confirmation against the date it was asked to skip, and refuses on a mismatch.
All actions default to dry_run.

tools/compat.py records what the integration assumes about a site that offers
no API and no stability contract, and CI asserts it daily. Only the
unauthenticated surface is covered: checking the rest would mean putting a
personal account password in public repo secrets.
2026-08-03 20:02:58 +00:00
flan 3e880ff2c1 Expose every order component as its own entity and prepare for release
Validate / hassfest (push) Failing after 23s
Validate / pytest (push) Successful in 9s
Validate / HACS (push) Failing after 1m21s
Each cost line is now an entity rather than an attribute: subtotal, box price,
add-ons, tax and delivery fee, plus a total and free-delivery-remaining for the
open order, a delivery-day sensor, and a binary sensor that turns off at the
cutoff. Entities share a base class and declare a scope, so a description
states only the field it reads.

Also parses the driver tip, Bounty savings and the free-delivery threshold. The
threshold is carried across orders because the progress bar only renders on
carts that have not met it.

Adds LICENSE, hacs.json, a CI workflow, a pre-publish audit script, and a test
that cross-checks every entity's translation_key against both translation
files. Manifest URLs now point at GitHub rather than a private forge.
2026-08-03 19:32:50 +00:00