Skip to content

Add snapshot data support to category data generation#5424

Draft
ludeeus wants to merge 8 commits into
mainfrom
claude/hacs-data-preflight-job-dlfkg4
Draft

Add snapshot data support to category data generation#5424
ludeeus wants to merge 8 commits into
mainfrom
claude/hacs-data-preflight-job-dlfkg4

Conversation

@ludeeus

@ludeeus ludeeus commented Jul 23, 2026

Copy link
Copy Markdown
Member

Summary

This PR adds support for using pre-fetched snapshot data during category data generation, enabling faster and more reliable data generation workflows. When snapshot data is available, the generator uses it instead of fetching from the data client.

Key Changes

  • New helper functions in generate_category_data.py:

    • get_stored_data(): Returns category data from snapshot directory if available, otherwise fetches from data client
    • get_removed_repositories(): Returns removed repositories list from snapshot directory if available, otherwise fetches from data client
    • Both functions respect the HACS_EXISTING_DATA_DIR environment variable
  • GitHub Actions workflow updates (generate-hacs-data.yml):

    • Added new preflight job that fetches and caches existing published data before category generation
    • Updated category-data job to download the cached snapshot data and pass it via HACS_EXISTING_DATA_DIR environment variable
    • Improved matrix generation logic with better bash variable handling and JSON formatting
    • Updated job dependencies to include the new preflight job
  • Test coverage in test_generate_category_data.py:

    • Added _StubDataClient and _StubHacs test fixtures for mocking
    • Added tests verifying snapshot data is read from disk when available
    • Added tests verifying fallback to data client fetch when snapshot is unavailable

Implementation Details

  • The snapshot data directory is optional and controlled via the HACS_EXISTING_DATA_DIR environment variable
  • When the snapshot directory is set, file I/O is used to read pre-fetched data, avoiding redundant network requests
  • The implementation maintains backward compatibility - if the environment variable is not set, the original data client fetch behavior is preserved
  • The workflow now fetches data for all categories and the removed repositories list in a separate preflight job, making the data available to all subsequent category generation jobs

https://claude.ai/code/session_01L22jYdNGF4hB4fvTYPTzP4

claude added 4 commits July 23, 2026 17:06
The generate workflow fetched the existing published data from R2 inside
every category leg: each leg called HacsDataClient.get_data(<category>) for
the category's data.json, and get_repositories("removed") for the removed
list. Since the six legs run serially, the removed list was fetched once per
category, and a republish of R2 mid-run could leave later legs working off a
different baseline than earlier ones.

Fetch that data exactly once in a new lightweight preflight job (curl only)
and share it with every category leg via an artifact, so the whole run uses
one consistent snapshot:

- generate_category_data.py: add get_stored_data()/get_removed_repositories()
  helpers that read each category's data.json and the removed list from
  $HACS_EXISTING_DATA_DIR when set, and fall back to fetching from the data
  client when unset (tests, local dev, single-repo validate.yml). Re-point the
  two existing fetches at these helpers.
- generate-hacs-data.yml: add a preflight job that fetches every category's
  data.json plus the removed list into outputdata/existing (retrying 5 times
  then failing the run), uploads it as the existing-data artifact, and has
  category-data depend on it, download it, and run with
  HACS_EXISTING_DATA_DIR=outputdata/existing. Add preflight to
  notify_on_failure.needs.
- Add unit tests covering both helper paths (snapshot dir vs. fetch fallback).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L22jYdNGF4hB4fvTYPTzP4
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L22jYdNGF4hB4fvTYPTzP4
Build the generate-matrix categories output with jq so it is valid JSON
(double-quoted) instead of single-quoted, letting both the matrix (fromJSON)
and the preflight loop (jq -r) parse it robustly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L22jYdNGF4hB4fvTYPTzP4
Read HACS_EXISTING_DATA_DIR once at module level instead of keeping the env
var name in a constant and resolving it on every call.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L22jYdNGF4hB4fvTYPTzP4
Copilot AI review requested due to automatic review settings July 23, 2026 17:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR adds optional “snapshot” inputs to the category data generator so it can reuse already-published data from disk (when provided) instead of always fetching from the remote data client, and updates the GitHub Actions workflow to prefetch that snapshot once per run.

Changes:

  • Add get_stored_data() and get_removed_repositories() helpers to read pre-fetched JSON from HACS_EXISTING_DATA_DIR (with fallback to the data client).
  • Update the generator to use the new helpers for stored category data and removed repositories.
  • Extend CI with a preflight job to download published data into an artifact, then pass it to category jobs via HACS_EXISTING_DATA_DIR; add tests for the new helper behavior.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
scripts/data/generate_category_data.py Adds snapshot-aware helper functions and wires them into category generation.
tests/scripts/data/test_generate_category_data.py Adds tests/stubs verifying snapshot read behavior and fallback fetching.
.github/workflows/generate-hacs-data.yml Adds a preflight fetch job + artifact handoff and improves matrix JSON handling.
Comments suppressed due to low confidence (2)

scripts/data/generate_category_data.py:80

  • When HACS_EXISTING_DATA_DIR is set but removed.json is missing/invalid, this will raise and fail the whole run. To match the intended "use snapshot when available" behavior, fall back to fetching removed repositories on file/JSON errors.
    if EXISTING_DATA_DIR:
        with open(
            os.path.join(EXISTING_DATA_DIR, "removed.json"), encoding="utf-8"
        ) as file:
            return json.load(file)

tests/scripts/data/test_generate_category_data.py:372

  • This assertion should match the (repo-id -> repo-data) shape returned by the real data client and the updated test stub, otherwise the test can pass while still diverging from production expectations.
    assert await get_stored_data(hacs, "plugin") == {"fetched": "plugin"}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread scripts/data/generate_category_data.py Outdated
Comment on lines +64 to +71
async def get_stored_data(hacs: AdjustedHacs, category: str) -> dict[str, dict[str, Any]]:
"""Return existing category data from the snapshot dir when set, else fetch."""
if EXISTING_DATA_DIR:
with open(
os.path.join(EXISTING_DATA_DIR, f"{category}.json"), encoding="utf-8"
) as file:
return json.load(file)
return await hacs.data_client.get_data(category, validate=False)
Comment on lines +327 to +330
async def get_data(self, section: str, *, validate: bool) -> dict[str, Any]:
self.calls.append(("get_data", section))
return {"fetched": section}

assert await get_removed_repositories(hacs) == removed
assert hacs.data_client.calls == []


Comment thread .github/workflows/generate-hacs-data.yml
claude and others added 4 commits July 23, 2026 17:28
Reading the existing-data snapshot now degrades gracefully: a missing file or
invalid JSON logs a warning and falls back to the data client instead of
aborting generation, so a partial snapshot cannot fail the run. Make the test
stub return realistic data-client shapes and add regression tests for the
missing/invalid-file fallback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L22jYdNGF4hB4fvTYPTzP4
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L22jYdNGF4hB4fvTYPTzP4
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L22jYdNGF4hB4fvTYPTzP4

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

scripts/data/generate_category_data.py:92

  • Similarly, get_removed_repositories will return whatever JSON is in removed.json if it parses, even if it isn't a list of repository names. Validating that the snapshot is a list (and falling back when it's not) avoids hard-to-debug failures later in the generation flow.
async def get_removed_repositories(hacs: AdjustedHacs) -> list[str]:
    """Return the removed-repositories list from the snapshot dir when available, else fetch."""
    if (removed := _read_snapshot(hacs, "removed.json")) is not None:
        return removed
    return await hacs.data_client.get_repositories("removed")

EXISTING_DATA_DIR = os.getenv("HACS_EXISTING_DATA_DIR")


def _read_snapshot(hacs: AdjustedHacs, filename: str) -> Any | None:
Comment on lines +81 to +85
async def get_stored_data(hacs: AdjustedHacs, category: str) -> dict[str, dict[str, Any]]:
"""Return existing category data from the snapshot dir when available, else fetch."""
if (data := _read_snapshot(hacs, f"{category}.json")) is not None:
return data
return await hacs.data_client.get_data(category, validate=False)
@ludeeus ludeeus added the pr: action Changes to actions label Jul 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr: action Changes to actions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants