Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/src/content/docs/reference/frontmatter.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,8 @@ on:
roles: all # Allow any user (⚠️ use with caution)
```

You can also use a single role string, for example `roles: write`.

Available roles: `admin`, `maintainer`/`maintain`, `write`, `triage`, `read`, `all`. Workflows with unsafe triggers (`push`, `issues`, `pull_request`) automatically enforce permission checks. Failed checks cancel the workflow with a warning.

> [!TIP]
Expand Down
4 changes: 2 additions & 2 deletions pkg/parser/schemas/main_workflow_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1840,8 +1840,8 @@
"oneOf": [
{
"type": "string",
"enum": ["all"],
"description": "Allow any authenticated user to trigger the workflow (\u26a0\ufe0f disables permission checking entirely - use with caution)"
"enum": ["admin", "maintainer", "maintain", "write", "triage", "read", "all"],
"description": "Single repository permission level that can trigger the workflow. Use 'all' to allow any authenticated user (\u26a0\ufe0f disables permission checking entirely - use with caution)"
},
{
"type": "array",
Expand Down
37 changes: 37 additions & 0 deletions pkg/workflow/role_checks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,43 @@ Test that role membership check uses GITHUB_TOKEN with bots.`
}
}

func TestRoleMembershipSupportsSingleRoleString(t *testing.T) {
tmpDir := testutil.TempDir(t, "role-membership-single-role-string-test")

compiler := NewCompiler()

frontmatter := `---
on:
pull_request:
types: [opened]
roles: write
---

# Test Workflow
Test that on.roles supports a single string permission value.`

workflowPath := filepath.Join(tmpDir, "role-membership-single-role-string.md")
err := os.WriteFile(workflowPath, []byte(frontmatter), 0644)
if err != nil {
t.Fatalf("Failed to write workflow file: %v", err)
}

err = compiler.CompileWorkflow(workflowPath)
if err != nil {
t.Fatalf("Expected workflow with on.roles as a single string to compile successfully: %v", err)
}

outputPath := filepath.Join(tmpDir, "role-membership-single-role-string.lock.yml")
compiledContent, err := os.ReadFile(outputPath)
if err != nil {
t.Fatalf("Failed to read compiled workflow: %v", err)
}

compiledStr := string(compiledContent)
assert.Contains(t, compiledStr, "id: check_membership", "Compiled workflow should include membership checks for role-gated triggers")
assert.Contains(t, compiledStr, "write", "Compiled workflow should require the single role provided as a string")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@copilot I suspect this assert is likely to have a false-positive match on "write" somewhere else in the compiled workflow. Can the assert look more specifically for: GH_AW_REQUIRED_ROLES: "write"?

}

func TestInferEventsFromTriggers(t *testing.T) {
c := &Compiler{}

Expand Down
Loading