From f50932df7326136d4f326397cafdfb8bdef54fb2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Apr 2026 15:05:00 +0000 Subject: [PATCH 1/3] Plan: address PR 27837 review comments Agent-Logs-Url: https://github.com/github/gh-aw/sessions/0527ad1c-e697-431a-9eb7-f68ee108bcbe Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/smoke-project.lock.yml | 4 ++-- .github/workflows/test-project-url-default.lock.yml | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/smoke-project.lock.yml b/.github/workflows/smoke-project.lock.yml index 228c765f09..3bd1d6c005 100644 --- a/.github/workflows/smoke-project.lock.yml +++ b/.github/workflows/smoke-project.lock.yml @@ -1130,7 +1130,7 @@ jobs: permissions: contents: write discussions: write - issues: write + issues: read pull-requests: write concurrency: group: "gh-aw-conclusion-smoke-project" @@ -1499,7 +1499,7 @@ jobs: permissions: contents: write discussions: write - issues: write + issues: read pull-requests: write timeout-minutes: 15 env: diff --git a/.github/workflows/test-project-url-default.lock.yml b/.github/workflows/test-project-url-default.lock.yml index 98b8937c41..c4b7f8d3f7 100644 --- a/.github/workflows/test-project-url-default.lock.yml +++ b/.github/workflows/test-project-url-default.lock.yml @@ -892,6 +892,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read + issues: read concurrency: group: "gh-aw-conclusion-test-project-url-default" cancel-in-progress: false @@ -1197,6 +1198,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read + issues: read timeout-minutes: 15 env: GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/test-project-url-default" From 6e437ecd68020b0fac0c16c5229d769c8a8302c1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Apr 2026 15:08:17 +0000 Subject: [PATCH 2/3] Handle PR 27837 review comments for create-project issues read Agent-Logs-Url: https://github.com/github/gh-aw/sessions/0527ad1c-e697-431a-9eb7-f68ee108bcbe Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../content/docs/reference/safe-outputs.md | 2 +- pkg/workflow/safe_outputs_app_test.go | 41 +++++++++++++++++++ pkg/workflow/safe_outputs_permissions.go | 1 + pkg/workflow/safe_outputs_permissions_test.go | 3 +- 4 files changed, 45 insertions(+), 2 deletions(-) diff --git a/docs/src/content/docs/reference/safe-outputs.md b/docs/src/content/docs/reference/safe-outputs.md index 9a6ad7d5c9..bacc031a1c 100644 --- a/docs/src/content/docs/reference/safe-outputs.md +++ b/docs/src/content/docs/reference/safe-outputs.md @@ -476,7 +476,7 @@ Optionally include `item_url` (GitHub issue URL) to add the issue as the first p Manages GitHub Projects boards. Requires a write-capable PAT or GitHub App token ([project token authentication](/gh-aw/patterns/project-ops/#project-token-authentication)); default `GITHUB_TOKEN` lacks Projects v2 access. Update-only by default; set `create_if_missing: true` to create boards (requires appropriate token permissions). -When using `github-app`, issue-backed project item resolution also requires `issues: read` on the minted token (in addition to `organization-projects: write`). +When using `github-app`, issue-backed project item resolution also requires `issues: read` on the minted token (in addition to `organization-projects: write`). This applies to `update-project`, and also to `create-project` when `item_url` is used to resolve an issue into a project item. ```yaml wrap safe-outputs: diff --git a/pkg/workflow/safe_outputs_app_test.go b/pkg/workflow/safe_outputs_app_test.go index c773a54c2a..f6ab6470d7 100644 --- a/pkg/workflow/safe_outputs_app_test.go +++ b/pkg/workflow/safe_outputs_app_test.go @@ -202,3 +202,44 @@ Test workflow with update-project permissions. assert.Contains(t, stepsStr, "permission-issues: read", "GitHub App token should include issues read permission for issue-backed project items") assert.Contains(t, stepsStr, "permission-contents: read", "GitHub App token should include contents read permission") } + +// TestSafeOutputsAppTokenCreateProjectWithItemURLIssuesReadPermission tests that issues read permission +// is included in the GitHub App token minting step when create-project is configured with item_url. +func TestSafeOutputsAppTokenCreateProjectWithItemURLIssuesReadPermission(t *testing.T) { + compiler := NewCompiler(WithVersion("1.0.0")) + + markdown := `--- +on: issues +safe-outputs: + create-project: + target-owner: "my-org" + github-app: + app-id: ${{ vars.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} +--- + +# Test Workflow + +Test workflow with create-project item_url permissions. +` + + tmpDir := t.TempDir() + testFile := filepath.Join(tmpDir, "test.md") + err := os.WriteFile(testFile, []byte(markdown), 0644) + require.NoError(t, err, "Failed to write test file") + + workflowData, err := compiler.ParseWorkflowFile(testFile) + require.NoError(t, err, "Failed to parse markdown content") + require.NotNil(t, workflowData.SafeOutputs, "SafeOutputs should not be nil") + require.NotNil(t, workflowData.SafeOutputs.CreateProjects, "CreateProjects should not be nil") + + job, _, err := compiler.buildConsolidatedSafeOutputsJob(workflowData, "main", testFile) + require.NoError(t, err, "Failed to build safe_outputs job") + require.NotNil(t, job, "Job should not be nil") + + stepsStr := strings.Join(job.Steps, "") + + assert.Contains(t, stepsStr, "permission-organization-projects: write", "GitHub App token should include organization projects write permission") + assert.Contains(t, stepsStr, "permission-issues: read", "GitHub App token should include issues read permission for issue-backed project items") + assert.Contains(t, stepsStr, "permission-contents: read", "GitHub App token should include contents read permission") +} diff --git a/pkg/workflow/safe_outputs_permissions.go b/pkg/workflow/safe_outputs_permissions.go index 0ff9e46cb2..89ea00a044 100644 --- a/pkg/workflow/safe_outputs_permissions.go +++ b/pkg/workflow/safe_outputs_permissions.go @@ -202,6 +202,7 @@ func ComputePermissionsForSafeOutputs(safeOutputs *SafeOutputsConfig) *Permissio if safeOutputs.CreateProjects != nil && !isHandlerStaged(safeOutputs.Staged, safeOutputs.CreateProjects.Staged) { safeOutputsPermissionsLog.Print("Adding permissions for create-project") permissions.Merge(NewPermissionsContentsReadProjectsWrite()) + permissions.Set(PermissionIssues, PermissionRead) } if safeOutputs.UpdateProjects != nil && !isHandlerStaged(safeOutputs.Staged, safeOutputs.UpdateProjects.Staged) { safeOutputsPermissionsLog.Print("Adding permissions for update-project") diff --git a/pkg/workflow/safe_outputs_permissions_test.go b/pkg/workflow/safe_outputs_permissions_test.go index cd5f37831e..0ee03e7c2f 100644 --- a/pkg/workflow/safe_outputs_permissions_test.go +++ b/pkg/workflow/safe_outputs_permissions_test.go @@ -384,7 +384,7 @@ func TestComputePermissionsForSafeOutputs(t *testing.T) { }, }, { - name: "create-project requires organization-projects write", + name: "create-project requires organization-projects write and issues read", safeOutputs: &SafeOutputsConfig{ CreateProjects: &CreateProjectsConfig{ BaseSafeOutputConfig: BaseSafeOutputConfig{Max: strPtr("1")}, @@ -393,6 +393,7 @@ func TestComputePermissionsForSafeOutputs(t *testing.T) { expected: map[PermissionScope]PermissionLevel{ PermissionContents: PermissionRead, PermissionOrganizationProj: PermissionWrite, + PermissionIssues: PermissionRead, }, }, { From 15d37588f50b5a23f02b59c9d5165e8c73c367f7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Apr 2026 15:13:24 +0000 Subject: [PATCH 3/3] Remove unrelated lockfile changes from PR branch Agent-Logs-Url: https://github.com/github/gh-aw/sessions/0527ad1c-e697-431a-9eb7-f68ee108bcbe Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/smoke-project.lock.yml | 4 ++-- .github/workflows/test-project-url-default.lock.yml | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/smoke-project.lock.yml b/.github/workflows/smoke-project.lock.yml index 3bd1d6c005..228c765f09 100644 --- a/.github/workflows/smoke-project.lock.yml +++ b/.github/workflows/smoke-project.lock.yml @@ -1130,7 +1130,7 @@ jobs: permissions: contents: write discussions: write - issues: read + issues: write pull-requests: write concurrency: group: "gh-aw-conclusion-smoke-project" @@ -1499,7 +1499,7 @@ jobs: permissions: contents: write discussions: write - issues: read + issues: write pull-requests: write timeout-minutes: 15 env: diff --git a/.github/workflows/test-project-url-default.lock.yml b/.github/workflows/test-project-url-default.lock.yml index c4b7f8d3f7..98b8937c41 100644 --- a/.github/workflows/test-project-url-default.lock.yml +++ b/.github/workflows/test-project-url-default.lock.yml @@ -892,7 +892,6 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - issues: read concurrency: group: "gh-aw-conclusion-test-project-url-default" cancel-in-progress: false @@ -1198,7 +1197,6 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - issues: read timeout-minutes: 15 env: GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/test-project-url-default"