Skip to content

[repo] Refactor workflow automation with pluginctl CLI and new tests#190

Draft
sethwv wants to merge 3 commits into
Dispatcharr:mainfrom
swvn-dispatch:main
Draft

[repo] Refactor workflow automation with pluginctl CLI and new tests#190
sethwv wants to merge 3 commits into
Dispatcharr:mainfrom
swvn-dispatch:main

Conversation

@sethwv

@sethwv sethwv commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

No description provided.

sethwv added 3 commits July 22, 2026 11:20
The registry automation had grown to roughly 5,000 lines of inline YAML and
Bash across the plugin workflows, with the app-token block copy-pasted 6 times,
the fork-checkout dance 3 times, and the CodeQL SARIF jq program pasted 3 times.
This moves the logic into a testable Python package and leaves the workflows as
thin wiring.

- Add .github/pluginctl: a stdlib-only Python package (core, validate, publish,
  integrations) exposing one CLI subcommand per task, with a 113-test pytest
  suite covering the pure logic and golden PR-comment/manifest/README output.
- Add two composite actions (gh-app-token, trusted-checkout) and two reusable
  workflows (_codeql-scan, _clamav-scan) to remove the biggest duplication.
- Thin the five core workflows to composite-action + `python -m pluginctl`
  steps; validate-plugin.yml drops from 1,370 to 442 lines.
- Add an opt-in, best-effort signed webhook emitter (HMAC-SHA256) plus
  docs/webhooks.md, so a downstream consumer can react to registry events.
- Delete the V1 validate/publish shell scripts; keep scripts/keys.
- Remove the disabled standalone codeql workflow.
- Fix a latent bug: deprecated plugins' version count now derives from the
  manifest's versions[] instead of the removed zips/ path (which rendered 0).

User-facing output is kept byte-identical to the old shell, verified by the
golden tests. Final fork end-to-end validation is still pending.
…upstream Dispatcharr#127)

Ports the four behaviors from Dispatcharr#127 into the pluginctl publish
path.

- Split the root manifest's root_url into download_base_url (GitHub Releases, for
  ZIP paths) and metadata_base_url (GitHub Pages when configured, else
  raw.githubusercontent.com). Each plugin's manifest_url is now a relative path
  clients compose with metadata_base_url.
- Copy each plugin's source logo (png, svg, jpg, or webp, first match) into
  metadata/<slug>/ on every publish.
- Retry gh release create with a numeric tag suffix on immutable-tag conflicts,
  skip cleanly when the release already exists, and discard the fresh per-version
  metadata on a skip so generate-manifest keeps the published asset's checksums.
- When a tag carries a retry suffix, keep it in the release directory but use the
  canonical version for the ZIP asset filename, for versioned urls and latest_url.

readmes.py reads download_base_url instead of root_url. 8 new tests.
…on it

Adds a tests.yml workflow that runs the pluginctl pytest suite, triggered on
push to main for any change outside plugins/, and also exposed via workflow_call
so validate-plugin.yml can run it on a PR's merge commit. It runs with a
read-only token and no secrets.

- validate-plugin.yml gains a test-suite job that calls tests.yml for authorized
  repo-update PRs (outside_files present, not a violation, not closing).
- The result flows through report (a "Tooling test suite" comment section on
  failure, folded into overall_failed) and through the Plugin PR Check gate,
  which now fails a repo-update PR with red tests even when validation is
  otherwise skipped. gate.evaluate and report.build_comment take a test_result;
  cmd_gate/cmd_report read TEST_RESULT.
- Branch protection is unchanged: still one required check (Plugin PR Check).
@dispatcharr-plugins-bot

Copy link
Copy Markdown

This PR is currently a draft. Plugin validation will run once the PR is marked ready for review.

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