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
4 changes: 2 additions & 2 deletions pkg/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ All diagnostic output MUST go to `stderr` using `console` formatting helpers. St
| `DependencyReport` | `deps_report.go` | Full dependency report |
| `OutdatedDependency` | `deps_outdated.go` | An outdated dependency entry |
| `SecurityAdvisory` | `deps_security.go` | A security advisory entry |
| `WorkflowStatus` | `status_command.go` | Run status for a single workflow |
| `WorkflowStatus` | `status_command.go` | Run status for a single workflow; embeds `WorkflowListItem` |
| `MCPRegistryClient` | `mcp_registry.go` | Client for the MCP registry API |
| `ToolGraph` | `tool_graph.go` | Dependency graph of MCP tools |
| `DependencyGraph` | `dependency_graph.go` | Dependency graph across workflows |
Expand Down Expand Up @@ -394,7 +394,7 @@ The `cli` package exports many types used across its command implementations. Th
| `WorkflowFailure` | struct | A workflow failure record |
| `WorkflowFileStatus` | struct | Status of a workflow file (exists, outdated, etc.) |
| `WorkflowJob` | struct | A GitHub Actions job within a workflow run |
| `WorkflowListItem` | struct | A single item in the `gh aw list` output |
| `WorkflowListItem` | struct | A single item in `gh aw list`; shared workflow metadata fields (name, engine, compiled status, labels, triggers) also embedded in `WorkflowStatus` |
| `WorkflowMCPMetadata` | struct | MCP server metadata scanned from a workflow file |
| `WorkflowNode` | struct | A node in the workflow dependency graph |
| `WorkflowOption` | struct | A selectable workflow option for interactive prompts |
Expand Down
2 changes: 1 addition & 1 deletion pkg/cli/add_interactive_workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ func findWorkflowsByFilenamePattern(pattern, repoOverride string, verbose bool)
if verbose {
fmt.Fprintf(os.Stderr, "Workflow with filename '%s' found in workflow list\n", pattern)
}
return []WorkflowStatus{{Workflow: pattern}}, nil
return []WorkflowStatus{{WorkflowListItem: WorkflowListItem{Workflow: pattern}}}, nil
}

if verbose {
Expand Down
12 changes: 7 additions & 5 deletions pkg/cli/mcp_schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -489,13 +489,15 @@ func TestGeneratedSchemasValidateRealOutput(t *testing.T) {

// Create realistic test data
data := WorkflowStatus{
Workflow: "status-workflow",
EngineID: "copilot",
Compiled: "true",
WorkflowListItem: WorkflowListItem{
Workflow: "status-workflow",
EngineID: "copilot",
Compiled: "true",
Labels: []string{"production", "automated"},
On: "push",
},
Status: "active",
TimeRemaining: "5m30s",
Labels: []string{"production", "automated"},
On: "push",
RunStatus: "completed",
RunConclusion: "success",
}
Expand Down
28 changes: 16 additions & 12 deletions pkg/cli/status_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,14 @@ import (

var statusLog = logger.New("cli:status_command")

// WorkflowStatus represents the status of a single workflow for JSON output
// WorkflowStatus represents the status of a single workflow for JSON output.
// It embeds WorkflowListItem so that both list and status commands share the
// same source of truth for the common workflow metadata fields.
type WorkflowStatus struct {
Workflow string `json:"workflow" console:"header:Workflow"`
EngineID string `json:"engine_id" console:"header:Engine"`
Compiled string `json:"compiled" console:"header:Compiled"`
WorkflowListItem
Status string `json:"status" console:"header:Status"`
TimeRemaining string `json:"time_remaining" console:"header:Time Remaining"`
Labels []string `json:"labels,omitempty" console:"header:Labels,omitempty"`
Dependencies []string `json:"dependencies,omitempty" console:"-"`
On any `json:"on,omitempty" console:"-"`
RunStatus string `json:"run_status,omitempty" console:"header:Run Status,omitempty"`
RunConclusion string `json:"run_conclusion,omitempty" console:"header:Run Conclusion,omitempty"`
}
Expand Down Expand Up @@ -178,14 +176,16 @@ func GetWorkflowStatuses(pattern string, ref string, labelFilter string, repoOve

// Build status object
statuses = append(statuses, WorkflowStatus{
Workflow: name,
EngineID: agent,
Compiled: compiled,
WorkflowListItem: WorkflowListItem{
Workflow: name,
EngineID: agent,
Compiled: compiled,
Labels: labels,
On: onField,
},
Status: status,
TimeRemaining: timeRemaining,
Labels: labels,
Dependencies: dependencies,
On: onField,
RunStatus: runStatus,
RunConclusion: runConclusion,
})
Expand Down Expand Up @@ -219,7 +219,11 @@ func buildRemoteWorkflowStatuses(pattern string, githubWorkflows map[string]*Git
}

statuses = append(statuses, WorkflowStatus{
Workflow: name,
// Remote workflow status only includes the workflow name here; the
// GitHub Actions API response does not provide list metadata fields.
WorkflowListItem: WorkflowListItem{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[/zoom-out] The WorkflowListItem inside buildRemoteWorkflowStatuses is partially initialised — only Workflow is set; EngineID, Compiled, Labels, and On stay at zero values. This was the same behaviour before the refactor, but the embedding makes the partial init more visible now.

💡 Suggestion: add a comment clarifying intent

A short comment prevents a future contributor from treating the missing fields as an oversight:

statuses = append(statuses, WorkflowStatus{
    // Remote-status entries only carry the workflow name; engine/compiled
    // metadata is not available from the GitHub Actions API at this point.
    WorkflowListItem: WorkflowListItem{Workflow: name},
    ...
})

Workflow: name,
},
Status: status,
RunStatus: runStatus,
RunConclusion: runConclusion,
Expand Down
82 changes: 50 additions & 32 deletions pkg/cli/status_command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,16 @@ func TestStatusWorkflows_JSONOutput(t *testing.T) {
func TestWorkflowStatus_JSONMarshaling(t *testing.T) {
// Test that WorkflowStatus can be marshaled to JSON
status := WorkflowStatus{
Workflow: "test-workflow",
EngineID: "copilot",
Compiled: "Yes",
WorkflowListItem: WorkflowListItem{
Workflow: "test-workflow",
EngineID: "copilot",
Compiled: "Yes",
On: map[string]any{
"workflow_dispatch": nil,
},
},
Status: "active",
TimeRemaining: "N/A",
On: map[string]any{
"workflow_dispatch": nil,
},
}

jsonBytes, err := json.Marshal(status)
Expand Down Expand Up @@ -297,16 +299,20 @@ func TestWorkflowStatus_ConsoleRendering(t *testing.T) {
// Create test data
statuses := []WorkflowStatus{
{
Workflow: "test-workflow-1",
EngineID: "copilot",
Compiled: "Yes",
WorkflowListItem: WorkflowListItem{
Workflow: "test-workflow-1",
EngineID: "copilot",
Compiled: "Yes",
},
Status: "active",
TimeRemaining: "N/A",
},
{
Workflow: "test-workflow-2",
EngineID: "claude",
Compiled: "No",
WorkflowListItem: WorkflowListItem{
Workflow: "test-workflow-2",
EngineID: "claude",
Compiled: "No",
},
Status: "disabled",
TimeRemaining: "2h 30m",
},
Expand Down Expand Up @@ -344,9 +350,11 @@ func TestWorkflowStatus_ConsoleRendering(t *testing.T) {
func TestWorkflowStatus_JSONMarshalingWithRunStatus(t *testing.T) {
// Test that WorkflowStatus with run status can be marshaled to JSON
status := WorkflowStatus{
Workflow: "test-workflow",
EngineID: "copilot",
Compiled: "Yes",
WorkflowListItem: WorkflowListItem{
Workflow: "test-workflow",
EngineID: "copilot",
Compiled: "Yes",
},
Status: "active",
TimeRemaining: "N/A",
RunStatus: "completed",
Expand Down Expand Up @@ -376,9 +384,11 @@ func TestWorkflowStatus_JSONMarshalingWithRunStatus(t *testing.T) {
func TestWorkflowStatus_JSONMarshalingWithEmptyRunStatus(t *testing.T) {
// Test that WorkflowStatus without run status omits those fields
status := WorkflowStatus{
Workflow: "test-workflow",
EngineID: "copilot",
Compiled: "Yes",
WorkflowListItem: WorkflowListItem{
Workflow: "test-workflow",
EngineID: "copilot",
Compiled: "Yes",
},
Status: "active",
TimeRemaining: "N/A",
// RunStatus and RunConclusion are empty
Expand Down Expand Up @@ -407,12 +417,14 @@ func TestWorkflowStatus_JSONMarshalingWithEmptyRunStatus(t *testing.T) {
func TestWorkflowStatus_JSONMarshalingWithLabels(t *testing.T) {
// Test that WorkflowStatus with labels can be marshaled to JSON
status := WorkflowStatus{
Workflow: "test-workflow",
EngineID: "copilot",
Compiled: "Yes",
WorkflowListItem: WorkflowListItem{
Workflow: "test-workflow",
EngineID: "copilot",
Compiled: "Yes",
Labels: []string{"automation", "testing"},
},
Status: "active",
TimeRemaining: "N/A",
Labels: []string{"automation", "testing"},
}

jsonBytes, err := json.Marshal(status)
Expand Down Expand Up @@ -447,12 +459,14 @@ func TestWorkflowStatus_JSONMarshalingWithLabels(t *testing.T) {
func TestWorkflowStatus_JSONMarshalingWithEmptyLabels(t *testing.T) {
// Test that WorkflowStatus without labels omits the field
status := WorkflowStatus{
Workflow: "test-workflow",
EngineID: "copilot",
Compiled: "Yes",
WorkflowListItem: WorkflowListItem{
Workflow: "test-workflow",
EngineID: "copilot",
Compiled: "Yes",
// Labels is empty/nil
},
Status: "active",
TimeRemaining: "N/A",
// Labels is empty/nil
}

jsonBytes, err := json.Marshal(status)
Expand All @@ -476,18 +490,22 @@ func TestWorkflowStatus_ConsoleRenderingWithRunStatus(t *testing.T) {
// Create test data with run status
statuses := []WorkflowStatus{
{
Workflow: "test-workflow-1",
EngineID: "copilot",
Compiled: "Yes",
WorkflowListItem: WorkflowListItem{
Workflow: "test-workflow-1",
EngineID: "copilot",
Compiled: "Yes",
},
Status: "active",
TimeRemaining: "N/A",
RunStatus: "completed",
RunConclusion: "success",
},
{
Workflow: "test-workflow-2",
EngineID: "claude",
Compiled: "No",
WorkflowListItem: WorkflowListItem{
Workflow: "test-workflow-2",
EngineID: "claude",
Compiled: "No",
},
Status: "disabled",
TimeRemaining: "2h 30m",
RunStatus: "completed",
Expand Down
6 changes: 3 additions & 3 deletions pkg/cli/status_dependency_tree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ func TestExtractWorkflowDependencies_ImportsObjectAW(t *testing.T) {
func TestRenderWorkflowDependencyTree(t *testing.T) {
statuses := []WorkflowStatus{
{
Workflow: "main-workflow",
Dependencies: []string{"shared/base.md", "local/helpers.md"},
WorkflowListItem: WorkflowListItem{Workflow: "main-workflow"},
Dependencies: []string{"shared/base.md", "local/helpers.md"},
},
}

Expand All @@ -65,6 +65,6 @@ func TestRenderWorkflowDependencyTree(t *testing.T) {
}

func TestRenderWorkflowDependencyTree_Empty(t *testing.T) {
statuses := []WorkflowStatus{{Workflow: "standalone"}}
statuses := []WorkflowStatus{{WorkflowListItem: WorkflowListItem{Workflow: "standalone"}}}
assert.Empty(t, renderWorkflowDependencyTree(statuses), "dependency tree should be empty when no dependencies exist")
}
Loading
Loading