From bf1aaee176481e088fc9c258ed7ad60aa9406c35 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 26 Feb 2026 06:54:32 +0000 Subject: [PATCH 1/3] Initial plan From fc1a2b479a6b493a1f2b558f536fff860368b6de Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 26 Feb 2026 07:13:39 +0000 Subject: [PATCH 2/3] Add safe-outputs.steps field for user-injected steps in safe-output jobs Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/parser/schemas/main_workflow_schema.json | 15 +++++++++++++ pkg/workflow/compiler_safe_outputs_job.go | 22 ++++++++++++++++++++ pkg/workflow/compiler_types.go | 1 + pkg/workflow/safe_outputs_config.go | 8 +++++++ 4 files changed, 46 insertions(+) diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json index 7ea42dff526..57f7fa1150d 100644 --- a/pkg/parser/schemas/main_workflow_schema.json +++ b/pkg/parser/schemas/main_workflow_schema.json @@ -6949,6 +6949,21 @@ "runs-on": { "type": "string", "description": "Runner specification for all safe-outputs jobs (activation, create-issue, add-comment, etc.). Single runner label (e.g., 'ubuntu-slim', 'ubuntu-latest', 'windows-latest', 'self-hosted'). Defaults to 'ubuntu-slim'. See https://github.blog/changelog/2025-10-28-1-vcpu-linux-runner-now-available-in-github-actions-in-public-preview/" + }, + "steps": { + "type": "array", + "description": "Custom steps to inject into all safe-output jobs. These steps run after checking out the repository and setting up the action, and before any safe-output code executes.", + "items": { + "$ref": "#/$defs/githubActionsStep" + }, + "examples": [ + [ + { + "name": "Install custom dependencies", + "run": "npm install my-package" + } + ] + ] } }, "additionalProperties": false diff --git a/pkg/workflow/compiler_safe_outputs_job.go b/pkg/workflow/compiler_safe_outputs_job.go index c3906899872..c330ebc672e 100644 --- a/pkg/workflow/compiler_safe_outputs_job.go +++ b/pkg/workflow/compiler_safe_outputs_job.go @@ -75,6 +75,28 @@ func (c *Compiler) buildConsolidatedSafeOutputsJob(data *WorkflowData, mainJobNa steps = append(steps, checkoutSteps...) } + // Add user-provided steps after checkout/setup, before safe-output code + if len(data.SafeOutputs.Steps) > 0 { + consolidatedSafeOutputsJobLog.Printf("Adding %d user-provided steps to safe-outputs job", len(data.SafeOutputs.Steps)) + for i, step := range data.SafeOutputs.Steps { + stepMap, ok := step.(map[string]any) + if !ok { + consolidatedSafeOutputsJobLog.Printf("Warning: safe-outputs step at index %d is not a valid step object (must be a map with properties like name, run, uses). Skipping this step.", i) + continue + } + typedStep, err := MapToStep(stepMap) + if err != nil { + return nil, nil, fmt.Errorf("failed to convert safe-outputs step at index %d to typed step: %w", i, err) + } + pinnedStep := ApplyActionPinToTypedStep(typedStep, data) + stepYAML, err := c.convertStepToYAML(pinnedStep.ToMap()) + if err != nil { + return nil, nil, fmt.Errorf("failed to convert safe-outputs step at index %d to YAML: %w", i, err) + } + steps = append(steps, stepYAML) + } + } + // Note: Unlock step has been moved to dedicated unlock job // The safe_outputs job now depends on the unlock job, so the issue // will already be unlocked when this job runs diff --git a/pkg/workflow/compiler_types.go b/pkg/workflow/compiler_types.go index 7c78fa2ada4..57a91a8343f 100644 --- a/pkg/workflow/compiler_types.go +++ b/pkg/workflow/compiler_types.go @@ -526,6 +526,7 @@ type SafeOutputsConfig struct { Footer *bool `yaml:"footer,omitempty"` // Global footer control - when false, omits visible footer from all safe outputs (XML markers still included) GroupReports bool `yaml:"group-reports,omitempty"` // If true, create parent "Failed runs" issue for agent failures (default: false) MaxBotMentions *string `yaml:"max-bot-mentions,omitempty"` // Maximum bot trigger references (e.g. 'fixes #123') allowed before filtering. Default: 10. Supports integer or GitHub Actions expression. + Steps []any `yaml:"steps,omitempty"` // User-provided steps injected after setup/checkout and before safe-output code AutoInjectedCreateIssue bool `yaml:"-"` // Internal: true when create-issues was automatically injected by the compiler (not user-configured) } diff --git a/pkg/workflow/safe_outputs_config.go b/pkg/workflow/safe_outputs_config.go index d4cf92d096c..559aafe32dd 100644 --- a/pkg/workflow/safe_outputs_config.go +++ b/pkg/workflow/safe_outputs_config.go @@ -459,6 +459,14 @@ func (c *Compiler) extractSafeOutputsConfig(frontmatter map[string]any) *SafeOut } } + // Handle steps (user-provided steps injected after checkout/setup, before safe-output code) + if steps, exists := outputMap["steps"]; exists { + if stepsList, ok := steps.([]any); ok { + config.Steps = stepsList + safeOutputsConfigLog.Printf("Configured %d user-provided steps for safe-outputs", len(stepsList)) + } + } + // Handle jobs (safe-jobs must be under safe-outputs) if jobs, exists := outputMap["jobs"]; exists { if jobsMap, ok := jobs.(map[string]any); ok { From a15c27dd6425fef0fbb8b299ad3fa40254131f6a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 26 Feb 2026 07:22:53 +0000 Subject: [PATCH 3/3] Merge safe-outputs.steps from imported workflows Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/imports.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/workflow/imports.go b/pkg/workflow/imports.go index dc7f4af4304..f39c03ac971 100644 --- a/pkg/workflow/imports.go +++ b/pkg/workflow/imports.go @@ -686,6 +686,11 @@ func mergeSafeOutputConfig(result *SafeOutputsConfig, config map[string]any, c * result.Mentions = importedConfig.Mentions } + // Merge steps: concatenate imported steps after main workflow's steps + if len(importedConfig.Steps) > 0 { + result.Steps = append(result.Steps, importedConfig.Steps...) + } + // NOTE: Jobs are NOT merged here. They are handled separately in compiler_orchestrator.go // via mergeSafeJobsFromIncludedConfigs and extractSafeJobsFromFrontmatter. // The Jobs field is managed independently from other safe-output types to support