Skip to content
Closed
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
14 changes: 14 additions & 0 deletions pkg/parser/schema_location_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,20 @@ func TestValidateMainWorkflowFrontmatterWithSchemaAndLocation(t *testing.T) {
filePath: "/test/workflow.md",
wantErr: false,
},
{
name: "valid workflow frontmatter with top-level applyTo and inputs",
frontmatter: map[string]any{
"on": "push",
"applyTo": []any{"**/*.go", "pkg/**/*.md"},
"inputs": map[string]any{
"mode": "fast",
"retries": 2,
"notify": true,
},
},
filePath: "/test/workflow.md",
wantErr: false,
},
{
name: "invalid workflow frontmatter with location",
frontmatter: map[string]any{
Expand Down
40 changes: 40 additions & 0 deletions pkg/parser/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1006,6 +1006,46 @@ func TestValidateMainWorkflowFrontmatterWithSchemaAndLocation_ProtectedFilesObje
}
}

func TestMainWorkflowSchema_TopLevelApplyToAndInputs(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")
}

applyToField, ok := properties["applyTo"].(map[string]any)
if !ok {
t.Fatal("'applyTo' field not found in schema")
}
applyToOneOf, ok := applyToField["oneOf"].([]any)
if !ok || len(applyToOneOf) != 2 {
t.Fatal("'applyTo.oneOf' not found or has unexpected shape")
}

inputsField, ok := properties["inputs"].(map[string]any)
if !ok {
t.Fatal("'inputs' field not found in schema")
}
if inputsField["type"] != "object" {
t.Fatalf("'inputs.type' expected object, got %v", inputsField["type"])
}
if _, ok := inputsField["additionalProperties"]; !ok {
t.Fatal("'inputs.additionalProperties' not found")
}
}

// TestMainWorkflowSchema_ProtectedFilesObjectFormStructure verifies that the
// main workflow JSON schema defines protected-files as a oneOf [string, object],
// not as a oneOf [string, null] (the old broken form that caused
Expand Down
19 changes: 19 additions & 0 deletions pkg/parser/schemas/main_workflow_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -9765,6 +9765,25 @@
"$ref": "#/$defs/github_app",
"description": "Top-level GitHub App configuration used as a fallback for all nested github-app token minting operations (on, safe-outputs, checkout, tools.github, dependencies). When a nested section does not define its own github-app, this top-level configuration is used automatically."
},
"applyTo": {
"description": "Optional custom-agent scope selector. Use a glob string or array of glob strings to restrict where an imported instruction/agent applies.",
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"inputs": {
"type": "object",
"description": "Optional top-level input values for imported/shared workflow contexts. Values may be scalars, arrays, objects, or expressions and are consumed by import-time substitution and trigger processing.",
"additionalProperties": true
},
"import-schema": {
"type": "object",
"description": "Schema for validating 'with' input values when this workflow is imported by another workflow using the 'uses'/'with' syntax. Defines the expected parameters, their types, and whether they are required. Scalar inputs are accessible via '${{ github.aw.import-inputs.<name> }}' expressions. Object inputs (type: object) allow one-level deep sub-fields accessible via '${{ github.aw.import-inputs.<name>.<subkey> }}' expressions.",
Expand Down