feat: support explicit custom key for close-older matching#21076
feat: support explicit custom key for close-older matching#21076
Conversation
…ion (#close-older-key) Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
|
@copilot set close-older-key on one of the smoke-* agentic workflow |
There was a problem hiding this comment.
Pull request overview
Adds an explicit close-older-key configuration option to make close-older deduplication deterministic across reusable workflow callers, and wires discussion close-older behavior after successful discussion creation.
Changes:
- Introduce
close-older-keyacross schema, Go config structs, and safe-outputs compiler output. - Embed and search on a new
<!-- gh-aw-close-key: <value> -->marker to drive close-older matching (issues + discussions). - Add/expand JS close-older logic and tests, including key precedence and empty-workflowId behavior.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/workflow/create_issue.go | Adds close-older-key to issue config struct for YAML parsing. |
| pkg/workflow/create_discussion.go | Adds close-older-key to discussion config struct for YAML parsing. |
| pkg/workflow/compiler_safe_outputs_config.go | Emits close_older_key into JS handler configs for issues/discussions. |
| pkg/parser/schemas/main_workflow_schema.json | Extends workflow schema with close-older-key for issues/discussions. |
| actions/setup/js/generate_footer.cjs | Adds helper functions to generate/search the close-key marker. |
| actions/setup/js/create_issue.cjs | Parses close_older_key, embeds marker, forwards key to close-older logic. |
| actions/setup/js/create_discussion.cjs | Parses close_older_key, embeds marker, forwards to fallback issue creation, and calls close-older after creating a discussion. |
| actions/setup/js/close_older_issues.cjs | Uses close-key marker as primary search + exact-match predicate when provided. |
| actions/setup/js/close_older_discussions.cjs | Same close-key primary matching behavior for discussions. |
| actions/setup/js/close_older_issues.test.cjs | Adds tests for close-key search query construction and precedence. |
| actions/setup/js/close_older_discussions.test.cjs | Adds tests for close-key search query construction and empty-workflowId behavior. |
Comments suppressed due to low confidence (1)
pkg/parser/schemas/main_workflow_schema.json:4252
- The schema only enforces
minLength: 1forclose-older-key, but the runtime code trims the value and treats all-whitespace as unset. Consider adding apatternthat requires at least one non-whitespace character (and optionally forbids newlines /-->) to prevent configurations that will silently disable key-based matching.
"close-older-key": {
"type": "string",
"description": "Optional explicit deduplication key for close-older matching. When set, a `<!-- gh-aw-close-key: <value> -->` marker is embedded in the issue body and used as the primary key for searching and filtering older issues instead of the workflow-id markers. This gives deterministic isolation across caller workflows and is stable across workflow renames.",
"minLength": 1
},
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| "close-older-key": { | ||
| "type": "string", | ||
| "description": "Optional explicit deduplication key for close-older matching. When set, a `<!-- gh-aw-close-key: <value> -->` marker is embedded in the discussion body and used as the primary key for searching and filtering older discussions instead of the workflow-id markers. This gives deterministic isolation across caller workflows and is stable across workflow renames.", | ||
| "minLength": 1 |
actions/setup/js/create_issue.cjs
Outdated
| const { defaultTargetRepo, allowedRepos } = resolveTargetRepoConfig(config); | ||
| const groupEnabled = parseBoolTemplatable(config.group, false); | ||
| const closeOlderIssuesEnabled = parseBoolTemplatable(config.close_older_issues, false); | ||
| const closeOlderKey = config.close_older_key ? String(config.close_older_key).trim() : ""; |
| const fallbackToIssue = config.fallback_to_issue !== false; // Default to true | ||
| const closeOlderDiscussions = parseBoolTemplatable(config.close_older_discussions, false); | ||
| const closeOlderDiscussionsEnabled = parseBoolTemplatable(config.close_older_discussions, false); | ||
| const closeOlderKey = config.close_older_key ? String(config.close_older_key).trim() : ""; |
| function generateCloseKeyMarker(closeKey) { | ||
| return `<!-- gh-aw-close-key: ${closeKey} -->`; | ||
| } | ||
|
|
||
| /** | ||
| * Gets the close-key marker content (without XML comment wrapper) for searching. | ||
| * This is used when searching for issues/discussions by close-older-key. | ||
| * | ||
| * @param {string} closeKey - User-supplied close-older deduplication key | ||
| * @returns {string} Close-key marker content for search queries | ||
| */ | ||
| function getCloseKeyMarkerContent(closeKey) { | ||
| return `gh-aw-close-key: ${closeKey}`; | ||
| } |
|
@copilot apply comments. normalize older key before writing down (convert to identifier style string), fail for empty |
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Done in d8cf827. Added |
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Done in 1eb0cab. Added |
Sets close-older-key from the title-prefix input on every workflow that creates issues, giving deterministic deduplication that won't collide when multiple caller workflows share a reusable workflow. Requires a compiler bump to a gh-aw version that includes github/gh-aw#21076 (close-older-key support). Made-with: Cursor
close-older-issues/close-older-discussionsdeduplication relies ongh-aw-workflow-id/gh-aw-workflow-call-idmarkers, which can collide when multiple caller workflows share a reusable workflow. This adds an optionalclose-older-keyto give users a stable, explicit deduplication key that bypasses marker-identity inference entirely.New config
Behavior
close-older-keyis set: the value is normalized to identifier style (lowercase, alphanumeric/dash/underscore only), then<!-- gh-aw-close-key: <normalized-value> -->is embedded in the body and used as the primary GitHub search term and exact-match filter — workflow-id markers are ignored for matchingChanges
generate_footer.cjs— newnormalizeCloseOlderKey()helper (identifier-style normalization),generateCloseKeyMarker()/getCloseKeyMarkerContent()helpersclose_older_issues.cjs/close_older_discussions.cjs— accept optionalcloseOlderKey; when set, substitute the close-key marker as the search term and exact-match predicatecreate_issue.cjs— readsconfig.close_older_key, normalizes it, embeds marker in body, forwards tocloseOlderIssues(); throws on empty-after-normalizationcreate_discussion.cjs— same; additionally wires in the previously-missingcloseOlderDiscussions()call after successful direct discussion creation (existing gap)create_issue.go,create_discussion.go) — addCloseOlderKey stringfieldcompiler_safe_outputs_config.go— emitclose_older_keyinto JS handler config for both issue and discussion buildersmain_workflow_schema.json— addclose-older-keystring property withpattern: "\\S"(rejects whitespace-only values) to both issue and discussion schemasnormalizeCloseOlderKeynormalization behaviorsmoke-copilot.md— addedclose-older-key: "smoke-copilot"to bothcreate-issueandcreate-discussionas a smoke test of the new feature✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.