From 6007730d066d72899fbf95870ad6979dd54f2e66 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Apr 2026 18:12:34 +0000 Subject: [PATCH 1/3] Initial plan From 40cdd56a11522fffc9b42c1e94aa31511a54460c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Apr 2026 18:23:10 +0000 Subject: [PATCH 2/3] test(parser): add regression for create-pull-request allowed-base-branches Agent-Logs-Url: https://github.com/github/gh-aw/sessions/9cd399bf-aee7-4e05-b57b-3908892d1a7f Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/parser/schema_location_test.go | 33 ++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/pkg/parser/schema_location_test.go b/pkg/parser/schema_location_test.go index 72deb3f58b3..84a789ac18c 100644 --- a/pkg/parser/schema_location_test.go +++ b/pkg/parser/schema_location_test.go @@ -274,3 +274,36 @@ func TestValidateMainWorkflowFrontmatterWithSchemaAndLocation_AdditionalProperti }) } } + +func TestValidateMainWorkflowFrontmatterWithSchemaAndLocation_AcceptsAllowedBaseBranchesInCreatePullRequest(t *testing.T) { + frontmatter := map[string]any{ + "on": map[string]any{ + "workflow_dispatch": map[string]any{}, + }, + "permissions": map[string]any{ + "contents": "read", + "pull-requests": "read", + }, + "engine": map[string]any{ + "id": "copilot", + "model": "gpt-5.4", + }, + "network": map[string]any{ + "allowed": []any{"defaults"}, + }, + "tools": map[string]any{ + "edit": map[string]any{}, + "bash": true, + }, + "safe-outputs": map[string]any{ + "create-pull-request": map[string]any{ + "allowed-base-branches": []any{"main", "release/*"}, + }, + }, + } + + err := ValidateMainWorkflowFrontmatterWithSchemaAndLocation(frontmatter, "/test/workflow.md") + if err != nil { + t.Fatalf("expected allowed-base-branches to be accepted under safe-outputs.create-pull-request, got error: %v", err) + } +} From fd99d12d30473e8ddfab9f102bef4868d404a100 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Apr 2026 23:01:39 +0000 Subject: [PATCH 3/3] test: cover allowed-base-branches in schema and compile integration Agent-Logs-Url: https://github.com/github/gh-aw/sessions/26060a2d-7a7c-48d8-84cd-98443f32de78 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/cli/compile_integration_test.go | 50 +++++++++++++++++++ pkg/parser/schema_test.go | 77 +++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) diff --git a/pkg/cli/compile_integration_test.go b/pkg/cli/compile_integration_test.go index 6b301c2d03f..bacf3d4da66 100644 --- a/pkg/cli/compile_integration_test.go +++ b/pkg/cli/compile_integration_test.go @@ -865,6 +865,56 @@ Verify staged safe-outputs with create-issue. } } +func TestCompileSafeOutputsCreatePullRequestAllowedBaseBranches(t *testing.T) { + setup := setupIntegrationTest(t) + defer setup.cleanup() + + testWorkflow := `--- +name: Allowed Base Branches +on: + workflow_dispatch: +permissions: read-all +engine: copilot +safe-outputs: + create-pull-request: + allowed-base-branches: + - main + - release/* +--- + +Verify allowed base branches in create-pull-request safe output. +` + testWorkflowPath := filepath.Join(setup.workflowsDir, "allowed-base-branches.md") + if err := os.WriteFile(testWorkflowPath, []byte(testWorkflow), 0644); err != nil { + t.Fatalf("Failed to write test workflow file: %v", err) + } + + cmd := exec.Command(setup.binaryPath, "compile", testWorkflowPath) + output, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("CLI compile command failed: %v\nOutput: %s", err, string(output)) + } + + lockFilePath := filepath.Join(setup.workflowsDir, "allowed-base-branches.lock.yml") + lockContent, err := os.ReadFile(lockFilePath) + if err != nil { + t.Fatalf("Failed to read lock file: %v", err) + } + lockContentStr := string(lockContent) + + if !strings.Contains(lockContentStr, "GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG") { + t.Fatalf("Expected handler config env var in lock file, got:\n%s", lockContentStr) + } + + if !strings.Contains(lockContentStr, "allowed_base_branches") { + t.Fatalf("Expected allowed_base_branches in handler config, got:\n%s", lockContentStr) + } + + if !strings.Contains(lockContentStr, "release/*") { + t.Fatalf("Expected release/* pattern in lock file handler config, got:\n%s", lockContentStr) + } +} + // TestCompileStagedSafeOutputsAddComment verifies that a workflow with staged: true // and an add-comment handler compiles and emits GH_AW_SAFE_OUTPUTS_STAGED. // Prior to the schema fix, staged was not listed in the add-comment handler schema. diff --git a/pkg/parser/schema_test.go b/pkg/parser/schema_test.go index 6baeb7fd1df..2e050c2a980 100644 --- a/pkg/parser/schema_test.go +++ b/pkg/parser/schema_test.go @@ -437,6 +437,83 @@ func TestMainWorkflowSchema_WorkflowDispatchNumberTypeDocumentation(t *testing.T } } +func TestMainWorkflowSchema_CreatePullRequestAllowedBaseBranches(t *testing.T) { + t.Parallel() + + schemaPath := "schemas/main_workflow_schema.json" + schemaContent, err := os.ReadFile(schemaPath) + if err != nil { + t.Fatalf("failed to read schema: %v", err) + } + + var schema map[string]any + if err := json.Unmarshal(schemaContent, &schema); err != nil { + t.Fatalf("failed to parse schema json: %v", err) + } + + properties, ok := schema["properties"].(map[string]any) + if !ok { + t.Fatal("schema properties section not found") + } + + safeOutputs, ok := properties["safe-outputs"].(map[string]any) + if !ok { + t.Fatal("'safe-outputs' field not found in schema") + } + + safeOutputsProperties, ok := safeOutputs["properties"].(map[string]any) + if !ok { + t.Fatal("'safe-outputs.properties' not found in schema") + } + + createPullRequest, ok := safeOutputsProperties["create-pull-request"].(map[string]any) + if !ok { + t.Fatal("'safe-outputs.create-pull-request' not found in schema") + } + + createPullRequestOneOf, ok := createPullRequest["oneOf"].([]any) + if !ok { + t.Fatal("'safe-outputs.create-pull-request.oneOf' not found in schema") + } + + var createPullRequestProperties map[string]any + for _, candidate := range createPullRequestOneOf { + candidateMap, ok := candidate.(map[string]any) + if !ok { + continue + } + + properties, ok := candidateMap["properties"].(map[string]any) + if !ok { + continue + } + + createPullRequestProperties = properties + break + } + if createPullRequestProperties == nil { + t.Fatal("'safe-outputs.create-pull-request' object schema with properties not found") + } + + allowedBaseBranches, ok := createPullRequestProperties["allowed-base-branches"].(map[string]any) + if !ok { + t.Fatal("'allowed-base-branches' not found under safe-outputs.create-pull-request") + } + + if gotType, _ := allowedBaseBranches["type"].(string); gotType != "array" { + t.Fatalf("'allowed-base-branches' should be type array, got: %v", allowedBaseBranches["type"]) + } + + items, ok := allowedBaseBranches["items"].(map[string]any) + if !ok { + t.Fatal("'allowed-base-branches.items' not found in schema") + } + + if gotItemType, _ := items["type"].(string); gotItemType != "string" { + t.Fatalf("'allowed-base-branches.items' should be type string, got: %v", items["type"]) + } +} + func TestGetSafeOutputTypeKeys(t *testing.T) { keys, err := GetSafeOutputTypeKeys() if err != nil {