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
54 changes: 42 additions & 12 deletions pkg/cli/upgrade_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,22 @@ func NewUpgradeCommand() *cobra.Command {
This command:
1. Updates all agent and prompt files to the latest templates (like 'init' command)
2. Applies automatic codemods to fix deprecated fields in all workflows (like 'fix --write')
3. Compiles all workflows to generate lock files (like 'compile' command)

The upgrade process ensures:
- GitHub Copilot instructions are up-to-date (.github/aw/github-agentic-workflows.md)
- Dispatcher agent is current (.github/agents/agentic-workflows.agent.md)
- All workflow prompts are updated (create, update, debug, upgrade)
- All workflows use the latest syntax and configuration options
- Deprecated fields are automatically migrated across all workflows
- All workflows are compiled and lock files are up-to-date

This command always upgrades all Markdown files in .github/workflows.

Examples:
` + string(constants.CLIExtensionPrefix) + ` upgrade # Upgrade all workflows
` + string(constants.CLIExtensionPrefix) + ` upgrade --no-fix # Update agent files only (skip codemods)
` + string(constants.CLIExtensionPrefix) + ` upgrade --dir custom/workflows # Upgrade workflows in custom directory

After upgrading, compile workflows manually with:
` + string(constants.CLIExtensionPrefix) + ` compile`,
` + string(constants.CLIExtensionPrefix) + ` upgrade --no-fix # Update agent files only (skip codemods and compilation)
` + string(constants.CLIExtensionPrefix) + ` upgrade --dir custom/workflows # Upgrade workflows in custom directory`,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
verbose, _ := cmd.Flags().GetBool("verbose")
Expand All @@ -62,7 +61,7 @@ After upgrading, compile workflows manually with:
}

cmd.Flags().StringP("dir", "d", "", "Workflow directory (default: .github/workflows)")
cmd.Flags().Bool("no-fix", false, "Skip applying codemods to workflows (only update agent files)")
cmd.Flags().Bool("no-fix", false, "Skip applying codemods and compiling workflows (only update agent files)")

// Register completions
RegisterDirFlagCompletion(cmd, "dir")
Expand Down Expand Up @@ -112,12 +111,43 @@ func runUpgradeCommand(verbose bool, workflowDir string, noFix bool, noCompile b
}
}

// Step 3: Compile workflows (only if explicitly requested - compilation is optional)
// Skipping compilation by default avoids issues when workflows might have other errors
if !noCompile && !noFix {
upgradeLog.Print("Skipping compilation (can be enabled with future --compile flag)")
// Note: We skip compilation by default to avoid crashing on workflows that might have
// validation errors. Users can compile manually after upgrade with: gh aw compile
// Step 3: Compile all workflows (unless --no-fix is specified)
if !noFix {
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Compiling all workflows..."))
upgradeLog.Print("Compiling all workflows")

// Create and configure compiler
compiler := createAndConfigureCompiler(CompileConfig{
Verbose: verbose,
WorkflowDir: workflowDir,
})

// Determine workflow directory
workflowsDir := workflowDir
if workflowsDir == "" {
workflowsDir = ".github/workflows"
}

// Compile all workflow files
stats, compileErr := compileAllWorkflowFiles(compiler, workflowsDir, verbose)
if compileErr != nil {
upgradeLog.Printf("Failed to compile workflows: %v", compileErr)
// Don't fail the upgrade if compilation fails - this is non-critical
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Warning: Failed to compile workflows: %v", compileErr)))
} else if stats != nil {
// Print compilation summary
if verbose {
fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("✓ Compiled %d workflow(s)", stats.Total-stats.Errors)))
}
if stats.Errors > 0 {
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Warning: %d workflow(s) failed to compile", stats.Errors)))
}
}
} else {
upgradeLog.Print("Skipping compilation (--no-fix specified)")
if verbose {
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Skipping compilation (--no-fix specified)"))
}
}

// Print success message
Expand Down
104 changes: 104 additions & 0 deletions pkg/cli/upgrade_command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,3 +222,107 @@ func TestUpgradeCommand_NonGitRepo(t *testing.T) {
require.Error(t, err, "Upgrade should fail in non-git repository")
assert.Contains(t, strings.ToLower(err.Error()), "git", "Error message should mention git")
}

func TestUpgradeCommand_CompilesWorkflows(t *testing.T) {
// Create a temporary directory for test files
tmpDir := t.TempDir()
originalDir, _ := os.Getwd()
defer os.Chdir(originalDir)

// Initialize git repository
os.Chdir(tmpDir)
exec.Command("git", "init").Run()
exec.Command("git", "config", "user.email", "test@example.com").Run()
exec.Command("git", "config", "user.name", "Test User").Run()

// Create .github/workflows directory
workflowsDir := filepath.Join(tmpDir, ".github", "workflows")
err := os.MkdirAll(workflowsDir, 0755)
require.NoError(t, err, "Failed to create workflows directory")

// Create a simple workflow that should compile successfully
workflowFile := filepath.Join(workflowsDir, "test-workflow.md")
content := `---
on:
workflow_dispatch:

permissions:
contents: read
---

# Test Workflow

This is a test workflow that should be compiled during upgrade.
`
err = os.WriteFile(workflowFile, []byte(content), 0644)
require.NoError(t, err, "Failed to create test workflow file")

// Run upgrade command (should compile workflows)
config := UpgradeConfig{
Verbose: false,
NoFix: false, // Apply codemods and compile
WorkflowDir: "",
}

err = RunUpgrade(config)
require.NoError(t, err, "Upgrade command should succeed")

// Verify that the lock file was created
lockFile := filepath.Join(workflowsDir, "test-workflow.lock.yml")
assert.FileExists(t, lockFile, "Lock file should be created after upgrade")

// Read lock file content and verify it's valid YAML
lockContent, err := os.ReadFile(lockFile)
require.NoError(t, err, "Failed to read lock file")
assert.NotEmpty(t, lockContent, "Lock file should not be empty")
assert.Contains(t, string(lockContent), "name:", "Lock file should contain workflow name")
}

func TestUpgradeCommand_NoFixSkipsCompilation(t *testing.T) {
// Create a temporary directory for test files
tmpDir := t.TempDir()
originalDir, _ := os.Getwd()
defer os.Chdir(originalDir)

// Initialize git repository
os.Chdir(tmpDir)
exec.Command("git", "init").Run()
exec.Command("git", "config", "user.email", "test@example.com").Run()
exec.Command("git", "config", "user.name", "Test User").Run()

// Create .github/workflows directory
workflowsDir := filepath.Join(tmpDir, ".github", "workflows")
err := os.MkdirAll(workflowsDir, 0755)
require.NoError(t, err, "Failed to create workflows directory")

// Create a simple workflow
workflowFile := filepath.Join(workflowsDir, "test-workflow.md")
content := `---
on:
workflow_dispatch:

permissions:
contents: read
---

# Test Workflow

This workflow should not be compiled with --no-fix.
`
err = os.WriteFile(workflowFile, []byte(content), 0644)
require.NoError(t, err, "Failed to create test workflow file")

// Run upgrade command with --no-fix
config := UpgradeConfig{
Verbose: false,
NoFix: true, // Skip codemods and compilation
WorkflowDir: "",
}

err = RunUpgrade(config)
require.NoError(t, err, "Upgrade command should succeed")

// Verify that the lock file was NOT created
lockFile := filepath.Join(workflowsDir, "test-workflow.lock.yml")
assert.NoFileExists(t, lockFile, "Lock file should not be created with --no-fix")
}