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: 1 addition & 1 deletion .github/workflows/issue-arborist.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .github/workflows/issue-arborist.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ steps:
# Fetch the last 100 open issues that don't have a parent issue
# Using search filter to exclude issues that are already sub-issues
gh issue list --repo ${{ github.repository }} \
--search "no:parent-issue" \
--search "-parent-issue:*" \
--state open \
--json number,title,author,createdAt,state,url,body,labels,updatedAt,closedAt,milestone,assignees \
--limit 100 \
Expand Down
12 changes: 6 additions & 6 deletions pkg/parser/import_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,12 +172,12 @@ func processImportsFromFrontmatterWithManifestAndSource(frontmatter map[string]a
var engines []string
var safeOutputs []string
var safeInputs []string
var bots []string // Track unique bot names
botsSet := make(map[string]bool) // Set for deduplicating bots
var labels []string // Track unique labels
labelsSet := make(map[string]bool) // Set for deduplicating labels
var caches []string // Track cache configurations (appended in order)
var agentFile string // Track custom agent file
var bots []string // Track unique bot names
botsSet := make(map[string]bool) // Set for deduplicating bots
var labels []string // Track unique labels
labelsSet := make(map[string]bool) // Set for deduplicating labels
var caches []string // Track cache configurations (appended in order)
var agentFile string // Track custom agent file
importInputs := make(map[string]any) // Aggregated input values from all imports

// Seed the queue with initial imports
Expand Down
72 changes: 36 additions & 36 deletions pkg/parser/schema_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ func TestForbiddenFieldsInSharedWorkflows(t *testing.T) {
for _, field := range forbiddenFields {
t.Run("reject_"+field, func(t *testing.T) {
frontmatter := map[string]any{
field: "test-value",
"tools": map[string]any{"bash": true},
field: "test-value",
"tools": map[string]any{"bash": true},
}

err := ValidateIncludedFileFrontmatterWithSchema(frontmatter)
Expand All @@ -33,40 +33,40 @@ func TestForbiddenFieldsInSharedWorkflows(t *testing.T) {

// TestAllowedFieldsInSharedWorkflows verifies allowed fields work correctly
func TestAllowedFieldsInSharedWorkflows(t *testing.T) {
allowedFields := map[string]any{
"tools": map[string]any{"bash": true},
"engine": "copilot",
"network": map[string]any{"allowed": []string{"defaults"}},
"mcp-servers": map[string]any{},
"permissions": "read-all",
"runtimes": map[string]any{"node": map[string]any{"version": "20"}},
"safe-outputs": map[string]any{},
"safe-inputs": map[string]any{},
"services": map[string]any{},
"steps": []any{},
"secret-masking": true,
"jobs": map[string]any{"test": map[string]any{"runs-on": "ubuntu-latest", "steps": []any{map[string]any{"run": "echo test"}}}},
"description": "test",
"metadata": map[string]any{},
"inputs": map[string]any{},
"bots": []string{"copilot"},
"post-steps": []any{map[string]any{"run": "echo cleanup"}},
"labels": []string{"automation", "testing"},
"imports": []string{"./shared.md"},
"cache": map[string]any{"key": "test-key", "path": "node_modules"},
"source": "githubnext/agentics/workflows/ci-doctor.md@v1.0.0",
}
allowedFields := map[string]any{
"tools": map[string]any{"bash": true},
"engine": "copilot",
"network": map[string]any{"allowed": []string{"defaults"}},
"mcp-servers": map[string]any{},
"permissions": "read-all",
"runtimes": map[string]any{"node": map[string]any{"version": "20"}},
"safe-outputs": map[string]any{},
"safe-inputs": map[string]any{},
"services": map[string]any{},
"steps": []any{},
"secret-masking": true,
"jobs": map[string]any{"test": map[string]any{"runs-on": "ubuntu-latest", "steps": []any{map[string]any{"run": "echo test"}}}},
"description": "test",
"metadata": map[string]any{},
"inputs": map[string]any{},
"bots": []string{"copilot"},
"post-steps": []any{map[string]any{"run": "echo cleanup"}},
"labels": []string{"automation", "testing"},
"imports": []string{"./shared.md"},
"cache": map[string]any{"key": "test-key", "path": "node_modules"},
"source": "githubnext/agentics/workflows/ci-doctor.md@v1.0.0",
}

for field, value := range allowedFields {
t.Run("allow_"+field, func(t *testing.T) {
frontmatter := map[string]any{
field: value,
}
for field, value := range allowedFields {
t.Run("allow_"+field, func(t *testing.T) {
frontmatter := map[string]any{
field: value,
}

err := ValidateIncludedFileFrontmatterWithSchema(frontmatter)
if err != nil && strings.Contains(err.Error(), "cannot be used in shared workflows") {
t.Errorf("Field '%s' should be allowed in shared workflows, got error: %v", field, err)
}
})
}
err := ValidateIncludedFileFrontmatterWithSchema(frontmatter)
if err != nil && strings.Contains(err.Error(), "cannot be used in shared workflows") {
t.Errorf("Field '%s' should be allowed in shared workflows, got error: %v", field, err)
}
})
}
}
5 changes: 2 additions & 3 deletions pkg/workflow/create_issue_group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package workflow
import (
"os"
"path/filepath"
"strings"
"testing"

"github.com/githubnext/gh-aw/pkg/testutil"
Expand Down Expand Up @@ -205,8 +204,8 @@ Test grouping with title prefix.
compiledStr := string(compiledContent)

// Verify both group and title_prefix are in the handler config
assert.True(t, strings.Contains(compiledStr, `"group":true`), "Expected group:true in compiled workflow")
assert.True(t, strings.Contains(compiledStr, `title_prefix`), "Expected title_prefix in compiled workflow")
assert.Contains(t, compiledStr, `"group":true`, "Expected group:true in compiled workflow")
assert.Contains(t, compiledStr, `title_prefix`, "Expected title_prefix in compiled workflow")
}

// TestCreateIssueGroupInMCPConfig verifies group flag is passed to MCP config
Expand Down
46 changes: 23 additions & 23 deletions pkg/workflow/forbidden_fields_import_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ This workflow imports a shared workflow with forbidden field.

// Should get error about forbidden field
require.Error(t, err, "Expected error for forbidden field '%s'", field)
assert.Contains(t, err.Error(), "cannot be used in shared workflows",
assert.Contains(t, err.Error(), "cannot be used in shared workflows",
"Error should mention forbidden field, got: %v", err)
})
}
Expand Down Expand Up @@ -168,12 +168,12 @@ This workflow imports a shared workflow with allowed field.
// TestImportsFieldAllowedInSharedWorkflows tests that the "imports" field is allowed in shared workflows
// and that nested imports work correctly
func TestImportsFieldAllowedInSharedWorkflows(t *testing.T) {
tempDir := testutil.TempDir(t, "test-allowed-imports-*")
workflowsDir := filepath.Join(tempDir, ".github", "workflows")
require.NoError(t, os.MkdirAll(workflowsDir, 0755))
tempDir := testutil.TempDir(t, "test-allowed-imports-*")
workflowsDir := filepath.Join(tempDir, ".github", "workflows")
require.NoError(t, os.MkdirAll(workflowsDir, 0755))

// Create a base shared workflow (level 2)
baseSharedContent := `---
// Create a base shared workflow (level 2)
baseSharedContent := `---
tools:
bash: true
labels: ["base"]
Expand All @@ -183,11 +183,11 @@ labels: ["base"]

This is the base shared workflow.
`
baseSharedPath := filepath.Join(workflowsDir, "base.md")
require.NoError(t, os.WriteFile(baseSharedPath, []byte(baseSharedContent), 0644))
baseSharedPath := filepath.Join(workflowsDir, "base.md")
require.NoError(t, os.WriteFile(baseSharedPath, []byte(baseSharedContent), 0644))

// Create intermediate shared workflow with "imports" field (level 1)
intermediateSharedContent := `---
// Create intermediate shared workflow with "imports" field (level 1)
intermediateSharedContent := `---
imports:
- ./base.md
tools:
Expand All @@ -199,11 +199,11 @@ labels: ["intermediate"]

This shared workflow imports another shared workflow (nested imports).
`
intermediateSharedPath := filepath.Join(workflowsDir, "intermediate.md")
require.NoError(t, os.WriteFile(intermediateSharedPath, []byte(intermediateSharedContent), 0644))
intermediateSharedPath := filepath.Join(workflowsDir, "intermediate.md")
require.NoError(t, os.WriteFile(intermediateSharedPath, []byte(intermediateSharedContent), 0644))

// Create main workflow that imports the intermediate shared workflow
mainContent := `---
// Create main workflow that imports the intermediate shared workflow
mainContent := `---
on: issues
imports:
- ./intermediate.md
Expand All @@ -213,15 +213,15 @@ imports:

This workflow imports a shared workflow that itself has imports (nested).
`
mainPath := filepath.Join(workflowsDir, "main.md")
require.NoError(t, os.WriteFile(mainPath, []byte(mainContent), 0644))
mainPath := filepath.Join(workflowsDir, "main.md")
require.NoError(t, os.WriteFile(mainPath, []byte(mainContent), 0644))

// Compile - should succeed because shared workflows can have imports (nested imports are supported)
compiler := NewCompiler(false, tempDir, "test")
err := compiler.CompileWorkflow(mainPath)
// Compile - should succeed because shared workflows can have imports (nested imports are supported)
compiler := NewCompiler(false, tempDir, "test")
err := compiler.CompileWorkflow(mainPath)

// Should NOT get error about forbidden field
if err != nil && strings.Contains(err.Error(), "cannot be used in shared workflows") {
t.Errorf("Field 'imports' should be allowed in shared workflows, got error: %v", err)
}
// Should NOT get error about forbidden field
if err != nil && strings.Contains(err.Error(), "cannot be used in shared workflows") {
t.Errorf("Field 'imports' should be allowed in shared workflows, got error: %v", err)
}
}