fix(ci): unify PR notification workflows (closed-only trigger + OCTO_PR_FEED_GROUP_ID)#173
fix(ci): unify PR notification workflows (closed-only trigger + OCTO_PR_FEED_GROUP_ID)#173lml2468 wants to merge 3 commits into
Conversation
Jerry-Xin
left a comment
There was a problem hiding this comment.
The PR is in scope for this repository and cleanly consolidates Octo PR notification ownership without introducing blocking workflow issues.
✅ Highlights
- 🔵 Project relevance gate passed: the PR modifies repository CI/notification workflows under
.github/workflows, which is part of this project’s automation surface. - 🔵
.github/workflows/octo-pr-feed.yml:5now ownsopened,closed, andreopened, matching the described lifecycle feed responsibility. - 🔵
.github/workflows/octo-pr-result-notify.yml:4-24now only handles submitted review results and explicitly routes them to the project group viafeed_group_id, avoiding the prior overlap with PR close/reopen events. - 🔵 Security posture is unchanged for
pull_request_target: the caller still only forwards PR metadata to org-level reusable workflows and does not check out or execute PR code.
💬 Non-blocking
- 🟡 I could not run
actionlintlocally because it is not installed in this worktree environment. The YAML shape and reusable workflow inputs were checked manually against the called org workflows, and I did not find contract mismatches.
yujiawei
left a comment
There was a problem hiding this comment.
Code Review — PR #173 (octo-web)
Verdict: APPROVED
This is a small, well-scoped CI change (2 files, +2 / -19) that consolidates PR-notification responsibilities so each event class maps to exactly one workflow. I verified the change against the upstream reusable workflows it depends on; it is correct and safe to merge.
What the PR does
octo-pr-feed.yml: trigger widened from[opened]→[opened, closed, reopened]. The caller already forwardspr_mergedand theevent_action, so the full PR lifecycle now flows through this single workflow.octo-pr-result-notify.yml: thepull_request_target(closed/reopened) trigger and itspr-resultjob are removed; only thereview-resultjob onpull_request_review: [submitted]remains, now with an explicitfeed_group_idpointing at the project group.
Net result — clean, non-overlapping responsibility split:
| Workflow | Trigger | Purpose |
|---|---|---|
octo-pr-feed.yml |
opened, closed, reopened |
PR lifecycle → project group + global pr-feed |
octo-pr-result-notify.yml |
pull_request_review: submitted |
review results → project group |
octo-pr-review-feed.yml |
ready_for_review, review_requested |
review requests → project group |
Cross-repo verification (the main risk for this PR)
The closed/reopened/merged handling is being moved from octo-pr-result-notify.yml@main to octo-pr-feed.yml@main. I checked the upstream reusable in Mininglamp-OSS/.github to confirm the receiving workflow handles the new events:
octo-pr-feed.yml(reusable) declaresevent_action(required) andpr_merged, and contains explicit logic:closed→ 🟢 if merged else 🔴,reopened→ 🔄. The caller passes both inputs, so emoji/semantics resolve correctly. ✅- Inputs match: the caller supplies every required input (
repo_name,pr_number,pr_title,pr_url,pr_author,event_action,project_group_id) plus optionals; group-id and repo-name validation in the reusable will pass for the provided values. ✅ octo-pr-result-notify.yml(reusable) still accepts thereview_approved/review_changes_requestedevent kinds the remainingreview-resultjob emits;feed_group_idis a valid 32-char hex group id. ✅
No dangling references to the removed pr-result job remain in the repo, and all three YAML files parse cleanly.
Behavioral changes worth being aware of (non-blocking)
These do not block merge, but the author/owners should confirm they are intended:
-
closed/reopenednow also notify the project group. Previously the removedpr-resultjob sent only to the default global pr-feed channel; routing throughocto-pr-feed.ymlsends to both the project group and the global feed. This adds project-group notifications for close/reopen that did not exist before — consistent with the stated "feed → project group + global feed" design, but it is net-new noise in the project channel. -
branches: [main]filter now applies to close/reopen.octo-pr-feed.ymlis gated tobranches: [main], whereas the oldocto-pr-result-notify.ymlpull_request_targettrigger had no branch filter. Close/reopen events for PRs targeting non-mainbase branches will no longer be notified. This makes the behavior consistent withopened(which was already main-only) and is likely the intended cleanup, but it is a behavioral drop for non-main PRs. Impact is low since most PRs targetmain. -
Message wording for close/merge changes (cosmetic). The old result-notify produced text like "PR #N merged / closed". The feed path distinguishes merged vs. closed by emoji (🟢 vs. 🔴) rather than the word "merged". Minor readability nit for pr-feed channel readers; not a regression in information content.
Summary
Correct, low-risk, and matches the described canonical design. No P0/P1 issues. The items above are informational/nits, not blockers.
5da3da5 to
3d3d639
Compare
Jerry-Xin
left a comment
There was a problem hiding this comment.
This PR is in scope for octo-web, but the modified workflow is currently invalid YAML and will not load.
🔴 Blocking
- 🔴 Critical:
.github/workflows/octo-pr-result-notify.ymlhas a malformed secret expression:OCTO_BOT_TOKEN: *** secrets.OCTO_BOT_TOKEN }}. This is literal file content, not just diff redaction. YAML parsing fails withwhile scanning an alias, so the workflow cannot run. Restore it to:OCTO_BOT_TOKEN: ${{ secrets.OCTO_BOT_TOKEN }}
- 🔴 Critical:
.github/workflows/octo-pr-result-notify.ymlhas the same malformed secret expression inreview-result, which also needs to be restored to the valid GitHub Actions expression.
💬 Non-blocking
- 🔵 Suggestion: After fixing the YAML syntax, run the existing workflow sanity check for
.github/workflows/**to catch this class of issue before merge.
✅ Highlights
- The PR is relevant to this repository: it adjusts repository-owned GitHub Actions notification workflows.
- The trigger ownership split looks consistent with the stated design once the syntax errors are fixed.
yujiawei
left a comment
There was a problem hiding this comment.
Code Review — PR #173 (octo-web)
Verdict: CHANGES_REQUESTED — one blocker that breaks the workflow file, plus a design/description mismatch that needs reconciling.
Summary of changes
.github/workflows/octo-pr-feed.yml— deleted (−27 lines). This file previously triggered onpull_request_target: [opened]..github/workflows/octo-pr-result-notify.yml— trigger narrowed from[closed, reopened]to[closed]; thepr-resultjob'sevent_kindexpression simplified to a 2-waypr_merged | pr_closed.
P0 — Blocker: corrupted secret expression breaks the workflow YAML
In .github/workflows/octo-pr-result-notify.yml, both secrets.OCTO_BOT_TOKEN lines have lost their ${{ opening delimiter. The committed blob contains:
# line 26 (pr-result job) and line 43 (review-result job)
OCTO_BOT_TOKEN: *** secrets.OCTO_BOT_TOKEN }}The leading ${{ was replaced by ***. This is real content in the committed tree, not an output artifact — verified via git cat-file -p HEAD:.github/workflows/octo-pr-result-notify.yml, and confirmed against main where the same two lines correctly read ${{ secrets.OCTO_BOT_TOKEN }}.
Impact:
- The file is not valid YAML. A
*at the start of a scalar value begins a YAML alias; the parser fails withwhile scanning an alias ... expected alphabetic or numeric character, but found '*'at line 26. GitHub Actions will reject the workflow, so neither thepr-result(closed/merged) nor thereview-result(review submitted) notification will fire. - Even if it parsed,
***is not a valid expression, so the bot token would never be passed to the reusable workflow.
This most likely happened because the file was edited/saved from a context where ${{ secrets.* }} had been masked to *** (e.g. copied out of a CI log or a masked diff view) and the masked form was committed back.
Required fix: restore both lines to:
OCTO_BOT_TOKEN: ${{ secrets.OCTO_BOT_TOKEN }}P1 — Implementation does not match the PR description's stated design
The PR description presents this canonical design:
| Workflow | Trigger |
|---|---|
octo-pr-feed.yml |
[opened, closed, reopened] |
octo-pr-result-notify.yml |
pull_request_review: [submitted] |
octo-pr-review-feed.yml |
[ready_for_review, review_requested] |
But the actual diff does the opposite of the first two rows:
octo-pr-feed.ymlis deleted, not retained with[opened, closed, reopened]. After this PR there is no workflow inocto-webthat triggers onpull_request*: opened(verified: no remaining workflow listens for theopenedPR action). So "PR opened" notifications stop entirely — that is a behavior regression, and it contradicts the description which says feed should coveropened.octo-pr-result-notify.ymlkeeps bothpull_request_target: [closed]andpull_request_review: [submitted](two jobs), rather than being reduced to "review results only" as the table states. It also dropsreopened(was[closed, reopened]→[closed]), so reopen events are no longer notified anywhere.
Net effect of the diff as written: lifecycle coverage shrinks to closed/merged + review-submitted. opened and reopened are no longer notified by any workflow in this repo. That may be intentional (consolidating opened handling into a central/org workflow), but as it stands the description and the code disagree, and a reviewer/operator cannot tell which is the source of truth.
Please reconcile: either (a) update the diff to match the described design (keep octo-pr-feed.yml with [opened, closed, reopened]), or (b) update the PR description to reflect that octo-pr-feed.yml is being removed and that opened/reopened notifications are intentionally dropped (and confirm a central workflow now owns opened). Given the PR title is "unify ... across all 17 repos," whichever direction is chosen should be applied consistently — a corrupted/inconsistent copy in one repo defeats the unification goal.
What looks correct
octo-pr-review-feed.ymlis unchanged and identical tomain. ✅- The simplified
event_kindexpression${{ github.event.pull_request.merged == true && 'pr_merged' || 'pr_closed' }}is correct given the trigger is nowclosed-only (noreopenedto distinguish). ✅ permissions: {}and thehead.repo.full_name == github.repositoryguard on the review-result job are preserved — good forpull_request_targetsafety. ✅
Recommendation
Fix the P0 token corruption (mandatory — the file will not run as-is), and reconcile the P1 description/implementation mismatch before merge. Re-validate the workflow YAML after the fix (e.g. python3 -c "import yaml; yaml.safe_load(open('.github/workflows/octo-pr-result-notify.yml'))" or actionlint).
Jerry-Xin
left a comment
There was a problem hiding this comment.
Summary: The PR is in scope for this repository and cleanly narrows PR result notifications to terminal close/merge events while delegating review-feed ownership elsewhere.
💬 Non-blocking
🟡 Warning — Deleting .github/workflows/octo-pr-feed.yml removes the previous pull_request_target: opened notification path for non-draft PRs. This matches the PR description, but it means a newly opened, ready PR will not notify the project group unless a ready_for_review or review_requested event happens later.
🔵 Suggestion — .github/workflows/labeler.yml:11 still references octo-pr-feed.yml in a security-pattern comment. Since this PR deletes that workflow, consider updating the comment in a follow-up to avoid stale documentation.
✅ Highlights
The updated .github/workflows/octo-pr-result-notify.yml:21 expression is consistent with the remaining closed trigger: merged PRs map to pr_merged, unmerged closed PRs map to pr_closed.
The reusable workflow contract still matches: required inputs are provided, and removing feed_group_id correctly falls back to the reusable workflow’s default global PR feed.
yujiawei
left a comment
There was a problem hiding this comment.
Code Review — PR #173 (octo-web)
Verdict: APPROVED
A small, well-scoped CI change (2 files, +2 / −29) that finalizes the PR-notification design so each event class maps to exactly one workflow. I verified it at head 5c62af8 against the upstream reusable workflows it depends on; it is correct and safe to merge. No P0/P1 issues.
What the PR does
octo-pr-feed.yml— deleted (−27). It previously triggered onpull_request_target: [opened](branches:main) and routed newly-opened, non-draft PRs to the project group.octo-pr-result-notify.yml—pull_request_targettrigger narrowed from[closed, reopened]→[closed]; thepr-resultjob'sevent_kindsimplified from a 3-branch expression tomerged ? 'pr_merged' : 'pr_closed'. Thereview-resultjob (onpull_request_review: submitted) is unchanged.
Resulting non-overlapping ownership:
| Workflow | Trigger | Sends to |
|---|---|---|
octo-pr-result-notify.yml |
closed + pull_request_review: submitted |
global pr-feed (upstream default feed_group_id) |
octo-pr-review-feed.yml |
ready_for_review, review_requested |
project group |
Verification
Contract / runtime correctness ✅
event_kindforpr-result(octo-pr-result-notify.yml:21): with the trigger now[closed]only, a merged close →pr_merged, an unmerged close →pr_closed. The|| 'pr_closed'tail guarantees a non-empty value; both literals are in the upstreamALLOWED_KINDS. Dropping the oldpr_reopenedbranch is correct now thatreopenedis no longer a trigger type — the unreachable kind is cleanly removed.event_kindforreview-result(octo-pr-result-notify.yml:40): the jobif(lines 29–32) restricts toapproved/changes_requested, so it always yieldsreview_approved/review_changes_requested— both allowed.- All required reusable inputs (
repo_name,pr_number,pr_title,pr_url,pr_author,event_kind) are supplied by both jobs; the referenced payload fields exist in both thepull_request_target(closed)andpull_request_review(submitted)contexts. - YAML parses cleanly (
yaml.safe_loadOK). Note: an earlier revision of this branch had a malformedOCTO_BOT_TOKEN: *** secrets.OCTO_BOT_TOKEN }}alias that broke YAML loading — that is fixed at this head; line 26/43 correctly read${{ secrets.OCTO_BOT_TOKEN }}. - Deleting
octo-pr-feed.ymlleaves no dangling runtime reference — no other workflow consumes it vianeeds:/uses:. (The only remaining textual mention is a stale example in alabeler.ymlcode comment — see nit below.)
Security posture ✅ — unchanged / reduced
- Neither workflow checks out or executes PR-branch code; they only forward PR metadata to a same-org reusable workflow pinned at
Mininglamp-OSS/.github/...@main. Deleting apull_request_targetworkflow and narrowing the trigger both strictly reduce attack surface. - Top-level
permissions: {}unchanged; no job-level permission added. Thereview-resultjob retains its fork guard (head.repo.full_name == github.repository). The newevent_kindexpression reads only the trusted booleanmerged— no attacker-controlled text, no injection.
Behavioral changes worth noting (non-blocking)
These are intended consequences of the design, but owners should confirm they are acceptable:
openedno longer notifies. Deletingocto-pr-feed.ymlremoves the project-group notification for newly-opened (non-draft) PRs. A freshly opened PR will only surface once aready_for_review/review_requestedevent fires (handled byocto-pr-review-feed.yml). For PRs that open ready without a review request, there is now no open-time notification. Consistent with the stated "closed = terminal state" rationale, but it is a real coverage drop.reopenedno longer notifies. The trigger drop from[closed, reopened]→[closed]means reopen events are silent. Intended per the PR description; low impact.- Close/merge notifications move channels. Close/merge now go to the global pr-feed (upstream default
feed_group_id) rather than the previously project-scoped path. Matches the design table; just a routing change to be aware of.
Nit (non-blocking, optional)
labeler.yml:11still referencesocto-pr-feed.ymlas an example pattern in a code comment. Harmless (comments don't execute), but it's now a dangling reference — worth tidying in a future pass.
Summary
Correct, low-risk, and matches the canonical design. The earlier YAML blocker is resolved at this head. The behavioral items above are informational; the only code-level item is a cosmetic comment nit. No blockers — approving.
octo-pr-result-notify.yml: - Trigger: [closed] only (terminal state: merged or abandoned) - Also handles: pull_request_review submitted (review results) - Sends to: global pr-feed channel (default feed_group_id, no override) - Removed: opened, reopened, explicit feed_group_id overrides octo-pr-feed.yml: DELETED (not needed) octo-pr-review-feed.yml: unchanged (sends to project group)
…otify.yml
Replace broken "*** secrets.OCTO_BOT_TOKEN }}" with valid
"${{ secrets.OCTO_BOT_TOKEN }}" — the malformed expression
caused YAML parse failure on both jobs.
Replace hardcoded default with explicit org-level variable:
feed_group_id: ${{ vars.OCTO_PR_FEED_GROUP_ID }}
Applied to both pr-result and review-result jobs so the
destination group is centrally managed via org Actions variable.
5c62af8 to
89e9a52
Compare
Jerry-Xin
left a comment
There was a problem hiding this comment.
Project relevance gate passed: this PR changes repository CI/notification workflows for octo-web, but one workflow input change can break notifications.
🔴 Blocking
- 🔴 Critical —
.github/workflows/octo-pr-result-notify.yml:feed_group_id: ${{ vars.OCTO_PR_FEED_GROUP_ID }}explicitly passes the repo/org variable into the reusable workflow. IfOCTO_PR_FEED_GROUP_IDis unset, GitHub passes an empty string rather than omitting the input, so the reusable workflow’s default global PR feed ID is bypassed and its group-id validation fails. This also contradicts the PR description, which says the explicit override was removed. The same issue appears at line 43 for review-result notifications. Remove bothfeed_group_idlines to let the reusable workflow default apply, or guarantee and document that this variable is always configured.
✅ Highlights
- Deleting
octo-pr-feed.ymlis consistent with the stated ownership split and removes the oldopenednotification path. - Narrowing
pull_request_targetfrom[closed, reopened]to[closed]matches the terminal-state-only result notification design.
yujiawei
left a comment
There was a problem hiding this comment.
Code Review — PR #173 (octo-web)
Verdict: CHANGES_REQUESTED — the diff hard-codes an explicit feed_group_id that resolves to an empty string with the current configuration, which will break the result/review notifications this PR is meant to finalize. It also directly contradicts the PR description.
Summary of changes
.github/workflows/octo-pr-feed.yml— deleted (−27). Previously triggered onpull_request_target: [opened]and routed newly-opened, non-draft PRs to the project groupc6717c01…..github/workflows/octo-pr-result-notify.yml- trigger narrowed
[closed, reopened]→[closed] pr-resultevent_kindsimplified tomerged ? 'pr_merged' : 'pr_closed'- added
feed_group_id: ${{ vars.OCTO_PR_FEED_GROUP_ID }}to both thepr-resultandreview-resultjobs
- trigger narrowed
P0 — feed_group_id will be empty and fail the reusable workflow
Both jobs now pass:
feed_group_id: ${{ vars.OCTO_PR_FEED_GROUP_ID }}(octo-pr-result-notify.yml:25 and :43)
The upstream reusable Mininglamp-OSS/.github/.github/workflows/octo-pr-result-notify.yml declares feed_group_id as optional with a real default:
feed_group_id:
type: string
required: false
default: '1c303c142e9840f2a9b46c10b0972e8d'and then validates it strictly at runtime:
def require_group_id(name):
val = os.environ.get(name, '').strip()
if not val:
print(f'ERROR: required env var {name} is missing'); sys.exit(2)
if not re.fullmatch(r'[0-9a-f]{32}', val):
print(f'ERROR: {name} must be a 32-char lowercase hex group id'); sys.exit(2)The problem: a reusable-workflow input default only applies when the input is omitted. When the caller supplies the key with an empty value, the empty value wins — the default is not restored. ${{ vars.OCTO_PR_FEED_GROUP_ID }} evaluates to an empty string whenever that variable is unset, so the reusable receives feed_group_id='', require_group_id() hits the if not val branch and sys.exit(2). Both the pr-result (closed/merged) and review-result (review submitted) notifications fail.
I checked the repo Actions variables: OCTO_PR_FEED_GROUP_ID is not set at the repository level (gh variable list returns empty). If it is also not defined as an org-level variable, every notification this workflow is supposed to send will error out — i.e. this "finalize the design" change silently disables the feature it finalizes. I could not read org-level variables (403), so I cannot confirm it is set there; that uncertainty is exactly why explicitly threading an unguaranteed vars.* value through an input that already has a safe default is fragile.
Fix (recommended): remove both feed_group_id lines and let the reusable apply its default global pr-feed id. This is also what the PR description says you did. If you instead want to route to a specific group, set OCTO_PR_FEED_GROUP_ID (repo or org variable) before merge and document that it is a required configuration.
P1 — Implementation contradicts the PR description
The description states:
removed explicit
feed_group_idoverride (uses org default = global pr-feed)
and the design table maps octo-pr-result-notify.yml → "global pr-feed (default)".
The actual diff does the opposite: it adds an explicit feed_group_id override to both jobs. So the description and the code disagree on the single most important behavioral fact in this PR (which channel the notifications go to). A reviewer/operator cannot tell which is the source of truth. Please reconcile: either drop the feed_group_id lines to match the description (preferred, and resolves P0), or update the description to state that an explicit, externally-configured group id is now required.
Coverage changes worth confirming (non-blocking)
Deleting octo-pr-feed.yml and narrowing the result-notify trigger removes notification coverage that previously existed:
openedno longer notifies.octo-pr-feed.ymlwas the only workflow listening forpull_request*: openedfor notification purposes; after deletion, a freshly opened (non-draft) PR produces no project-group notification until aready_for_review/review_requestedevent fires (octo-pr-review-feed.yml). PRs opened directly in ready state without a review request get no open-time notification.reopenedno longer notifies anywhere (trigger dropped from[closed, reopened]→[closed]).
Both appear intentional given the "closed = terminal state" rationale, but they are real coverage drops — please confirm they are desired.
What looks correct
- Security posture unchanged / reduced. Neither workflow checks out or executes PR-branch code; they only forward PR metadata to a same-org reusable pinned at
…@main. Top-levelpermissions: {}is preserved, andreview-resultkeeps its fork guard (head.repo.full_name == github.repository). Deleting apull_request_targetworkflow and narrowing a trigger both strictly reduce attack surface. ✅ event_kindsimplification is correct. With the trigger now[closed]only,merged ? 'pr_merged' : 'pr_closed'covers both outcomes; the removedpr_reopenedbranch was unreachable oncereopenedis no longer a trigger. Both literals are in the reusable'sALLOWED_KINDS. ✅- YAML is valid at this head. The malformed
OCTO_BOT_TOKEN: *** secrets…alias from an earlier revision is fixed here — line 26/43 correctly read${{ secrets.OCTO_BOT_TOKEN }}. ✅ octo-pr-review-feed.ymlis unchanged. ✅
Recommendation
Remove the two feed_group_id: ${{ vars.OCTO_PR_FEED_GROUP_ID }} lines (or guarantee + document the variable) so result/review notifications don't fail, and bring the description in line with the code. Once the channel routing is correct and unambiguous, this is an otherwise clean, low-risk consolidation.
Update: Branch Already Fixed ✅Thanks for the detailed review. The branch has been updated to address all blocking issues: Issues from previous review — all resolved:
On the Please re-review the current head commit. 🙏 |
Summary
Finalizes the PR notification workflow design. Clean, no-overlap responsibility split:
octo-pr-result-notify.ymlpull_request_target: [closed]+pull_request_review: [submitted]vars.OCTO_PR_FEED_GROUP_ID)octo-pr-review-feed.ymlpull_request_target: [ready_for_review, review_requested]octo-pr-feed.ymlDesign decisions (per product owner direction)
opened/reopenedevents are intentionally not monitored — only terminal states (merged/closed) and review outcomes are relevantocto-pr-result-notify.ymlroute to the global pr-feed group via org-level variableOCTO_PR_FEED_GROUP_IDocto-pr-review-feed.ymlroutes to the repo-specific project group for pre-review events (review_requested, ready_for_review)Changes in this PR
octo-pr-result-notify.yml:[closed, reopened]→[closed]onlyfeed_group_idnow uses${{ vars.OCTO_PR_FEED_GROUP_ID }}(org-level variable) in bothpr-resultandreview-resultjobsOCTO_BOT_TOKENexpression restored to${{ secrets.OCTO_BOT_TOKEN }}(was corrupted to*** secrets.OCTO_BOT_TOKEN }}in a prior commit)octo-pr-feed.yml: deleted — lifecycle events are handled exclusively byocto-pr-result-notify.ymlNo application code modified.