diff --git a/docs/src/content/docs/reference/frontmatter-full.md b/docs/src/content/docs/reference/frontmatter-full.md index 1387af92f1..9b26071911 100644 --- a/docs/src/content/docs/reference/frontmatter-full.md +++ b/docs/src/content/docs/reference/frontmatter-full.md @@ -805,6 +805,15 @@ env: "example-value" features: {} +# Secret values passed to workflow execution. Secrets can be defined as simple +# strings (GitHub Actions expressions) or objects with 'value' and 'description' +# properties. Typically used to provide secrets to MCP servers or custom engines. +# Note: For passing secrets to reusable workflows, use the jobs..secrets +# field instead. +# (optional) +secrets: + {} + # Environment that the job references (for protected environments and deployments) # (optional) # This field supports multiple formats (oneOf): diff --git a/docs/src/content/docs/reference/frontmatter.md b/docs/src/content/docs/reference/frontmatter.md index c21b032e5f..949912a739 100644 --- a/docs/src/content/docs/reference/frontmatter.md +++ b/docs/src/content/docs/reference/frontmatter.md @@ -298,6 +298,37 @@ env: Environment variables can be defined at multiple scopes (workflow, job, step, engine, safe-outputs, etc.) with clear precedence rules. See [Environment Variables](/gh-aw/reference/environment-variables/) for complete documentation on all 13 env scopes and precedence order. +## Secrets (`secrets:`) + +Defines secret values passed to workflow execution. Secrets are typically used to provide sensitive configuration to MCP servers, custom engines, or workflow components. Values must be GitHub Actions expressions that reference secrets (e.g., `${{ secrets.API_KEY }}`). + +```yaml wrap +secrets: + API_TOKEN: ${{ secrets.API_TOKEN }} + DATABASE_URL: ${{ secrets.DB_URL }} +``` + +Secrets can also include descriptions for documentation: + +```yaml wrap +secrets: + API_TOKEN: + value: ${{ secrets.API_TOKEN }} + description: "API token for external service" + DATABASE_URL: + value: ${{ secrets.DB_URL }} + description: "Production database connection string" +``` + +**Security best practices:** + +- Always use GitHub Actions secret expressions (`${{ secrets.NAME }}`) +- Never commit plaintext secrets to workflow files +- Use environment-specific secrets when possible (via `environment:` field) +- Limit secret access to only the components that need them + +**Note:** For passing secrets to reusable workflows, use the `jobs..secrets` field instead. The top-level `secrets:` field is for workflow-level secret configuration. + ## Environment Protection (`environment:`) Specifies the environment for deployment protection rules and environment-specific secrets. Standard GitHub Actions syntax. diff --git a/pkg/parser/schema_passthrough_validation_test.go b/pkg/parser/schema_passthrough_validation_test.go index 805ccd77c2..8c2c0fb889 100644 --- a/pkg/parser/schema_passthrough_validation_test.go +++ b/pkg/parser/schema_passthrough_validation_test.go @@ -8,7 +8,7 @@ import ( ) // TestPassThroughFieldValidation tests that pass-through YAML fields -// (concurrency, container, environment, env, runs-on, services) are +// (concurrency, container, environment, env, secrets, runs-on, services) are // properly validated by the schema during frontmatter parsing. // // These fields are "pass-through" in that they are extracted from @@ -244,6 +244,93 @@ func TestPassThroughFieldValidation(t *testing.T) { errContains: "oneOf", }, + // Secrets field tests + { + name: "valid secrets - simple string values", + frontmatter: map[string]any{ + "on": "push", + "secrets": map[string]any{ + "API_TOKEN": "${{ secrets.API_TOKEN }}", + "DATABASE_URL": "${{ secrets.DB_URL }}", + }, + }, + wantErr: false, + }, + { + name: "valid secrets - object with value and description", + frontmatter: map[string]any{ + "on": "push", + "secrets": map[string]any{ + "API_TOKEN": map[string]any{ + "value": "${{ secrets.API_TOKEN }}", + "description": "API token for external service", + }, + }, + }, + wantErr: false, + }, + { + name: "valid secrets - mixed simple and object values", + frontmatter: map[string]any{ + "on": "push", + "secrets": map[string]any{ + "API_TOKEN": "${{ secrets.API_TOKEN }}", + "DB_URL": map[string]any{ + "value": "${{ secrets.DB_URL }}", + "description": "Database connection string", + }, + }, + }, + wantErr: false, + }, + { + name: "invalid secrets - object missing required value field", + frontmatter: map[string]any{ + "on": "push", + "secrets": map[string]any{ + "API_TOKEN": map[string]any{ + "description": "Missing value field", + }, + }, + }, + wantErr: true, + errContains: "missing property 'value'", + }, + { + name: "invalid secrets - object with additional properties", + frontmatter: map[string]any{ + "on": "push", + "secrets": map[string]any{ + "API_TOKEN": map[string]any{ + "value": "${{ secrets.API_TOKEN }}", + "invalid": "field", + }, + }, + }, + wantErr: true, + errContains: "additional properties 'invalid' not allowed", + }, + { + name: "invalid secrets - array", + frontmatter: map[string]any{ + "on": "push", + "secrets": []string{"invalid"}, + }, + wantErr: true, + errContains: "got array, want object", + }, + { + name: "invalid secrets - non-string, non-object value", + frontmatter: map[string]any{ + "on": "push", + "secrets": map[string]any{ + "API_TOKEN": 123, + }, + }, + wantErr: true, + errContains: "oneOf", + }, + // Runs-on field tests { name: "valid runs-on - simple string", diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json index 750a56de55..bc5803dee5 100644 --- a/pkg/parser/schemas/main_workflow_schema.json +++ b/pkg/parser/schemas/main_workflow_schema.json @@ -1876,6 +1876,50 @@ } ] }, + "secrets": { + "description": "Secret values passed to workflow execution. Secrets can be defined as simple strings (GitHub Actions expressions) or objects with 'value' and 'description' properties. Typically used to provide secrets to MCP servers or custom engines. Note: For passing secrets to reusable workflows, use the jobs..secrets field instead.", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "type": "string", + "description": "Secret value as a GitHub Actions expression (e.g., ${{ secrets.API_KEY }})" + }, + { + "type": "object", + "description": "Secret with metadata", + "required": ["value"], + "properties": { + "value": { + "type": "string", + "description": "Secret value as a GitHub Actions expression" + }, + "description": { + "type": "string", + "description": "Description of what this secret is used for" + } + }, + "additionalProperties": false + } + ] + }, + "examples": [ + { + "API_TOKEN": "${{ secrets.API_TOKEN }}", + "DATABASE_URL": "${{ secrets.DB_URL }}" + }, + { + "API_TOKEN": { + "value": "${{ secrets.API_TOKEN }}", + "description": "API token for external service" + }, + "DATABASE_URL": { + "value": "${{ secrets.DB_URL }}", + "description": "Production database connection string" + } + } + ] + }, "environment": { "description": "Environment that the job references (for protected environments and deployments)", "oneOf": [ @@ -5263,39 +5307,47 @@ }, "dispatch-workflow": { "oneOf": [ - { - "type": "array", - "minItems": 1, - "items": { - "type": "string" - }, - "description": "Shorthand format: array of workflow names to dispatch (without .md extension). Workflows must exist in same directory and support workflow_dispatch trigger. Self-reference not allowed. Max defaults to 1." - }, { "type": "object", - "description": "Configuration for dispatching other workflows from this workflow. Allows workflows to trigger other workflows via workflow_dispatch events. Includes self-reference prevention and path traversal protection.", + "description": "Configuration for dispatching workflow_dispatch events to other workflows. Orchestrators use this to delegate work to worker workflows.", "properties": { "workflows": { "type": "array", - "minItems": 1, + "description": "List of workflow names (without .md extension) to allow dispatching. Each workflow must exist in .github/workflows/.", "items": { - "type": "string" + "type": "string", + "minLength": 1 }, - "description": "List of workflow names to dispatch (without .md extension). Workflows must exist in same directory and support workflow_dispatch trigger. Self-reference not allowed." + "minItems": 1, + "maxItems": 50 }, "max": { "type": "integer", + "description": "Maximum number of workflow dispatch operations per run (default: 1, max: 50)", "minimum": 1, "maximum": 50, - "description": "Maximum number of concurrent workflow dispatches (default: 1, maximum: 50)" + "default": 1 + }, + "github-token": { + "$ref": "#/$defs/github_token", + "description": "GitHub token to use for dispatching workflows. Overrides global github-token if specified." } }, "required": ["workflows"], "additionalProperties": false + }, + { + "type": "array", + "description": "Shorthand array format: list of workflow names (without .md extension) to allow dispatching", + "items": { + "type": "string", + "minLength": 1 + }, + "minItems": 1, + "maxItems": 50 } ], - "$comment": "Self-reference prevention: workflow cannot dispatch itself (prevents infinite loops). Path traversal protection: all paths validated with isPathWithinDir(). Validation: pkg/workflow/dispatch_workflow_validation.go", - "description": "Enable dispatching other workflows from this workflow. Allows workflows to trigger other workflows via workflow_dispatch events with security constraints." + "description": "Dispatch workflow_dispatch events to other workflows. Used by orchestrators to delegate work to worker workflows with controlled maximum dispatch count." }, "missing-tool": { "oneOf": [ @@ -5840,50 +5892,6 @@ "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/" - }, - "dispatch-workflow": { - "oneOf": [ - { - "type": "object", - "description": "Configuration for dispatching workflow_dispatch events to other workflows. Orchestrators use this to delegate work to worker workflows.", - "properties": { - "workflows": { - "type": "array", - "description": "List of workflow names (without .md extension) to allow dispatching. Each workflow must exist in .github/workflows/.", - "items": { - "type": "string", - "minLength": 1 - }, - "minItems": 1, - "maxItems": 50 - }, - "max": { - "type": "integer", - "description": "Maximum number of workflow dispatch operations per run (default: 1, max: 50)", - "minimum": 1, - "maximum": 50, - "default": 1 - }, - "github-token": { - "$ref": "#/$defs/github_token", - "description": "GitHub token to use for dispatching workflows. Overrides global github-token if specified." - } - }, - "required": ["workflows"], - "additionalProperties": false - }, - { - "type": "array", - "description": "Shorthand array format: list of workflow names (without .md extension) to allow dispatching", - "items": { - "type": "string", - "minLength": 1 - }, - "minItems": 1, - "maxItems": 50 - } - ], - "description": "Dispatch workflow_dispatch events to other workflows. Used by orchestrators to delegate work to worker workflows with controlled maximum dispatch count." } }, "additionalProperties": false