Skip to content

feat: prevent GitHub App safe-outputs from self-cancelling issue_comment workflows#22825

Merged
pelikhan merged 3 commits intomainfrom
copilot/warn-when-github-app-authored-safe-outputs
Mar 25, 2026
Merged

feat: prevent GitHub App safe-outputs from self-cancelling issue_comment workflows#22825
pelikhan merged 3 commits intomainfrom
copilot/warn-when-github-app-authored-safe-outputs

Conversation

Copy link
Contributor

Copilot AI commented Mar 25, 2026

When a workflow combines safe-outputs.github-app with issue_comment-capable triggers (slash_command, command:, explicit issue_comment:), App-authored comments from safe-outputs re-trigger the same workflow. Those passive bot runs resolve to the same workflow-level concurrency group as the primary run, causing cancel-in-progress: true to cancel the originating run mid-flight.

Changes

Auto-fix: bot-actor isolation in generated concurrency keys (concurrency.go)

When the dangerous combination is detected, the auto-generated concurrency group key is prefixed with contains(github.actor, '[bot]') && github.run_id, routing all bot-triggered runs to a unique per-run key:

# Before (vulnerable to self-cancellation)
concurrency:
  group: "gh-aw-${{ github.workflow }}-${{ github.event.issue.number || github.run_id }}"

# After (bot runs get unique key, no collision with primary run)
concurrency:
  group: "gh-aw-${{ github.workflow }}-${{ contains(github.actor, '[bot]') && github.run_id || github.event.issue.number || github.run_id }}"

Applied automatically for slash_command, command:, and explicit issue_comment: triggers when safe-outputs.github-app is configured. Workflows with custom concurrency: are not modified.

New helpers:

  • hasIssueCommentTrigger() — detects issue_comment-capable triggers
  • hasBotSelfCancelRisk() — detects the dangerous combination
  • botIsolatedConcurrencyKey() — builds the bot-isolated key expression

Warning for custom concurrency (compiler.go)

When the user provides their own concurrency: block with cancel-in-progress: true and the dangerous combination is present, a compiler warning is emitted explaining the risk and the required fix (contains(github.actor, '[bot]') && github.run_id || prefix).

Tests (concurrency_test.go)

  • TestHasBotSelfCancelRisk — detection logic for all trigger/safe-outputs combinations
  • TestBotActorIsolationInConcurrencyKeys — verifies bot prefix applied/absent correctly
  • TestGenerateConcurrencyConfigWithBotIsolation — end-to-end concurrency string output

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/graphql
    • Triggering command: /usr/bin/gh /usr/bin/gh api graphql -f query=query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } -f owner=github -f name=gh-aw GOMOD GOMODCACHE go env ck '**/*.cjs' '*GOINSECURE GO111MODULE 64/bin/go GOINSECURE GOMOD ode-gyp-bin/node-json go (http block)
    • Triggering command: /usr/bin/gh /usr/bin/gh api graphql -f query=query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } -f owner=github -f name=gh-aw GOMOD GOMODCACHE rtcfg env ripts/lint_errorGOINSECURE GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE ache/go/1.25.0/xGOPROXY (http block)
    • Triggering command: /usr/bin/gh /usr/bin/gh api graphql -f query=query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } -f owner=github -f name=gh-aw GOMOD GOMODCACHE rtcfg env ternal/tools/actGOINSECURE GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE ache/go/1.25.0/xGOPROXY (http block)
  • https://api.github.com/orgs/test-owner/actions/secrets
    • Triggering command: /usr/bin/gh gh api /orgs/test-owner/actions/secrets --jq .secrets[].name ck '**/*.cjs' '*GOINSECURE GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE fa04f7f04dc6e954-C GOMODCACHE go (http block)
  • https://api.github.com/repos/actions/ai-inference/git/ref/tags/v1
    • Triggering command: /usr/bin/gh gh api /repos/actions/ai-inference/git/ref/tags/v1 --jq .object.sha --show-toplevel sh /usr/bin/git "prettier" --chegit go 64/bin/go git conf�� user.email test@example.com /opt/hostedtoolcache/node/24.14.0/x64/bin/node -json GO111MODULE 64/bin/go /opt/hostedtoolcache/node/24.14.0/x64/bin/node (http block)
  • https://api.github.com/repos/actions/checkout/git/ref/tags/v3
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v3 --jq .object.sha 5852749/b441/styles.test GOPROXY (http block)
  • https://api.github.com/repos/actions/checkout/git/ref/tags/v5
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha -json GO111MODULE ache/go/1.25.0/x64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE ache/go/1.25.0/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha needs.build.outputs.version go /usr/bin/git le-frontmatter.mgit GO111MODULE 64/bin/go git rev-�� --show-toplevel go /usr/bin/git -json GO111MODULE 64/bin/go git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha --show-toplevel go /usr/bin/git -json GO111MODULE /opt/hostedtoolc--show-toplevel git rev-�� --show-toplevel go 0/x64/bin/node -json GO111MODULE /opt/hostedtoolc--show-toplevel git (http block)
  • https://api.github.com/repos/actions/checkout/git/ref/tags/v6
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq .object.sha -test.paniconexit0 -test.v=true /usr/lib/git-core/git -test.timeout=10git -test.run=^Test -test.short=true--show-toplevel /usr/lib/git-core/git main�� nt/action/git/ref/tags/v999.999.999 --auto /usr/bin/git --detach **/*.ts 64/bin/go git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq .object.sha /tmp/go-build1525852749/b447/timeutil.test -importcfg /usr/bin/git -s -w -buildmode=exe git chec�� .github/workflows/test.md -extld=gcc /usr/bin/git --check **/*.cjs 64/bin/go git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq .object.sha --show-toplevel go /usr/bin/git -json GO111MODULE ache/go/1.25.0/x--show-toplevel git rev-�� --show-toplevel go /usr/bin/git 5231-52614/test-git GO111MODULE 0/x64/bin/node git (http block)
  • https://api.github.com/repos/actions/github-script/git/ref/tags/v8
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v8 --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE erignore env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v8 --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE sh (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v8 --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE node (http block)
  • https://api.github.com/repos/actions/setup-go/git/ref/tags/v4
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-go/git/ref/tags/v4 --jq .object.sha -bool -buildtags /usr/bin/git -errorsas -ifaceassert -nilfunc git -C /tmp/gh-aw-test-runs/20260325-035231-52614/test-225390722 rev-parse /usr/bin/git @{u} **/*.cjs nch,headSha,disp--show-toplevel git (http block)
  • https://api.github.com/repos/actions/setup-node/git/ref/tags/v4
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v4 --jq .object.sha /tmp/TestHashConsistency_GoAndJavaScript3124821442/001/test-frontmatter-with-nes--detach go /usr/bin/git -json GO111MODULE 64/bin/go git rev-�� --show-toplevel node /opt/hostedtoolcache/node/24.14.0/x64/bin/node --check **/*.cjs 64/bin/go node (http block)
  • https://api.github.com/repos/actions/upload-artifact/git/ref/tags/v4
    • Triggering command: /usr/bin/gh gh api /repos/actions/upload-artifact/git/ref/tags/v4 --jq .object.sha 5852749/b434/_pkg_.a GO111MODULE 5852749/b434=> GOINSECURE b/gh-aw/pkg/slicrev-parse GOMODCACHE bash --no�� ithub-script/git/ref/tags/v8 GOPROXY /home/REDACTED/.local/bin/bash GOSUMDB GOWORK 64/bin/go 5852749/b434/importcfg (http block)
  • https://api.github.com/repos/github/gh-aw-actions/git/ref/tags/v1.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw-actions/git/ref/tags/v1.0.0 --jq .object.sha -json GO111MODULE /opt/pipx_bin/bash GOINSECURE GOMOD GOMODCACHE bash --no�� --noprofile GOPROXY 5852749/b438/stringutil.test GOSUMDB GOWORK 64/bin/go 5852749/b438/stringutil.test (http block)
  • https://api.github.com/repos/github/gh-aw-actions/git/ref/tags/v1.2.3
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw-actions/git/ref/tags/v1.2.3 --jq .object.sha 5231-52614/test-225390722 GO111MODULE /snap/bin/bash l GOMOD GOMODCACHE bash --no�� k/gh-aw/gh-aw/.github/workflows GOPROXY ache/go/1.25.0/x64/pkg/tool/linux_amd64/vet GOSUMDB GOWORK 64/bin/go ache/go/1.25.0/x64/pkg/tool/linux_amd64/vet (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/1/artifacts
    • Triggering command: /usr/bin/gh gh run download 1 --dir test-logs/run-1 GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile env -json GO111MODULE 0/x64/bin/bash GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/12345/artifacts
    • Triggering command: /usr/bin/gh gh run download 12345 --dir test-logs/run-12345 GO111MODULE x_amd64/vet GOINSECURE GOMOD GOMODCACHE x_amd64/vet env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/12346/artifacts
    • Triggering command: /usr/bin/gh gh run download 12346 --dir test-logs/run-12346 GO111MODULE x_amd64/vet Action pins syngit GOMOD GOMODCACHE x_amd64/vet env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/2/artifacts
    • Triggering command: /usr/bin/gh gh run download 2 --dir test-logs/run-2 GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE x_amd64/link GOINSECURE GOMOD GOMODCACHE x_amd64/link (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/3/artifacts
    • Triggering command: /usr/bin/gh gh run download 3 --dir test-logs/run-3 GO111MODULE x_amd64/link GOINSECURE GOMOD GOMODCACHE x_amd64/link env -json GO111MODULE ache/go/1.25.0/x64/bin/go GOINSECURE GOMOD GOMODCACHE ef/N6GE9dzJuLpfUe9tz4e_/ThKvzodBlPIPkS6j74YO (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/4/artifacts
    • Triggering command: /usr/bin/gh gh run download 4 --dir test-logs/run-4 GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE ache/go/1.25.0/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/5/artifacts
    • Triggering command: /usr/bin/gh gh run download 5 --dir test-logs/run-5 GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE ache/go/1.25.0/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/actions/workflows
    • Triggering command: /usr/bin/gh gh workflow list --json name,state,path 16cd4a5c5f3f40b6GOINSECURE GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE ache/go/1.25.0/xGOPROXY env 233397/b373/_pkgGOSUMDB GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh run list --json databaseId,number,url,status,conclusion,workflowName,createdAt,startedAt,updatedAt,event,headBranch,headSha,displayTitle --workflow nonexistent-workflow-12345 --limit 100 GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE node (http block)
    • Triggering command: /usr/bin/gh gh run list --json databaseId,number,url,status,conclusion,workflowName,createdAt,startedAt,updatedAt,event,headBranch,headSha,displayTitle --workflow nonexistent-workflow-12345 --limit 6 GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v1.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v1.0.0 --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE ache/go/1.25.0/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v1.2.3
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v1.2.3 --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE node (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v2.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v2.0.0 --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE node (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v2.0.0 --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE node /opt�� run lint:cjs 64/bin/go GOSUMDB GOWORK 64/bin/go sh (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v2.0.0 --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE sh -c npx prettier --c-errorsas GOPROXY 64/bin/go GOSUMDB GOWORK 64/bin/go sh (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v3.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v3.0.0 --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE /bin/sh -c cd actions/setup-errorsas GOPROXY 64/bin/go GOSUMDB GOWORK 64/bin/go sh (http block)
  • https://api.github.com/repos/nonexistent/action/git/ref/tags/v999.999.999
    • Triggering command: /usr/bin/gh gh api /repos/nonexistent/action/git/ref/tags/v999.999.999 --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE Ah/irCVNlwFVr1e4kQn-6MP/w6Oxv7UArev-parse env ithout_min-integrity3475756308/001 GO111MODULE ache/go/1.25.0/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/nonexistent/repo/actions/runs/12345
    • Triggering command: /usr/bin/gh gh run view 12345 --repo nonexistent/repo --json status,conclusion GOINSECURE GOMOD GOMODCACHE go env 1795751090/.github/workflows GO111MODULE ache/go/1.25.0/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/owner/repo/actions/workflows
    • Triggering command: /usr/bin/gh gh workflow list --json name,state,path --repo owner/repo 64/bin/go GOINSECURE GOMOD GOMODCACHE ache/go/1.25.0/xGOPROXY env 233397/b413/_pkgGOSUMDB GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh workflow list --json name,state,path --repo owner/repo 64/bin/go GOINSECURE GOMOD GOMODCACHE ache/go/1.25.0/xGOPROXY env 233397/b393/_pkgGOSUMDB GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/owner/repo/contents/file.md
    • Triggering command: /tmp/go-build1525852749/b402/cli.test /tmp/go-build1525852749/b402/cli.test -test.testlogfile=/tmp/go-build1525852749/b402/testlog.txt -test.paniconexit0 -test.v=true -test.parallel=4 -test.timeout=10m0s -test.run=^Test -test.short=true GOINSECURE GOMOD ode-gyp-bin/sh go env ck 'scripts/**/*GOINSECURE GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/test-owner/test-repo/actions/secrets
    • Triggering command: /usr/bin/gh gh api /repos/test-owner/test-repo/actions/secrets --jq .secrets[].name -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE erignore env e=false GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)

If you need me to access, download, or install something from one of these locations, you can either:


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits March 25, 2026 03:55
…rkflows (#concurrency)

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Agent-Logs-Url: https://github.com/github/gh-aw/sessions/b2acf6df-c3c1-43a4-9e4f-880d4df1f0c7
…mber capture

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Agent-Logs-Url: https://github.com/github/gh-aw/sessions/b2acf6df-c3c1-43a4-9e4f-880d4df1f0c7
Copilot AI changed the title [WIP] Warn when GitHub App-authored safe-outputs can self-cancel workflows feat: prevent GitHub App safe-outputs from self-cancelling issue_comment workflows Mar 25, 2026
Copilot AI requested a review from pelikhan March 25, 2026 03:59
@pelikhan pelikhan marked this pull request as ready for review March 25, 2026 04:01
Copilot AI review requested due to automatic review settings March 25, 2026 04:01
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Prevents GitHub App safe-outputs from inadvertently cancelling the originating run when App-authored issue_comment events re-trigger the same workflow and collide in a shared workflow-level concurrency group.

Changes:

  • Adds bot-actor isolation to auto-generated workflow concurrency group keys when safe-outputs.github-app is used alongside issue_comment-capable triggers.
  • Emits a compiler warning when custom workflow-level concurrency + cancel-in-progress: true is used in the risky configuration (since auto-fix can’t be applied).
  • Adds unit tests covering risk detection and bot-isolated concurrency key generation.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
pkg/workflow/concurrency.go Detects bot self-cancel risk and injects bot-isolated concurrency key logic into auto-generation.
pkg/workflow/compiler.go Warns users when custom concurrency + cancel-in-progress can self-cancel under the risky trigger/safe-outputs combination.
pkg/workflow/concurrency_test.go Adds tests for risk detection and generated bot-isolated concurrency strings.
Comments suppressed due to low confidence (1)

pkg/workflow/compiler.go:241

  • The new custom-concurrency warning triggers solely on cancel-in-progress: true + hasBotSelfCancelRisk(...). As written, it will still warn even if the user’s custom concurrency.group already includes bot isolation (or otherwise avoids collisions, e.g. always uses github.run_id). This produces a misleading warning and suggests an unnecessary change. Consider extracting the group via extractConcurrencyGroupFromYAML(...) and suppressing the warning when the group expression already contains an equivalent bot-isolation guard (e.g. contains(github.actor, '[bot]') && github.run_id) or already guarantees uniqueness for bot-triggered runs.
	if workflowData.Concurrency != "" &&
		strings.Contains(workflowData.Concurrency, "cancel-in-progress: true") &&
		hasBotSelfCancelRisk(workflowData) {
		fmt.Fprintln(os.Stderr, formatCompilerMessage(markdownPath, "warning",
			"Custom workflow-level concurrency with cancel-in-progress: true may cause self-cancellation.\n"+
				"safe-outputs.github-app can post comments that re-trigger this workflow via issue_comment,\n"+
				"and those passive bot-authored runs can collide with the primary run's concurrency group.\n"+
				"Add `contains(github.actor, '[bot]') && github.run_id ||` at the start of your concurrency\n"+
				"group expression to route bot-triggered runs to a unique key and prevent self-cancellation.\n"+
				"See: https://gh.io/gh-aw/reference/concurrency for details."))

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +227 to +243
// Warn when the user has specified custom workflow-level concurrency with cancel-in-progress: true
// AND the workflow has the bot self-cancel risk combination (issue_comment triggers + GitHub App
// safe-outputs). In this case the auto-generated bot-actor isolation cannot be applied because the
// user's concurrency expression is preserved as-is. The user must add the bot-actor isolation
// themselves (e.g. prepend `contains(github.actor, '[bot]') && github.run_id ||` to their group key).
if workflowData.Concurrency != "" &&
strings.Contains(workflowData.Concurrency, "cancel-in-progress: true") &&
hasBotSelfCancelRisk(workflowData) {
fmt.Fprintln(os.Stderr, formatCompilerMessage(markdownPath, "warning",
"Custom workflow-level concurrency with cancel-in-progress: true may cause self-cancellation.\n"+
"safe-outputs.github-app can post comments that re-trigger this workflow via issue_comment,\n"+
"and those passive bot-authored runs can collide with the primary run's concurrency group.\n"+
"Add `contains(github.actor, '[bot]') && github.run_id ||` at the start of your concurrency\n"+
"group expression to route bot-triggered runs to a unique key and prevent self-cancellation.\n"+
"See: https://gh.io/gh-aw/reference/concurrency for details."))
c.IncrementWarningCount()
}
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

This new warning path isn’t covered by tests. There are existing warning-count tests in this package; adding a unit test that asserts (1) warning is emitted/incremented for a risky workflow with custom concurrency + cancel-in-progress: true, and (2) no warning when bot isolation is already present in the custom group, would help prevent regressions.

This issue also appears on line 232 of the same file.

Copilot uses AI. Check for mistakes.
Comment on lines +282 to +287
// When bot risk is detected, prepend the bot-actor isolation check.
if botRisk {
keys = append(keys, "${{ contains(github.actor, '[bot]') && github.run_id || github.event.issue.number || github.event.pull_request.number || github.run_id }}")
} else {
keys = append(keys, "${{ github.event.issue.number || github.event.pull_request.number || github.run_id }}")
}
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

buildConcurrencyGroupKeys hardcodes a bot-isolated expression for the command/slash_command branch instead of reusing the new botIsolatedConcurrencyKey/entityKey helper. This duplicates the bot-isolation logic and makes future edits easy to miss or diverge. Consider using the shared helper to build the expression for this branch as well (passing the issue/PR primary parts and github.run_id tail).

Suggested change
// When bot risk is detected, prepend the bot-actor isolation check.
if botRisk {
keys = append(keys, "${{ contains(github.actor, '[bot]') && github.run_id || github.event.issue.number || github.event.pull_request.number || github.run_id }}")
} else {
keys = append(keys, "${{ github.event.issue.number || github.event.pull_request.number || github.run_id }}")
}
// Bot-actor isolation is applied via entityKey when botRisk is detected.
keys = append(keys, entityKey(
[]string{"github.event.issue.number", "github.event.pull_request.number"},
[]string{"github.run_id"},
))

Copilot uses AI. Check for mistakes.
@pelikhan pelikhan merged commit 1e02c52 into main Mar 25, 2026
173 checks passed
@pelikhan pelikhan deleted the copilot/warn-when-github-app-authored-safe-outputs branch March 25, 2026 04:18
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.

Warn when GitHub App-authored safe-outputs can self-cancel comment-triggered workflows via shared concurrency

3 participants