feat(ci): auto-set Module field when adding items to project board#43
feat(ci): auto-set Module field when adding items to project board#43lml2468 wants to merge 1 commit into
Conversation
Jerry-Xin
left a comment
There was a problem hiding this comment.
Clean, well-structured addition.
Security: No checkout under pull_request_target (good — preserves the existing security boundary). Dynamic values (ITEM_ID, REPO_NAME) are injected via env block rather than ${{ }} interpolation inside the script body, avoiding script-injection risk. Action SHA-pinned.
Correctness: The if: steps.add-project.outputs.itemId gate and continue-on-error: true together guarantee the new step is both conditional and non-blocking. Unmapped repos log and skip cleanly. The GraphQL mutation shape is correct for singleSelectOptionId.
Backward compatibility: workflow_call interface is unchanged — zero caller-side changes needed.
✅ Highlights:
- Idiomatic use of
actions/github-scriptfor a targeted GraphQL mutation - Defensive by default (
continue-on-error, early return on unmapped repos) - Good inline comments on the option-ID mapping
One minor observation (non-blocking): the "Known callers" header comment could be updated to include openclaw-channel-octo, octo-speech, .github, and community since they now appear in the Module mapping — but that is cosmetic and does not affect correctness.
yujiawei
left a comment
There was a problem hiding this comment.
Code Review — PR #43 (.github)
Verdict
APPROVED — small, well-scoped CI improvement with defensive failure handling and no security or correctness blockers. A few maintainability nits below; none block merge.
What this PR does
Adds a post-step to the reusable auto-add-to-project.yml workflow that, after actions/add-to-project adds an item to the org Project board, sets a single-select Module field on the new item via updateProjectV2ItemFieldValue. Mapping from github.event.repository.name to the option id is hard-coded inside an actions/github-script step.
Strengths
- Reusable-workflow contract preserved. The
workflow_callinterface inauto-add-to-project.ymlis unchanged, so all existing callers keep working without edits. - Non-blocking by design.
continue-on-error: trueplus theif: steps.add-project.outputs.itemIdguard mean a Module-mapping failure or a missing itemId never breaks the primary "add to board" behavior. - Sound
pull_request_targethygiene. The new step does not introduceactions/checkoutand does not interpolate any PR-controlled string into the inline script —ITEM_IDandREPO_NAMEare passed via theenv:block and read throughprocess.env.*, which is the recommended pattern for avoiding script injection ingithub-script. The pre-existingSECURITY:comment block at the top of the file remains accurate. - Action SHAs are correctly pinned.
actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3resolves to the annotatedv9.0.0tag (verified against the upstream tag object).actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fdresolves tov2.0.0.
- Unmapped-repo path is explicit and silent-safe. A repo name not in
REPO_MODULElogs acore.infoline and returns; no error, no partial write. - Header comment updated to reflect the new responsibility ("…and set the Module field on the newly-added Project item based on the source repository.").
Findings
P2 — Maintainability nits (non-blocking)
-
Hard-coded magic IDs with no provenance.
PROJECT_ID,MODULE_FIELD_ID, and everysingleSelectOptionIdare hex strings with only an end-of-line comment for the human-readable label. If a Project admin renames or recreates an option in the UI, this file will silently fall through to thecontinue-on-errorpath on every run for that module, and the symptom (Module just stops getting set) is invisible from the workflow's success/fail signal. Two cheap mitigations to consider, either now or in a follow-up:- Add a brief comment near
REPO_MODULEshowing the GraphQL query used to derive the option ids (something likegh api graphql -f query='{ node(id:"PVT_kwDOEOckHc4BXcvH"){ ... on ProjectV2 { field(name:"Module"){ ... on ProjectV2SingleSelectField { options { id name } } } } } }') so the next maintainer who adds a module doesn't have to rediscover it. - Optionally swap to looking up the option by name at runtime (one extra GraphQL read), so renames in the UI keep working. This trades one API call for one less drift class; reasonable to defer.
- Add a brief comment near
-
"Known callers" header comment is now incomplete. The block at the top of the file lists eight known callers (
octo-web,octo-adapters,octo-matter,octo-smart-summary,octo-admin,octo-lib,octo-deployment,octo-server), butREPO_MODULEalso coversopenclaw-channel-octo,octo-speech,.github, andcommunity. Either extend the comment list or replace it with a pointer toREPO_MODULEas the source of truth, so the two don't continue to drift apart. -
Token-scope assumption is implicit.
secrets.PROJECT_TOKENis reused for bothadd-to-project(which only needs board write) and the newupdateProjectV2ItemFieldValuemutation (which needsprojectscope on the org project). For the existing fine-grained PAT this should already be satisfied —actions/add-to-projectv2 already requires write access to the project — so in practice this is a non-issue, but a one-line comment near the script saying "uses the sameprojectscope as the add-to-project step above" would prevent a future maintainer from accidentally narrowing the token.
Out of scope but worth a future ticket
- Backfill for the 92% historical gap. The PR description correctly identifies that 92% of items on the board have no Module set, but this change only fixes future inserts. A one-off script (or
workflow_dispatchjob) that walks existing items and applies the same mapping would let you delete the "filter by Module is broken" complaint entirely. Not appropriate to bundle into this PR; tracking it as a follow-up keeps the diff minimal here.
Verification I ran locally
- Confirmed
gh pr diffmatches the description: only.github/workflows/auto-add-to-project.ymlchanges (+57/-1). - Resolved both pinned SHAs against
actions/github-scriptandactions/add-to-projectupstream tag objects — both match the v9.0.0 / v2.0.0 releases respectively. - Re-read the
pull_request_targetsecurity comment and confirmed the new step neither checks out PR code nor interpolatesgithub.eventstrings into the inline script body;ITEM_ID/REPO_NAMEare passed viaenv:and read withprocess.env.*.
Recommendation
Ship it. The nits above are best handled as a small follow-up (or just a comment-only commit) rather than holding this PR — the value of getting Module classification on new items now outweighs the cost of any of the maintainability tweaks.
|
Request changes: the GraphQL field/option IDs are valid, but the workflow does not cover all current callers and it hides mutation failures. 🔴 Blocking — issues that must be fixed before merge
💬 Non-blocking — warnings and suggestions
✅ Highlights — what's done well
|
Problem
92% of items on the org Project board had no Module classification, making it impossible to filter or report by component.
Root cause
actions/add-to-projectonly adds items to the board — it does not (and cannot) set any custom field values. The Module field has remained empty for almost every item ever auto-added.Solution
Add a post-add step using
actions/github-scriptthat:itemIdoutput from the existingactions/add-to-projectstep.github.event.repository.name) to the corresponding Module single-select option ID.updateProjectV2ItemFieldValueGraphQL mutation to set the Module field on the newly-added item.Mapping covers all current callers (
octo-server,octo-web,octo-lib,octo-admin,octo-matter,octo-adapters,openclaw-channel-octo,octo-smart-summary,octo-deployment,octo-speech,.github,community). Unmapped repos log and skip.Why this is safe
workflow_callinterface is unchanged — every existing caller keeps working as-is.continue-on-error: trueon the Module step guarantees that a Module-mapping failure never prevents an item from being added to the board.itemId(i.e., the item was actually added).Test plan
octo-server) and verify the item appears on the Project board with Module =server..githubrepo and verify Module =infra.