MCPcopy Create free account
hub / github.com/github/gh-aw / buildConcurrencyGroupKeys

Function buildConcurrencyGroupKeys

pkg/workflow/concurrency.go:230–328  ·  view source on GitHub ↗

buildConcurrencyGroupKeys builds an array of keys for the concurrency group

(workflowData *WorkflowData, isCommandTrigger bool)

Source from the content-addressed store, hash-verified

228
229// buildConcurrencyGroupKeys builds an array of keys for the concurrency group
230func buildConcurrencyGroupKeys(workflowData *WorkflowData, isCommandTrigger bool) []string {
231 keys := []string{"gh-aw", "${{ github.workflow }}"}
232
233 // Whether this workflow exposes inputs.item_number via workflow_dispatch (label trigger shorthand).
234 // When true, include it in the concurrency key so that manual dispatches for different items
235 // use distinct groups and don't cancel each other.
236 hasItemNumber := workflowData.HasDispatchItemNumber
237
238 // Detect whether the workflow is at risk of bot-self-cancellation.
239 // When safe-outputs uses a GitHub App token AND the workflow has issue_comment triggers,
240 // App-authored comments can re-trigger the workflow and collide with the primary run's
241 // concurrency group. When this risk is present we use botIsolatedConcurrencyKey so that
242 // bot-triggered runs always resolve to a unique github.run_id key instead.
243 botRisk := hasBotSelfCancelRisk(workflowData)
244 if botRisk {
245 concurrencyLog.Print("Bot self-cancel risk detected: applying bot-actor isolation to concurrency group key")
246 }
247
248 // entityKey selects the correct concurrency key builder based on bot risk.
249 // It captures both botRisk and hasItemNumber from the outer scope, so call
250 // sites only need to supply the entity-specific parts.
251 entityKey := func(primaryParts []string, tailParts []string) string {
252 if botRisk {
253 return botIsolatedConcurrencyKey(primaryParts, tailParts, hasItemNumber)
254 }
255 return entityConcurrencyKey(primaryParts, tailParts, hasItemNumber)
256 }
257
258 if isCommandTrigger || isSlashCommandWorkflow(workflowData.On) {
259 // For command/slash_command workflows: use issue/PR number; fall back to run_id when
260 // neither is available (e.g. manual workflow_dispatch of the outer workflow).
261 // When bot risk is detected, prepend the bot-actor isolation check.
262 concurrencyLog.Print("Building concurrency key for command/slash_command workflow")
263 if botRisk {
264 keys = append(keys, "${{ contains(github.actor, '[bot]') && github.run_id || github.event.issue.number || github.event.pull_request.number || github.run_id }}")
265 } else {
266 keys = append(keys, "${{ github.event.issue.number || github.event.pull_request.number || github.run_id }}")
267 }
268 } else if isPullRequestWorkflow(workflowData.On) && isIssueWorkflow(workflowData.On) {
269 // Mixed workflows with both issue and PR triggers
270 concurrencyLog.Print("Building concurrency key for mixed PR+issue workflow")
271 keys = append(keys, entityKey(
272 []string{"github.event.issue.number", "github.event.pull_request.number"},
273 []string{"github.run_id"},
274 ))
275 } else if isPullRequestWorkflow(workflowData.On) && isDiscussionWorkflow(workflowData.On) {
276 // Mixed workflows with PR and discussion triggers
277 keys = append(keys, entityConcurrencyKey(
278 []string{"github.event.pull_request.number", "github.event.discussion.number"},
279 []string{"github.run_id"},
280 hasItemNumber,
281 ))
282 } else if isIssueWorkflow(workflowData.On) && isDiscussionWorkflow(workflowData.On) {
283 // Mixed workflows with issue and discussion triggers
284 keys = append(keys, entityKey(
285 []string{"github.event.issue.number", "github.event.discussion.number"},
286 []string{"github.run_id"},
287 ))

Calls 9

hasBotSelfCancelRiskFunction · 0.85
entityConcurrencyKeyFunction · 0.85
isSlashCommandWorkflowFunction · 0.85
isPullRequestWorkflowFunction · 0.85
isIssueWorkflowFunction · 0.85
isDiscussionWorkflowFunction · 0.85
isPushWorkflowFunction · 0.85
PrintMethod · 0.80