Skip to content

ci: publish only todo to fork GHCR until the pipeline is proven - #10

Merged
durandom merged 3 commits into
mainfrom
issue-5-todo-publish-pipeline
Sep 3, 2026
Merged

durandom merged 3 commits into
mainfrom
issue-5-todo-publish-pipeline

Conversation

@durandom

@durandom durandom commented Sep 1, 2026

Copy link
Copy Markdown

Summary

Closes the code side of #5. Publish uses the same mechanism as upstream (GITHUB_TOKEN → ghcr.io/${{ github.repository }} via rhdh-plugin-export-utils), but automatic export is allow-listed to workspaces/todo so the first main push cannot matrix all 65 workspaces.

  • Allow-list: parasol-publish-workspaces.txt (todo)
  • In-repo reusable publish workflow (no redhat-developer/...@main pin)
  • todo Package dynamicArtifact retargeted to ghcr.io/rhdh-parasol/rhdh-plugin-export-overlays/...
  • Community catalog-index uses fork GHCR + parasol-community-packages.txt; supported/Quay jobs skipped until Setup: Quay.io robot + QUAY_USERNAME / QUAY_TOKEN repo secrets #8
  • workflow_dispatch publish without workspace-path is refused

Setup (walk through together)

Same auth model as upstream. No extra GitHub robot for plugin images.

  1. Setup: GitHub Actions + GHCR so GITHUB_TOKEN can publish packages #7 (blocks /publish) — org/repo GHCR + Actions settings so GITHUB_TOKEN can create packages; make the two todo packages public after the first push
  2. This PR — merge or leave open; /publish works from a todo-only PR even before merge (issue_comment workflows run from main, overlay checkout is the PR head)
  3. Comment /publish on this PR
  4. Setup: Quay.io robot + QUAY_USERNAME / QUAY_TOKEN repo secrets #8 — Quay robot + QUAY_USERNAME / QUAY_TOKEN (supported catalog only)
  5. Setup: optional upstream-parity credentials (later) #9 — optional later (Codecov, e2e GitHub App, wiki, scheduled fork workflows)

Test plan

Made with Cursor

Co-authored-by: Cursor <cursoragent@cursor.com>
@fullsend-ai-review

fullsend-ai-review Bot commented Sep 1, 2026 •

Copy link
Copy Markdown

🤖 Finished Review · ❌ Failure (validation failed after 2 iteration(s)) · Started 1:04 PM UTC · Completed 1:05 PM UTC

Commit: a86d05c · View workflow run →

Runtime: claude · Model: claude-opus-4-8

Co-authored-by: Cursor <cursoragent@cursor.com>
@fullsend-ai-review

fullsend-ai-review Bot commented Sep 1, 2026 •

Copy link
Copy Markdown

🤖 Finished Review · ❌ Failure (validation failed after 2 iteration(s)) · Started 1:14 PM UTC · Completed 1:15 PM UTC

Commit: 9219cfc · View workflow run →

Runtime: claude · Model: claude-opus-4-8

@kadel

kadel commented Sep 1, 2026

Copy link
Copy Markdown

/fs-review

@fullsend-ai-review

fullsend-ai-review Bot commented Sep 1, 2026 •

Copy link
Copy Markdown

🤖 Finished Review · ❌ Failure (validation failed after 2 iteration(s)) · Started 1:38 PM UTC · Completed 1:40 PM UTC

Commit: 9219cfc · View workflow run →

Runtime: claude · Model: claude-opus-4-8

@kadel

kadel commented Sep 1, 2026

Copy link
Copy Markdown

/fs-review

@fullsend-ai-review

fullsend-ai-review Bot commented Sep 1, 2026 •

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 1:43 PM UTC · Completed 1:49 PM UTC

Commit: 9219cfc · View workflow run →

Runtime: claude · Model: claude-opus-4-6 · Cost: $0.99

@fullsend-ai-review

Copy link
Copy Markdown

Review — approve

This PR adapts the CI/CD pipeline for the rhdh-parasol fork, narrowing the publish scope to only the todo workspace via a new allow-list mechanism. The changes are coherent, well-guarded, and appropriately scoped.

What was reviewed

9 files across 6 review dimensions (correctness, security, intent/coherence, style, documentation currency, cross-repo contracts).

Findings

No blocking findings. Two low-severity observations noted below.


Detailed analysis

Correctness

Allow-list mechanism (parasol-publish-workspaces.txt + parasol-allow-list.py): The Python script is well-structured with two clean modes (--workspaces-json for CI workspace resolution, --packages-file for catalog-index). The string-based parsing of plugins-list.yaml (splitting on :) is pragmatic and matches the actual file format (plugins/todo:, plugins/todo-backend:). Output format aligns with rhdh-community-packages.txt expectations.

Null-safety fix (context.payload.commits && ...): Correct. When the workflow is invoked via workflow_call (rather than a push event), context.payload.commits is undefined. Without this guard, the original code would throw a TypeError. Good defensive fix.

Job filter broadening (startsWith('export /') → startsWith('export') && j.conclusion !== 'skipped'): The old filter matched upstream job names like export / todo. With the new matrix strategy, GitHub names jobs differently (e.g., export (workspaces/todo) or nested reusable workflow patterns). The new filter correctly matches both old and new formats. The !== 'skipped' exclusion prevents counting guard jobs or skipped matrix entries.

refuse-unscoped-publish guard: The if: always() && needs.refuse-unscoped-publish.result != 'failure' pattern on the export job is correct — when the guard job is skipped (on non-dispatch events), the export job still proceeds normally.

Catalog-index changes: The gen-supported step now outputs ok=false/clean=false and the validation step was updated to only fail on community catalog failure, issuing a notice for supported — consistent with deferring Quay integration to #8.

Matrix strategy: fail-fast: false ensures all workspaces are attempted even if one fails. The shared last-publish-commit baseline is correct since it represents the last successful full-pipeline run.

create-release-branch.yaml: The switch to the local workflow is sound. The image-repository-prefix, image-registry-user, and secrets are now embedded in the callee (publish-release-branch-workspace-plugins.yaml) rather than passed by the caller — they resolve to the same values.

image-repository-prefix default: The expression inputs.image-repository-prefix != '' && inputs.image-repository-prefix || (inputs.publish-container && format('ghcr.io/{0}', github.repository) || '') correctly defaults to the fork GHCR when no prefix is explicitly provided and container publishing is enabled.

Security

  • GITHUB_TOKEN is used for GHCR auth — standard GitHub Actions pattern, no new secrets.
  • The refuse-unscoped-publish guard prevents accidentally publishing all 65 workspaces from a manual workflow dispatch — good safety measure.
  • No credential exposure or privilege escalation risks identified.

Intent & coherence

The PR is well-scoped to its stated goal: adapting the upstream CI/CD pipeline for a fork that publishes only allow-listed workspaces to its own GHCR. The supported/Quay tier is explicitly deferred to #8. The PR body clearly documents the setup sequence (#7 → this PR → #8 → #9) and test plan.

Style & conventions

  • Python script follows standard practices (argparse, type hints, Path, encoding parameters).
  • Trailing whitespace cleanup throughout workflow files is appropriate.
  • Workflow YAML structure follows existing conventions.

Documentation

  • parasol-publish-workspaces.txt includes clear inline instructions for adding workspaces.
  • The Python script has a module docstring explaining its purpose.

Cross-repo contracts

  • The upstream reusable workflow (redhat-developer/rhdh-plugin-export-utils/.github/workflows/export-workspaces-as-dynamic.yaml@main) is still used with its expected interface (workspace-path, overlay-branch, publish-container, etc.).
  • The todo workspace metadata dynamicArtifact is correctly retargeted to ghcr.io/rhdh-parasol/rhdh-plugin-export-overlays/... with preserved version tags.

Low-severity observations

  1. workspace_paths() doesn't validate directory existence: In --workspaces-json mode, a typo in parasol-publish-workspaces.txt would produce a matrix entry for a non-existent workspace. The upstream export workflow would fail fast, but an earlier validation could surface a clearer error message. (file: scripts/parasol-allow-list.py)

  2. Broadened job name filter: startsWith('export') is broader than the original startsWith('export /'). While this works correctly in the current workflow composition, future jobs with names starting with "export" would inadvertently be included in the last-publish-commit check. (file: .github/workflows/publish-release-branch-workspace-plugins.yaml)


Protected paths detected — this PR modifies files under one or more
protected paths. The review agent cannot approve PRs that touch these paths.
A human reviewer must approve this PR.

Protected files in this PR:

  • .github/workflows/create-release-branch.yaml
  • .github/workflows/export-workspaces-as-dynamic.yaml
  • .github/workflows/generate-catalog-index.yaml
  • .github/workflows/publish-release-branch-workspace-plugins.yaml
  • .github/workflows/publish-workspace-plugins.yaml
  • scripts/parasol-allow-list.py

@durandom

durandom commented Sep 1, 2026

Copy link
Copy Markdown
Author

/publish

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

Publish workflow has completed with success.

Publishing process

✅ Finished successfully.

✅ Published container images:

  • ghcr.io/rhdh-parasol/rhdh-plugin-export-overlays/backstage-community-plugin-todo:pr_10__0.22.0
  • ghcr.io/rhdh-parasol/rhdh-plugin-export-overlays/backstage-community-plugin-todo-backend:pr_10__0.23.0

Backstage Compatibility Check

✅ All workspaces are compatible with the target Backstage version (1.52.0).

No action required.

Metadata Validation

✅ All metadata files validated successfully.

No E2E tests available for this workspace.

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

✅ Smoke tests workflow passed. All plugins loaded successfully.

Co-authored-by: Cursor <cursoragent@cursor.com>
@fullsend-ai-review

fullsend-ai-review Bot commented Sep 3, 2026 •

Copy link
Copy Markdown

🤖 Finished Review · ❌ Failure (validation failed after 2 iteration(s)) · Started 11:44 AM UTC · Completed 11:46 AM UTC

Commit: 9b327ec · View workflow run →

Runtime: claude · Model: claude-opus-4-8

@durandom
durandom merged commit 1e0d1ed into main Sep 3, 2026
11 of 12 checks passed
@fullsend-ai-retro

fullsend-ai-retro Bot commented Sep 3, 2026 •

Copy link
Copy Markdown

🤖 Finished Retro · ✅ Success · Started 11:51 AM UTC · Completed 12:08 PM UTC

Commit: 9b327ec · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $4.87

@fullsend-ai-retro

Copy link
Copy Markdown

Retro: PR #10 — ci: publish only todo to fork GHCR

Timeline

  1. Sep 1, 13:02 — PR created by durandom with commit a86d05c (adapt CI/CD pipeline for rhdh-parasol fork, scoped to workspaces/todo)
  2. Sep 1, 13:04 — Review run 1 (33511087533) FAILED (claude-opus-4-8, policy_denied — Vertex AI auth blocked for Claude Code CLI)
  3. Sep 1, 13:10 — Second commit 9219cfc pushed
  4. Sep 1, 13:14 — Review run 2 (33511800129) FAILED (claude-opus-4-8, same policy_denied)
  5. Sep 1, 13:36 — kadel manually triggered /fs-review
  6. Sep 1, ~13:36 — PR fix(fullsend): allow Claude Code Vertex authentication #13 merged (fixes Vertex policy for Claude Code)
  7. Sep 1, 13:38 — Review run 3 (33514404069) FAILED (claude-opus-4-8, model not available — opus-4-8 not enabled on Vertex project)
  8. Sep 1, ~13:42 — PR fix(fullsend): use available Claude Opus 4.6 model #14 merged (switches all agents from opus-4-8 to opus-4-6)
  9. Sep 1, 13:42 — kadel manually triggered second /fs-review
  10. Sep 1, 13:43–13:49 — Review run 4 (33515043148) SUCCESS (claude-opus-4-6, $0.99)
  11. Sep 1, 13:49 — Review posted: approve with 2 low-severity observations, protected paths correctly flagged for human review
  12. Sep 1, 14:05 — durandom ran /publish; OCI images published successfully; smoke tests passed
  13. Sep 3, 11:42 — Third commit 9b327ec pushed (experimental set-ghcr-packages-public.yaml workflow)
  14. Sep 3, 11:44 — Review run 5 (33751208233) FAILED (claude-opus-4-8, policy_denied)
  15. Sep 3, 11:50 — PR merged

Assessment

Infrastructure failures dominated this PR's lifecycle. Four of five review runs failed before the agent consumed a single token — all were infrastructure errors during initial fork setup (Vertex AI policy blocking Claude Code auth, then the pinned model not being enabled on the Vertex project). Two humans (kadel and durandom) had to debug infrastructure via PRs #13 and #14 while manually retrying /fs-review.

Every failed status comment showed only "Failure (validation failed after 2 iteration(s))" — no indication of the actual error class (policy_denied, model not available). Diagnosing required reading workflow logs.

Review quality was strong when the agent ran. The successful review (run 4) covered 9 files across 6 dimensions, correctly recommended approve, and raised two valid low-severity observations: (1) workspace_paths() doesn't validate directory existence, and (2) the broadened startsWith('export') job filter could match unintended future jobs. Protected paths were correctly detected and human review was correctly required.

The 3rd commit was never successfully reviewed. Run 5 failed on commit 9b327ec, and the PR was merged 6 minutes later. Given the requires-manual-review label and the nature of the commit (experimental GHCR visibility workflow), this was a reasonable human judgment call.

Run 5's failure (Sep 3) is puzzling. PRs #13 and #14 were merged to main on Sep 1, which should have resolved both the policy and model issues. The exact cause of run 5 hitting policy_denied again is unclear — it may indicate the config was resolved from the PR branch (not rebased) rather than main, a policy regression, or a different auth issue.

Existing Issues (no new proposals needed)

All improvement opportunities identified in this retro are already covered by open issues in fullsend-ai/fullsend:

  • Opaque error messages: #2058 (harness should fail fast on auth errors instead of retrying with generic validation-failed message), #3635 (post failure status comment when agent fails before creating one), #5382 (status comment should classify infrastructure failures and suggest retry). New evidence from this retro: 4 consecutive runs produced "validation failed after 2 iteration(s)" for two distinct infrastructure errors (policy_denied and model not available), requiring a human to read logs to diagnose.

  • Model availability: #6922 (model unavailable on Vertex causes wasted retries), #6883 (per-repo model catalog and pre-sandbox availability preflight). New evidence: claude-opus-4-8 was pinned in config (PR chore: pin customized Fullsend agents to Claude Opus 4.8 #6) without verifying availability on the Vertex project, leading to 2 wasted runs before the model was switched.

Agents Repo

Agent definitions, harness configs, and scripts resolved from fullsend-ai/agents@v0.38.0 (commit 4bbe4f5). Platform orchestration from fullsend-ai/fullsend@v0.38.0. Per-repo harness overrides in .fullsend/rhdh/ with base: pinning to the agents repo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

requires-manual-review Review requires human judgment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants