Skip to content

feat(ci): auto-set Module field when adding items to project board#43

Open
lml2468 wants to merge 1 commit into
mainfrom
feat/auto-set-module
Open

feat(ci): auto-set Module field when adding items to project board#43
lml2468 wants to merge 1 commit into
mainfrom
feat/auto-set-module

Conversation

@lml2468
Copy link
Copy Markdown
Contributor

@lml2468 lml2468 commented May 24, 2026

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-project only 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-script that:

  1. Reads the itemId output from the existing actions/add-to-project step.
  2. Maps the source repository name (github.event.repository.name) to the corresponding Module single-select option ID.
  3. Issues an updateProjectV2ItemFieldValue GraphQL 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

  • Zero caller changes. The reusable workflow's workflow_call interface is unchanged — every existing caller keeps working as-is.
  • Non-blocking. continue-on-error: true on the Module step guarantees that a Module-mapping failure never prevents an item from being added to the board.
  • Conditional. The new step only runs when the add-to-project step produced an itemId (i.e., the item was actually added).

Test plan

  • Open a new issue in a mapped repo (e.g., octo-server) and verify the item appears on the Project board with Module = server.
  • Open a new issue in this .github repo and verify Module = infra.
  • Confirm an unmapped repo logs the skip message and does not fail the workflow.

@lml2468 lml2468 requested a review from a team as a code owner May 24, 2026 15:54
Copy link
Copy Markdown

@Jerry-Xin Jerry-Xin left a comment

Choose a reason for hiding this comment

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

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-script for 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.

Copy link
Copy Markdown

@yujiawei yujiawei left a comment

Choose a reason for hiding this comment

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

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_call interface in auto-add-to-project.yml is unchanged, so all existing callers keep working without edits.
  • Non-blocking by design. continue-on-error: true plus the if: steps.add-project.outputs.itemId guard mean a Module-mapping failure or a missing itemId never breaks the primary "add to board" behavior.
  • Sound pull_request_target hygiene. The new step does not introduce actions/checkout and does not interpolate any PR-controlled string into the inline script — ITEM_ID and REPO_NAME are passed via the env: block and read through process.env.*, which is the recommended pattern for avoiding script injection in github-script. The pre-existing SECURITY: comment block at the top of the file remains accurate.
  • Action SHAs are correctly pinned.
    • actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 resolves to the annotated v9.0.0 tag (verified against the upstream tag object).
    • actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd resolves to v2.0.0.
  • Unmapped-repo path is explicit and silent-safe. A repo name not in REPO_MODULE logs a core.info line 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)

  1. Hard-coded magic IDs with no provenance. PROJECT_ID, MODULE_FIELD_ID, and every singleSelectOptionId are 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 the continue-on-error path 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_MODULE showing the GraphQL query used to derive the option ids (something like gh 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.
  2. "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), but REPO_MODULE also covers openclaw-channel-octo, octo-speech, .github, and community. Either extend the comment list or replace it with a pointer to REPO_MODULE as the source of truth, so the two don't continue to drift apart.

  3. Token-scope assumption is implicit. secrets.PROJECT_TOKEN is reused for both add-to-project (which only needs board write) and the new updateProjectV2ItemFieldValue mutation (which needs project scope on the org project). For the existing fine-grained PAT this should already be satisfied — actions/add-to-project v2 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 same project scope 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

  1. 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_dispatch job) 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 diff matches the description: only .github/workflows/auto-add-to-project.yml changes (+57/-1).
  • Resolved both pinned SHAs against actions/github-script and actions/add-to-project upstream tag objects — both match the v9.0.0 / v2.0.0 releases respectively.
  • Re-read the pull_request_target security comment and confirmed the new step neither checks out PR code nor interpolates github.event strings into the inline script body; ITEM_ID / REPO_NAME are passed via env: and read with process.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.

@lml2468
Copy link
Copy Markdown
Contributor Author

lml2468 commented May 24, 2026

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

  • .github/workflows/auto-add-to-project.yml:88 — the REPO_MODULE map omits active callers of this reusable workflow. I verified that both Mininglamp-OSS/octo-cli and Mininglamp-OSS/octo-daemon-cli have .github/workflows/auto-add-to-project.yml jobs that call Mininglamp-OSS/.github/.github/workflows/auto-add-to-project.yml@main, but neither repository is mapped here. The Project's Module field already has a valid cli option with ID 9ac9e614, so issues/PRs from those repos will still be added to the board but will never get their Module set. Please add mappings for the current callers, at least octo-cli and octo-daemon-cli, or explicitly remove/disable their caller workflows if they should not participate.

  • .github/workflows/auto-add-to-project.yml:77continue-on-error: true makes the new Module-setting behavior best-effort even for real GraphQL failures. If the PAT lacks write access, the Project/field IDs drift, an option ID is removed, or GitHub returns a non-transient GraphQL error, the workflow still goes green and the board item is left without Module. That differs from the fail-closed Project API handling established in PR feat: add reusable check-sprint-on-merge workflow #42's check-sprint workflow. The unmapped-repo case can still skip cleanly in script, but GraphQL/API failures should fail the job so the automation is visibly broken and can be rerun after fixing credentials or IDs.

💬 Non-blocking — warnings and suggestions

  • .github/workflows/auto-add-to-project.yml:85 — the hardcoded Project, field, and option IDs are correct today: I verified PVTSSF_lADOEOckHc4BXcvHzhSpg48 is the Module single-select field and every mapped option ID exists. For maintainability, consider adding a short comment with the GraphQL query/lookup procedure, or resolving the field and option IDs by name at runtime like PR feat: add reusable check-sprint-on-merge workflow #42 resolves project metadata by org/project number and field name.

  • .github/workflows/auto-add-to-project.yml:43 — since this reusable workflow now requires project write access for both adding items and updating Module, it would be useful to document the least-privilege PROJECT_TOKEN scopes in the workflow header, matching the style used by PR feat: add reusable check-sprint-on-merge workflow #42.

✅ Highlights — what's done well

  • The mutation shape is correct for a ProjectV2 single-select field: updateProjectV2ItemFieldValue with value: { singleSelectOptionId: $optionId } and $optionId: String!.

  • The script avoids PR-code execution under pull_request_target; there is no checkout step, actions are SHA-pinned, and dynamic event values are passed through env rather than interpolated into the script body.

  • The itemId guard and unmapped-repo early return handle the expected no-op cases without token exposure.

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.

3 participants