Skip to content

Commit 2c2bbb3

Browse files
authored
Fix duplicate GitHub App token step in safe-outputs job (#16135)
1 parent 5878d89 commit 2c2bbb3

2 files changed

Lines changed: 48 additions & 8 deletions

File tree

pkg/workflow/compiler_safe_outputs_job.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,8 @@ func (c *Compiler) buildConsolidatedSafeOutputsJob(data *WorkflowData, mainJobNa
3535
// Track whether threat detection job is enabled for step conditions
3636
threatDetectionEnabled := data.SafeOutputs.ThreatDetection != nil
3737

38-
// Add GitHub App token minting step if app is configured
39-
if data.SafeOutputs.App != nil {
40-
consolidatedSafeOutputsJobLog.Print("Adding GitHub App token minting step")
41-
// Prepend GitHub App token step before other steps
42-
appTokenSteps := c.buildGitHubAppTokenMintStep(data.SafeOutputs.App, permissions)
43-
steps = append(steps, appTokenSteps...)
44-
}
38+
// Note: GitHub App token minting step is added later (after setup/downloads)
39+
// to ensure proper step ordering. See insertion logic below.
4540

4641
// Add setup action to copy JavaScript files
4742
setupActionRef := c.resolveActionReference("./actions/setup", data)
@@ -244,7 +239,7 @@ func (c *Compiler) buildConsolidatedSafeOutputsJob(data *WorkflowData, mainJobNa
244239
// Add GitHub App token minting step at the beginning if app is configured
245240
if data.SafeOutputs.App != nil {
246241
appTokenSteps := c.buildGitHubAppTokenMintStep(data.SafeOutputs.App, permissions)
247-
// Calculate insertion index: after setup action (if present) and artifact downloads, but before safe output steps
242+
// Calculate insertion index: after setup action (if present) and artifact downloads, but before checkout and safe output steps
248243
insertIndex := 0
249244

250245
// Count setup action steps (checkout + setup if in dev mode without action-tag, or just setup)
@@ -271,6 +266,9 @@ func (c *Compiler) buildConsolidatedSafeOutputsJob(data *WorkflowData, mainJobNa
271266
insertIndex += len(patchDownloadSteps)
272267
}
273268

269+
// Note: App token step must be inserted BEFORE shared checkout steps
270+
// because those steps reference steps.safe-outputs-app-token.outputs.token
271+
274272
// Insert app token steps
275273
var newSteps []string
276274
newSteps = append(newSteps, steps[:insertIndex]...)

pkg/workflow/compiler_safe_outputs_job_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,3 +466,45 @@ func TestJobDependencies(t *testing.T) {
466466
})
467467
}
468468
}
469+
470+
// TestGitHubAppWithPushToPRBranch tests that GitHub App token step is not duplicated
471+
// when both app and push-to-pull-request-branch are configured
472+
// Regression test for duplicate step bug reported in issue
473+
func TestGitHubAppWithPushToPRBranch(t *testing.T) {
474+
compiler := NewCompiler()
475+
compiler.jobManager = NewJobManager()
476+
477+
workflowData := &WorkflowData{
478+
Name: "Test Workflow",
479+
SafeOutputs: &SafeOutputsConfig{
480+
App: &GitHubAppConfig{
481+
AppID: "${{ vars.ACTIONS_APP_ID }}",
482+
PrivateKey: "${{ secrets.ACTIONS_PRIVATE_KEY }}",
483+
},
484+
PushToPullRequestBranch: &PushToPullRequestBranchConfig{},
485+
},
486+
}
487+
488+
job, _, err := compiler.buildConsolidatedSafeOutputsJob(workflowData, string(constants.AgentJobName), "test.md")
489+
490+
require.NoError(t, err, "Should successfully build job")
491+
require.NotNil(t, job, "Job should not be nil")
492+
493+
stepsContent := strings.Join(job.Steps, "")
494+
495+
// Should include app token minting step exactly once
496+
tokenMintCount := strings.Count(stepsContent, "Generate GitHub App token")
497+
assert.Equal(t, 1, tokenMintCount, "App token minting step should appear exactly once, found %d times", tokenMintCount)
498+
499+
// Should include app token invalidation step exactly once
500+
tokenInvalidateCount := strings.Count(stepsContent, "Invalidate GitHub App token")
501+
assert.Equal(t, 1, tokenInvalidateCount, "App token invalidation step should appear exactly once, found %d times", tokenInvalidateCount)
502+
503+
// Token step should come before checkout step (checkout references the token)
504+
tokenIndex := strings.Index(stepsContent, "Generate GitHub App token")
505+
checkoutIndex := strings.Index(stepsContent, "Checkout repository")
506+
assert.Less(t, tokenIndex, checkoutIndex, "Token minting step should come before checkout step")
507+
508+
// Verify step ID is set correctly
509+
assert.Contains(t, stepsContent, "id: safe-outputs-app-token")
510+
}

0 commit comments

Comments
 (0)