Repository navigation
[Enhancement]: Watch unraid/api releases and schema changes, and open bug/enhancement issues automatically #136
Description
Affected area
CI and repository automation
Problem statement
This library tracks unraid/api by hand, so upstream changes reach it late. Releases 4.37.7–4.37.9 (23–28 Oct) shipped two changes that broke the library on live servers, and both were only found by a manual audit on 30 Oct (#135):
unraid/api#2123made thedockerandvmsquery roots nullable (docker: Docker!→docker: Docker). A failed Docker or VM service now raisesAttributeErrorin the library instead ofUnraidAPIError.unraid/api#2095and #2129 disabled schema introspection by default. Capability detection silently fell back to permissive, so consumers lost VM UUIDs. The same change breaksscripts/validate-schema.py, which introspects a live server.
Upstream also publishes the complete schema as api/generated-schema.graphql in every commit. That is enough to detect most of these changes in CI, without a server, the day they merge.
Proposed solution and architecture
Add an Upstream watch workflow (.github/workflows/upstream-watch.yml), modelled on ha-unraid's HA Next and HA dev blog watch (tinkerologie/ha-unraid#391): scheduled daily and workflow_dispatch, permissions: {} at the top level, SHA-pinned actions, Python 3.14.2 through uv. actions/setup-python has no 3.14.2 build for the Ubuntu 26.04 runner.
script/upstream_watch.py(scan), read-only, withgraphql-core(already a dependency):- Releases: list
unraid/apireleases newer than the last one recorded, using a marker such as<!-- upstream-release: v4.37.9 -->in this repository's issues, open or closed. For each new release, read its notes and the PRs it links. - Schema diff: parse
api/generated-schema.graphqlat the previous and the new release tag, and also atmainfor unreleased changes. Report removed types and fields, non-null → nullable and nullable → non-null changes, argument and input changes, enum value changes, new deprecations, and added types, fields, queries, mutations and subscriptions. - Impact on this library: validate every operation the library sends against the new schema with
graphql.validate, reusing the operation extraction inscripts/validate-schema.py. Then, for each changed field, say whether the library selects it (file:line). A removed field the library selects, or a root or field the library reads that became nullable, is breaking. - Security and behaviour notes: flag release-note entries that change request handling (introspection, request limits, auth, rate limiting, error shapes), even without a schema change.
- Write one JSON report.
- Releases: list
- Report job (
issues: writeon that job only,actions/github-script), one issue per finding group, deduplicated by a hidden marker in the issue body (release tag, plusmainfor unreleased changes), never through the search API, which lags:bug+upstream: breaking changes (a library operation no longer validates; a field or root the library reads became nullable or was removed), with the schema diff, the affected methods and the release notes.enhancement+upstream: new types, fields, queries, mutations and subscriptions the library doesn't expose yet, plus non-breaking deprecations.- Labels are set in the create call (created first if missing). Issues opened with
GITHUB_TOKENnever triggerissue-labeler.yml, and CodeRabbit only writes a coding plan forbug/enhancement. - The job summary lists every release and schema change checked, including those that need no action.
- Make
scripts/validate-schema.pywork on 4.37.8+: a--schema-fileoption (default: the upstream SDL for the server's API version) instead of introspection, which those servers refuse.
Acceptance criteria
-
script/upstream_watch.py scanproduces a report for any release range; run against v4.37.6 → v4.37.9 it reports the nullabledocker/vmsroots as breaking (with the library methods that read them) and the introspection policy as a behaviour change - The workflow opens deduplicated
bug/enhancement+upstreamissues and records the last processed release; a re-run opens nothing new, and closed issues still prevent duplicates - Unreleased schema changes on upstream
mainare reported once, and not again when they ship in a release -
scripts/validate-schema.py --schema-filevalidates every library operation without a live server - New functionality exposed with full test coverage
- Linter and type checks pass locally
- Documentation and CHANGELOG.md updated under [Unreleased]
Alternatives considered
- Watching releases only: this would have caught 4.37.8, but not unreleased schema changes on
maindays earlier, and it can't say whether the library is affected. - Live-server contract tests in CI: they need credentials and a server, and introspection is now disabled.
- Dependabot or Renovate: the library doesn't depend on unraid/api as a package.
Additional context
Found while auditing upstream 4.37.7–4.37.9 for #135. Upstream issues to keep watching that need no library change: unraid/api#2120 (Docker stats stream goes silent until an API restart), #2131 and #2007. The watcher could also list open upstream issues that mention a subscription or query the library uses, as an optional second stage.
Disclosure: this issue was written with the help of AI (Claude Code).
Pre-submission confirmations
- I searched existing open and closed issues before opening this request.
- I confirm this request targets the unraid-api Python package.
- I provided sufficient technical context and acceptance criteria for plan generation.
- I read and agree to follow repository contribution rules and guidelines.
Activity
[-]Watch unraid/api releases and schema changes, and open bug/enhancement issues automatically[/-][+][Enhancement]: Watch unraid/api releases and schema changes, and open bug/enhancement issues automatically[/+]coderabbitai commentedon Oct 30, 2026
Coding Plan
Summary
script/schema_tools.pymodule for the watcher and validator. Replace the static extractor that corrupts dynamic operations with a hybrid inventory. Render capability-gated builders against capabilities derived from the target schema.graphql.validate. Use a custom diff with stable change IDs. Classify changes by whether the library selects the affected coordinate.state: alllisting. Use group markers, per-change markers and a closed state issue. Prevent duplicates from re-runs, closed issues and changes moving frommainto a release.issues: writereport job. Add an offline SDL path tovalidate-schema.py. Keep introspection as an opt-in.Design Choices
Design Choice 1: Provide graphql-core as a tooling dependency. Exploration confirms that it is absent from pyproject.toml and uv.lock, despite the ticket stating that it is already a dependency.
Options Considered:
devoptional extra and regenerateuv.lock.Chosen Option: 2
Rationale: Only tooling uses it, and the workflow already installs
--all-extras. The library runtime does not change.Design Choice 2: Choose script/ from the ticket or the existing scripts/ directory for watcher code. scripts/ is excluded from Ruff. File names with a hyphen cannot be imported normally.
Options Considered:
upstream_watch.pyinscripts/next to the validator.script/as the ticket specifies. Put shared schema tooling there. Letscripts/validate-schema.pyimport it.Chosen Option: 2
Rationale: Ruff then lints the new code, the modules are importable by tests, and the ticket path is kept.
Design Choice 3: Build an accurate library operation inventory. Static AST extraction corrupts interpolated operations and attributes them to builders instead of public methods.
Options Considered:
extract_queries_from_sourceunchanged.Chosen Option: 3
Rationale: This is accurate for every current dynamic case. It does not need to invoke hundreds of methods with arguments. The guard catches new dynamic builders.
Design Choice 4: Record the last processed release and deduplicate changes from main to a release. A release with no findings creates no issue. A main change appears again in the next release diff.
Options Considered:
Upstream watch stateissue that holds theupstream-releaseandupstream-mainmarkers. Add oneupstream-changemarker for each change in each finding issue.Chosen Option: 2
Rationale: Releases without findings are recorded. Changes reported from
mainare not reported again when they ship in a release. Closed issues still count.Design Choice 5: Select the default schema source when --schema-file is absent and retain optional introspection.
Options Considered:
--introspectflag. Fail clearly if the version is"unknown"or does not parse.Chosen Option: 2
Rationale: The ticket asks for this default. Existing behaviour stays available for servers with the developer sandbox enabled.
Design Choice 6: Choose the location for reporting logic.
Options Considered:
actions/github-scriptJavaScript.Chosen Option: 2
Rationale: The logic is in Python and pytest covers it. The job with write access stays small, and does not need a checkout.
Design Choice 7: Meet the v4.37.6 → v4.37.9 acceptance criterion without confirmed public tags. The web search did not verify v4.37.6, v4.37.7 or v4.37.9, or the schema file at each tag.
Options Considered:
Chosen Option: 2
Rationale: Tests are deterministic. A real
scan --since v4.37.6 --until v4.37.9run stays a manual check.💡 User Tips
Regenerate the plan with different choices with
@coderabbitai <feedback>.Implementation Steps
Phase 1: Shared schema and operation tooling
Create one tested module that loads SDL, builds the library operation inventory with provenance, validates operations, maps selected fields to
file:line, and diffs two schemas. Use this module in the watcher and validator.Task 1: Dependency and tooling configuration
Make
script/installable, linted, type-checked, tested and measured.pyproject.toml: addgraphql-coreto thedevextra. Addscriptto Ruffsrc. Addpythonpath = ["script"]to pytest options. Addscriptto coveragesource.uv.lockwithuv lock..github/workflows/ci.ymland.pre-commit-config.yamlso mypy checkssrcandscript. If the mypy hook uses an isolated environment, addgraphql-coreto itsadditional_dependencies.Task 2: SDL loading and operation inventory
Replace the corrupting extractor with an accurate inventory. Keep one implementation.
script/schema_tools.py. MoveExtractedQueryandextract_queries_from_sourcefromscripts/validate-schema.py. Extend them:query/mutation/subscription.src/unraid_api/client.pyandsrc/unraid_api/capabilities.py._build_containers_query,_build_containers_query_safe, the VM query intyped_get_vms, andbuild_introspection_query(). Render each source with two capability sets:ServerCapabilities.permissive().build_introspection_query()against the graphql-core schema withgraphql_sync. Pass the result toServerCapabilities.from_introspection_response.UnraidClientwith dummy credentials and preset_capabilities. Fortyped_get_vms, patchquerywith a recordingAsyncMock. Do not use network access.self.<method>(calls. Attribute each operation to all transitive public callers, for exampletyped_get_docker_networks→get_docker_networks. Keep all provenance when deduplicating identical documents.query(),subscribe()arguments) and script-owned documents.Task 3: Validation, selected-field coordinates and schema diff
Provide the analysis primitives in
script/schema_tools.py.graphql.build_schemafrom a file path or text.graphql.parseandgraphql.validate. Return errors with the operation, its methods andfile:line.Type.field, including root fields such asQuery.docker) for each operation withTypeInfo/TypeInfoVisitor. Map each coordinate to methods andfile:line.Query/Mutation/Subscription.Task 4: Tests for shared tooling
tests/test_schema_tools.pywithTest*classes. Store fixture SDL files intests/fixtures/upstream/.uuidpresent only for detected support.🤖 Prompt for AI agents
Phase 2: Upstream watcher scan
Implement the read-only
scancommand. Produce one JSON report with findings, pre-rendered issue payloads and a summary.Task 1: GitHub access and release range
script/upstream_watch.pywith ascansubcommand. Add--since,--until,--outputand--repo(this repository, for markers). Accept an optional token fromGITHUB_TOKEN.api.github.com,raw.githubusercontent.com), a timeout and clear errors. Inject the helper so tests can replace it. Keep RuffS310handling local to the helper.^v\d+\.\d+\.\d+$and commits against a 40-hex SHA. Sort releases withpackaging.version. Skip drafts and pre-releases.state: all. Skip pull requests. Do not use the search API. Read markers only from issues authored bygithub-actions[bot].<!-- upstream-release: vX -->,<!-- upstream-main: <sha> -->, all<!-- upstream-change: <id> -->IDs, and all group markers.--since/--untiloverride markers.mainto its HEAD SHA.Task 2: Schema diff, impact and classification
api/generated-schema.graphqlat each consecutive tag pair and at latest release →main@sha. If a ref has no file, record "schema unavailable" for that step and continue.file:lineto each change.bug): an operation fails validation; a selected field or root was removed; a selected field or root changed from non-null to nullable.Task 3: Release notes, linked PRs and behaviour notes
unraid/apiPR numbers, up to a fixed limit. Fetch each PR title and body.__schema/__type, sandbox, request limits, complexity/depth, auth/API key, rate limiting, CORS/CSRF, WebSocket and error shape.buggroup, in a separate "Behaviour changes" section. If a release has only behaviour notes, create abugissue.Task 4: Report and issue payloads
range: the processed range.main: schema status, changes, classification, behaviour notes and PR links.issues[]: group key, title, labels, body and change IDs.state: the new release andmainmarkers.summary_markdown: every release and change checked, including no-action items.<tag>:bug,<tag>:enhancement,main@<sha7>:bug,main@<sha7>:enhancement.upstream-releasemarker (ormainmarker), oneupstream-changemarker for each change, the schema diff, affected methods withfile:line, and links to release notes and PRs.Task 5: Tests for the watcher
tests/test_upstream_watch.pywith a fake fetcher and fixtures intests/fixtures/upstream/.docker: Docker!→docker: Docker, and the same change forvms). Add release notes that link#2095,#2123and#2129.scan --since v4.37.6 --until v4.37.9reportsQuery.dockerandQuery.vmsas breaking. The report liststyped_get_containers,typed_get_containers_safe,get_containers,typed_get_vms,get_vms,get_container_update_statuses,get_docker_port_conflicts,get_docker_networks,typed_get_docker_networks,get_container_logs,typed_get_container_logs,get_docker_organizerandtyped_get_docker_organizer. It flags introspection as a behaviour change.mainis not reported again in a later release.🤖 Prompt for AI agents
Phase 3: Offline --schema-file for scripts/validate-schema.py
Remove the dependency on introspection.
Task 1: Schema source selection
scripts/validate-schema.py. Insertscript/intosys.path. Import the inventory, SDL loader and validation fromschema_tools. Remove the moved extractor.--schema-file PATH: SDL file.--api-version: optional override for the default fetch.--introspect: the old path.get_version(), normalise tov{api}, and fetch upstream SDL through the shared fetch helper. Ifapiis"unknown"or does not parse, exit 1. Tell the user to pass--schema-fileor--api-version.--schema-fileis given with--schema-only, branch beforeload_env()andUnraidClientcreation. Do not require credentials or a server.Task 2: Reporting and model comparison on SDL
graphql.validate. Map errors toValidationIssueERROR entries. Keepprint_validation_reportand exit codes unchanged.SchemaType/SchemaFieldmap. Keepprint_schema_summary,compare_models_to_schemaand--dump-schemaworking.Task 3: Tests for the validator
tests/test_validate_schema_script.py. Load the script withimportlib.util.spec_from_file_location. Register it insys.modules.--schema-file --schema-onlydoes not callload_envorUnraidClient.🤖 Prompt for AI agents
Phase 4: Workflow, reporting job and documentation
Connect the scan and reporting to CI automation. Document the change.
Task 1: Upstream watch workflow
.github/workflows/upstream-watch.yml.scheduletrigger with the timezone pattern fromstale.yml. Addworkflow_dispatchwith optionalsince/untilinputs.permissions: {}at the top level. Add a concurrency group without cancel-in-progress.scanjob:contents: read,issues: read.persist-credentials: false;astral-sh/setup-uvwithpython-version: "3.14.2"and nosetup-python;uv sync --frozen --all-extras;uv run python script/upstream_watch.py scan.env. Do not use inline expression interpolation.report.jsonas an artifact.actions/upload-artifact,actions/download-artifactandactions/github-scriptto verified full commit SHAs with version comments. These actions are new to this repository. Look up the SHAs. Do not guess them.Task 2: Report job
reportjob withneeds: scan,issues: writeonly, and no checkout.actions/github-scriptinline:state: alland pagination. Skip pull requests. Read markers from bot authors only.bug,enhancementandupstreamexist. Create a missing label before use.Upstream watch stateissue with the newupstream-release/upstream-mainmarkers. Label itupstreamonly. Never label itbugorenhancement.summary_markdownand links to created or existing issues withcore.summary.Task 3: Documentation and changelog
CONTRIBUTING.md:scancommand andvalidate-schema.py --schema-fileusage.upstreamto the labels section.AGENTS.mdto state thatscript/is linted and type-checked.CHANGELOG.mdunder[Unreleased]:--schema-file.graphql-coredev dependency.uv run pytest,ruff check,ruff format --check,mypy src script,pre-commit run --all-files, and an Aikido scan on new and modified files.🤖 Prompt for AI agents
Research
Ticket Summary
Add an
Upstream watchworkflow. The workflow scansunraid/apireleases andapi/generated-schema.graphqlat release tags and atmain. It validates every library operation against the new schema. It opens deduplicated issues withbugorenhancement, plusupstream. Add--schema-filetoscripts/validate-schema.pyso the validator works on servers that refuse introspection (4.37.8 and later).Codebase Summary
All GraphQL operations are inline in
src/unraid_api/client.py.build_introspection_query()insrc/unraid_api/capabilities.pybuilds capability introspection. The container queries (_build_containers_query,_build_containers_query_safe) and the VM query intyped_get_vmsare capability-gated f-strings.scripts/validate-schema.pyhas an AST extractor,extract_queries_from_source. The extractor drops f-string interpolations, which empties the container selections. It attributes operations to builders, not public callers. The validator always introspects a live server. It has no SDL loader.parse_schema()is unused.scripts/is excluded from Ruff. mypy checks onlysrc/. Coverage measures onlysrc/unraid_apiat 95%.graphql-coreis not a dependency.Workflows pin actions to full SHAs. They use
uv sync --frozen --all-extras, credential-free checkout and job-scoped permissions. Tests use pytest classes withunittest.mockand fixtures intests/conftest.py. CHANGELOG follows Keep a Changelog under[Unreleased].File-level Change Summary
pyproject.toml,uv.lockgraphql-coreto thedevextra. Includescriptin Ruffsrc, pytestpythonpathand coveragesource..github/workflows/ci.yml,.pre-commit-config.yamlscriptandsrc.script/schema_tools.py(new)script/upstream_watch.py(new)scanCLI, GitHub fetch, markers, range, classification, behaviour notes, JSON report and issue payloads.scripts/validate-schema.py--schema-file,--api-versionand--introspect. Use upstream SDL by default. Add the SDL →SchemaTypeadapter..github/workflows/upstream-watch.yml(new)scanjob and thereportjob withissues: write.tests/test_schema_tools.py,tests/test_upstream_watch.py,tests/test_validate_schema_script.py,tests/fixtures/upstream/(new)CONTRIBUTING.md,AGENTS.md,CHANGELOG.md[Unreleased]entries.Instructions for follow-up agents
AttributeErroron a nulldocker/vmsroot.#135tracks that fix.typed_get_containers_safeto the validator's live-method list. That change is out of scope.🚀 Next Steps
🤖 All AI agent prompts combined
💡 Iterate on the plan with:
@coderabbitai <feedback>Start Coding →
coderabbitai commentedon Oct 30, 2026
🔗 Related PRs
#139 - feat: watch unraid/api releases and schema changes [closed]
#137 - fix: handle Unraid API 4.37.8 nullable roots and disabled introspection [closed]
📝 Issue Planner
Check the box below or use the
@coderabbitai plancommand to generate an implementation plan and prompts that you can use with your favorite coding assistant.🧪 Issue enrichment is currently in open beta.
To disable automatic issue enrichment, add the following to your
.coderabbit.yaml:💬 Have feedback or questions? Drop into our discord!
fix: address upstream watch review findings (#136)
fix: address second upstream watch review (#136)
fix: address third upstream watch review (#136)
feat: watch unraid/api releases and schema changes (#136)
fix: address upstream watch review findings (#136)
fix: address second upstream watch review (#136)
fix: address third upstream watch review (#136)