Skip to content

fix: forked plugin publish workflow#2014

Merged
elibosley merged 4 commits into
mainfrom
codex/fix-forked-plugin-publish
May 20, 2026
Merged

fix: forked plugin publish workflow#2014
elibosley merged 4 commits into
mainfrom
codex/fix-forked-plugin-publish

Conversation

@elibosley
Copy link
Copy Markdown
Member

@elibosley elibosley commented May 20, 2026

Summary

  • skips direct Cloudflare uploads from pull_request plugin builds where fork secrets are unavailable
  • adds a trusted workflow_run uploader that downloads the plugin artifact, validates .plg/.txz contents, syncs to R2, and comments the preview URL
  • hardens the merged-PR close-out workflow so it runs from trusted pull_request_target context, reads the existing PR plugin from R2, and skips successfully when there is nothing to close out
  • addresses CodeRabbit workflow feedback by pinning new privileged workflow actions, avoiding direct workflow_dispatch input interpolation, fixing upload concurrency fallback, and clarifying the close-out sed pattern
  • quotes and validates Cloudflare endpoint/bucket inputs for R2 operations so missing secrets fail with a clear message

Root Cause

Forked PR builds cannot access the Cloudflare R2 secrets used by the reusable plugin build workflow. The AWS CLI step still ran, so ${{ secrets.CF_ENDPOINT }} expanded to an empty value and aws s3 sync failed with argument --endpoint-url: expected one argument.

The PR close-out workflow had a separate reliability issue: it tried to download a successful main.yml plugin artifact on every merged PR and failed if no artifact existed. It also ran on pull_request, so fork-originated runs had Secret source: None and would fail later R2 operations even when an artifact was present.

Validation

  • ruby -e 'require "yaml"; ARGV.each { |f| YAML.load_file(f); puts "ok #{f}" }' .github/workflows/upload-pr-plugin.yml .github/workflows/push-staging-pr-on-close.yml
  • rg -nP '^\s*uses:\s*[^@]+@(?!(?:[a-f0-9]{40})$).+' .github/workflows/upload-pr-plugin.yml .github/workflows/push-staging-pr-on-close.yml returned no matches
  • rg -n '\.\*\?|\$\{\{ inputs\.pr_number \}\}' .github/workflows/push-staging-pr-on-close.yml only reports the safe step env assignment for INPUT_PR_NUMBER
  • git diff --check
  • coderabbit-review-data final 2014 returned unresolved_coderabbit_threads 0
  • inspected recent failed Replace PR Plugin with Staging Redirect on Merge runs; latest failure was no matching workflow run found with any artifacts?

GitHub Actions still needs to run the full fork publish and merged-PR close-out handoffs in CI.

Summary by CodeRabbit

  • New Features

    • PR builds now produce preview uploads with generated download URLs and a sticky PR comment.
    • Added automated PR-side upload workflow to stage plugin artifacts for previews.
  • Chores

    • Upload and preview flows tightened with safer validations, stricter input checks, and more flexible publish conditions.
    • PR merge flow updated to fetch, modify and re-deploy staging redirects automatically.
  • Documentation

    • Added internal review/notes document for tracking fixes.

Review Change Stack

@elibosley elibosley changed the title [codex] Fix forked plugin publish workflow fix: forked plugin publish workflow May 20, 2026
@elibosley elibosley marked this pull request as ready for review May 20, 2026 15:01
Purpose of the change:
- Allow forked pull request builds to publish preview plugins through a trusted workflow path.

How behavior was before:
- The pull_request build attempted to upload directly to Cloudflare R2 from the reusable plugin build workflow.
- Forked builds could not access Cloudflare secrets, leaving --endpoint-url empty and causing the aws command to fail.

Why that was a problem:
- Network/plugin preview artifacts were built but never uploaded to Cloudflare for forked PRs.

What the new change accomplishes:
- Pull request builds upload plugin files as GitHub artifacts only.
- A workflow_run job running with base repository permissions downloads, validates, uploads, and comments the preview URL.

How it works:
- The direct Cloudflare upload step is skipped for pull_request events.
- The new upload-pr-plugin workflow finds the successful CI run artifact, validates .plg/.txz contents, syncs them to the PR preview path, and posts a sticky PR comment.
- Direct push uploads now quote and validate Cloudflare endpoint and bucket inputs.
@elibosley elibosley force-pushed the codex/fix-forked-plugin-publish branch from 565556c to 4a8cdf0 Compare May 20, 2026 15:01
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 20, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: e5a29fd3-31f8-451a-a75f-d8cfb485e28e

📥 Commits

Reviewing files that changed from the base of the PR and between ca2db18 and dd7ed30.

📒 Files selected for processing (1)
  • .codex/coderabbit-fixes-wip.md

Walkthrough

Refactors build-plugin uploads to use optional Cloudflare inputs and env vars, adds upload-pr-plugin.yml to validate and publish PR build artifacts to Cloudflare R2 with PR preview comments, and updates push-staging to fetch/modify/upload PR redirect plugins from R2.

Changes

Plugin Distribution Workflows

Layer / File(s) Summary
Cloudflare optional and artifact naming fallback
.github/workflows/build-plugin.yml
Cloudflare/R2 workflow_call secrets marked required: false. GHA artifact name now falls back: `inputs.TAG
Upload and cleanup step refactoring
.github/workflows/build-plugin.yml
Cloudflare upload step excludes pull_request events, adds shell checks for required env/inputs, and updates aws s3 sync/listing/removal to use CF_BUCKET_PREVIEW, BUCKET_PATH, and CF_ENDPOINT with quoted params.
Staging redirect: fetch, modify, upload
.github/workflows/push-staging-pr-on-close.yml
Trigger moved to pull_request_target; job runs for merged PRs. Replaces artifact download with R2 fetch of PR plugin, validates key, edits redirect plugin in pr-release/ (timestamp and URL), clears PR directory, and uploads modified redirect plugin back to R2; PR comment gated on plugin presence.
New PR upload workflow trigger and PR resolution
.github/workflows/upload-pr-plugin.yml
New workflow triggers on completion of "CI - Main (API)" for PR runs; resolves run_id and pr_number from payload or GitHub API and exports them.
Artifact download, extract and validation
.github/workflows/upload-pr-plugin.yml
Downloads workflow-run artifact matching unraid-plugin-<runId>-*, unpacks into a safe workspace, rejects unsafe zip members and duplicates, enforces exactly one .plg and at least one .txz, and stages files for deploy.
Cloudflare upload and PR notification
.github/workflows/upload-pr-plugin.yml
Syncs staged files to Cloudflare R2 under unraid-api/tag/PR<pr_number> using aws s3 sync, then posts/updates a sticky PR comment with the preview .plg URL.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Poem

I hop through CI with a tiny spin,
Preview plugins packed neatly within,
R2 doors now open, secrets optional and bright,
PR links appear, a bunny's delight,
Hooray—previews land under moonlit light! 🐇✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: forked plugin publish workflow' directly and specifically identifies the main change: fixing the plugin publication workflow for forked repositories.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/fix-forked-plugin-publish

Comment @coderabbitai help to get the list of available commands and usage tips.

Purpose of the change:
- Make the merged-PR plugin closeout workflow reliable for forked and non-plugin PRs.

How behavior was before:
- The workflow ran on pull_request closed events and tried to use Cloudflare secrets directly.
- It failed when no successful plugin artifact existed for the PR.
- Fork-originated runs had no secret source, so any later R2 operation would fail with empty Cloudflare values.

Why that was a problem:
- Closed or merged PRs produced noisy workflow failures even when there was no PR plugin to close out.
- Forked PR plugin redirects could not safely reach Cloudflare using the pull_request event context.

What the new change accomplishes:
- Runs merged-PR closeout from pull_request_target so trusted workflow code can access repository secrets.
- Checks for the existing PR plugin in Cloudflare R2 and exits successfully when there is nothing to close out.
- Rewrites and uploads the staging redirect only when an actual PR plugin exists.

How it works:
- The workflow validates the PR number and expected R2 key format.
- It downloads the existing PR plugin from R2, bumps its version, changes the plugin URL to staging, clears old PR artifacts, then uploads the redirect plugin.
- R2 commands now use quoted endpoint values, isolated AWS config, and explicit secret validation.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/upload-pr-plugin.yml:
- Line 31: Replace mutable action tags with immutable commit SHAs: update the
uses entry "actions/github-script@v7" to the corresponding full commit SHA
(e.g., "actions/github-script@<commit-sha>") and do the same for other uses
entries mentioned (lines referenced include the other occurrences like the ones
currently using "`@v2`" or "`@v7`"); find the latest tested commit SHAs from each
action's GitHub repo and pin them so the workflow uses the fixed commit SHAs
instead of mutable tags.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9dd213ff-e1a1-45c0-811e-ef518dd2e634

📥 Commits

Reviewing files that changed from the base of the PR and between 329f66f and 4a8cdf0.

📒 Files selected for processing (2)
  • .github/workflows/build-plugin.yml
  • .github/workflows/upload-pr-plugin.yml

Comment thread .github/workflows/upload-pr-plugin.yml Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4a8cdf0fe7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/upload-pr-plugin.yml Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
.github/workflows/push-staging-pr-on-close.yml (1)

122-122: 💤 Low value

Note: .*? in sed is not non-greedy.

In sed extended regex, ? is a quantifier (0 or 1 occurrences), not a non-greedy modifier. The .*? here is equivalent to .* followed by an optional empty string, which is just .*. The code works because entity declarations typically have only one "> per line, but the ? is misleading.

Consider removing the ? for clarity, or if non-greedy is truly needed, use a negated character class:

Suggested clarification
-          sed -i -E "s#(<!ENTITY plugin_url \").*?(\">)#\1${url}\2#g" "${plgfile}" || exit 1
+          sed -i -E "s#(<!ENTITY plugin_url \")[^\"]*(\">)#\1${url}\2#g" "${plgfile}" || exit 1
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/push-staging-pr-on-close.yml at line 122, The sed regex
uses ".*?" which is not a non-greedy operator in sed; update the expression in
the sed command that targets "(<!ENTITY plugin_url \").*?(\">)" so it no longer
contains the misleading "?" — either remove the "?" (i.e. use "(<!ENTITY
plugin_url \").*(\">)") or, to enforce non-greedy matching, replace ".*?" with a
negated character class like "[^"]*" so it becomes "(<!ENTITY plugin_url
\")[^"]*(\">)"; modify the sed invocation that writes to "${plgfile}" and
continues to substitute with "${url}" accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/push-staging-pr-on-close.yml:
- Around line 39-50: The workflow currently interpolates the user-provided
inputs.pr_number directly into the shell which can lead to template injection;
instead pass the input via an environment variable and then read that env var
inside the script (avoid direct use of `${{ inputs.pr_number }}` in the script
body). Concretely: set an env var (e.g., PR_INPUT) to `inputs.pr_number` in the
step's env context, then inside the script assign PR_NUMBER from that env var
(or from github.event.pull_request.number when appropriate), keep the numeric
validation on PR_NUMBER (the existing regex check) and write the validated
PR_NUMBER to GITHUB_OUTPUT; ensure all expansions are quoted to prevent
word-splitting or command injection when assigning or echoing PR_NUMBER.

---

Nitpick comments:
In @.github/workflows/push-staging-pr-on-close.yml:
- Line 122: The sed regex uses ".*?" which is not a non-greedy operator in sed;
update the expression in the sed command that targets "(<!ENTITY plugin_url
\").*?(\">)" so it no longer contains the misleading "?" — either remove the "?"
(i.e. use "(<!ENTITY plugin_url \").*(\">)") or, to enforce non-greedy matching,
replace ".*?" with a negated character class like "[^"]*" so it becomes
"(<!ENTITY plugin_url \")[^"]*(\">)"; modify the sed invocation that writes to
"${plgfile}" and continues to substitute with "${url}" accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 6b0cd8c1-7b7b-4ce8-8ed5-04cec08f6526

📥 Commits

Reviewing files that changed from the base of the PR and between 4a8cdf0 and 81cd03c.

📒 Files selected for processing (1)
  • .github/workflows/push-staging-pr-on-close.yml

Comment thread .github/workflows/push-staging-pr-on-close.yml Outdated
@codecov
Copy link
Copy Markdown

codecov Bot commented May 20, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 52.69%. Comparing base (329f66f) to head (dd7ed30).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2014   +/-   ##
=======================================
  Coverage   52.68%   52.69%           
=======================================
  Files        1033     1033           
  Lines       71688    71688           
  Branches     8211     8205    -6     
=======================================
+ Hits        37771    37773    +2     
+ Misses      33791    33789    -2     
  Partials      126      126           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

elibosley added 2 commits May 20, 2026 12:08
Purpose of the change:
- Address CodeRabbit review feedback on the forked plugin publish and PR plugin closeout workflows.

How behavior was before:
- New workflow actions used mutable tags.
- workflow_run upload concurrency could fall back to a non-unique branch name for forked PRs.
- workflow_dispatch input was interpolated directly into the closeout shell script.
- The closeout sed expression used a misleading non-greedy-looking pattern.

Why that was a problem:
- Mutable actions and direct input interpolation are avoidable security risks in workflows that handle repository secrets.
- Non-unique fallback concurrency could cancel unrelated forked upload jobs.
- The sed pattern was confusing and could invite incorrect future edits.

What the new change accomplishes:
- Pins the new workflow actions to immutable SHAs.
- Uses workflow_run.id as the upload concurrency fallback.
- Passes workflow_dispatch PR input through step env before shell validation.
- Uses a quoted-string character class for plugin_url rewriting.

How it works:
- GitHub action refs are replaced with resolved 40-character SHAs.
- The PR number selector reads trusted shell variables and keeps numeric validation.
- The targeted workflow regexes and YAML parse checks now pass.
Purpose of the change:
- Preserve the CodeRabbit fix-loop checklist for PR review traceability.

How behavior was before:
- The checklist existed only as a local untracked file.

Why that was a problem:
- Reviewers could not see the item-by-item evidence for resolved CodeRabbit feedback.

What the new change accomplishes:
- Adds the completed WIP checklist under .codex for the PR branch.

How it works:
- Records the reviewed items, statuses, validation commands, final unresolved-thread check, and resolved thread IDs.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/push-staging-pr-on-close.yml (1)

122-126: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail if the .plg rewrite does not actually happen.

sed -i still exits 0 when the target entity is missing, and this step never enables fail-fast shell options. A malformed plugin fetched from R2 would therefore be re-uploaded unchanged while the workflow reports a successful redirect. Add set -Eeuo pipefail here and verify that both the version and plugin_url entities exist and were rewritten before uploading.

Suggested hardening
       - name: Update Downloaded Plugin to Redirect to Staging
         if: steps.download_pr_plugin.outputs.found == 'true'
         run: |
+          set -Eeuo pipefail
+          IFS=$'\n\t'
+
           # Find the .plg file in the downloaded artifact
           plgfile="pr-release/dynamix.unraid.net.plg"
           if [ ! -f "$plgfile" ]; then
             echo "ERROR: .plg file not found in pr-release/"
             ls -la pr-release/
             exit 1
           fi

+          grep -q '<!ENTITY version "' "$plgfile"
+          grep -q '<!ENTITY plugin_url "' "$plgfile"
+
           echo "Found plugin file: $plgfile"
           
           # Get current version and bump it with current timestamp
           current_version=$(grep '<!ENTITY version' "${plgfile}" | sed -E 's/.*"(.*)".*/\1/')
@@
           # Change the plugin url to point to staging - users will switch to staging on next update
           url="https://preview.dl.unraid.net/unraid-api/dynamix.unraid.net.plg"
           sed -i -E "s#(<!ENTITY plugin_url \")[^\"]*(\">)#\1${url}\2#g" "${plgfile}" || exit 1
+
+          grep -q "<!ENTITY version \"${new_version}\">" "$plgfile"
+          grep -q "<!ENTITY plugin_url \"${url}\">" "$plgfile"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/push-staging-pr-on-close.yml around lines 122 - 126, Add
robust shell failure handling and explicit verification around the .plg
rewrites: enable strict mode at the start of this step with set -Eeuo pipefail,
run the sed substitutions that update the <!ENTITY version> (using new_version
and plgfile) and <!ENTITY plugin_url> (using url and plgfile) as currently
written, then confirm the replacements actually occurred by checking the file
contains the expected new_version and the staging URL (or that sed reported
changes) before proceeding to upload; if either check fails, exit non‑zero so
the workflow fails fast.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In @.github/workflows/push-staging-pr-on-close.yml:
- Around line 122-126: Add robust shell failure handling and explicit
verification around the .plg rewrites: enable strict mode at the start of this
step with set -Eeuo pipefail, run the sed substitutions that update the <!ENTITY
version> (using new_version and plgfile) and <!ENTITY plugin_url> (using url and
plgfile) as currently written, then confirm the replacements actually occurred
by checking the file contains the expected new_version and the staging URL (or
that sed reported changes) before proceeding to upload; if either check fails,
exit non‑zero so the workflow fails fast.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 16546901-1ae4-4448-a4e7-53382b9f6cb0

📥 Commits

Reviewing files that changed from the base of the PR and between 81cd03c and ca2db18.

📒 Files selected for processing (2)
  • .github/workflows/push-staging-pr-on-close.yml
  • .github/workflows/upload-pr-plugin.yml

@elibosley elibosley merged commit eb60d4d into main May 20, 2026
13 checks passed
@elibosley elibosley deleted the codex/fix-forked-plugin-publish branch May 20, 2026 19:48
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