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
219 changes: 219 additions & 0 deletions cli/cmd/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/omattsson/stackctl/cli/pkg/client"
"github.com/omattsson/stackctl/cli/pkg/output"
"github.com/omattsson/stackctl/cli/pkg/types"
"github.com/pmezard/go-difflib/difflib"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -443,6 +444,220 @@ Examples:
},
}

var templateVersionsCmd = &cobra.Command{
Use: "versions",
Short: "Manage template version history",
Long: "List, inspect, and compare versioned snapshots of a stack template.",
}

var templateVersionsListCmd = &cobra.Command{
Use: "list <id>",
Short: "List version history for a template",
Long: `List all published versions of a stack template, newest first.

Examples:
stackctl template versions list 1
stackctl template versions list 1 -o json`,
Args: cobra.ExactArgs(1),
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
id, err := parseID(args[0])
if err != nil {
return err
}

c, err := newClient()
if err != nil {
return err
}

versions, err := c.ListTemplateVersions(id)
if err != nil {
return err
}

if printer.Quiet {
ids := make([]string, len(versions))
for i, v := range versions {
ids[i] = v.ID
}
printer.PrintIDs(ids)
return nil
}

switch printer.Format {
case output.FormatJSON:
return printer.PrintJSON(versions)
case output.FormatYAML:
return printer.PrintYAML(versions)
default:
headers := []string{"ID", "VERSION", "CHANGE SUMMARY", "CREATED BY", "CREATED AT"}
rows := make([][]string, len(versions))
for i, v := range versions {
rows[i] = []string{
v.ID,
v.Version,
v.ChangeSummary,
v.CreatedBy,
v.CreatedAt.Format("2006-01-02 15:04"),
}
}
return printer.PrintTable(headers, rows)
}
},
}

var templateVersionsGetCmd = &cobra.Command{
Use: "get <id> <version-id>",
Short: "Show a specific template version",
Long: `Show details of a specific template version snapshot.

The <version-id> is the UUID shown in the ID column of 'template versions list'.

Examples:
stackctl template versions get 1 $(stackctl template versions list 1 -q | head -1)
stackctl template versions get 1 <version-id> -o json`,
Args: cobra.ExactArgs(2),
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
templateID, err := parseID(args[0])
if err != nil {
return err
}
versionID := args[1]

c, err := newClient()
if err != nil {
return err
}

v, err := c.GetTemplateVersion(templateID, versionID)
if err != nil {
return err
}

if printer.Quiet {
fmt.Fprintln(printer.Writer, v.ID)
return nil
}

switch printer.Format {
case output.FormatJSON:
return printer.PrintJSON(v)
case output.FormatYAML:
return printer.PrintYAML(v)
default:
headers := []string{"FIELD", "VALUE"}
rows := [][]string{
{"ID", v.ID},
{"Template ID", v.TemplateID},
{"Version", v.Version},
{"Change Summary", v.ChangeSummary},
{"Created By", v.CreatedBy},
{"Created At", v.CreatedAt.Format("2006-01-02 15:04")},
}
for _, ch := range v.Snapshot.Charts {
rows = append(rows, []string{"Chart", ch.ChartName})
}
return printer.PrintTable(headers, rows)
}
},
}

var templateVersionsDiffCmd = &cobra.Command{
Use: "diff <id> <left-version-id> <right-version-id>",
Short: "Compare two template versions",
Long: `Compare two template version snapshots side by side.

The version IDs are the UUIDs shown in the ID column of 'template versions list'.
In table mode, shows a chart-level diff summary.
In JSON or YAML mode, returns the full structured diff.

Examples:
stackctl template versions diff 1 <left-version-id> <right-version-id>
stackctl template versions diff 1 <left-version-id> <right-version-id> -o json`,
Args: cobra.ExactArgs(3),
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
templateID, err := parseID(args[0])
if err != nil {
return err
}
leftID := args[1]
rightID := args[2]

c, err := newClient()
if err != nil {
return err
}

diff, err := c.DiffTemplateVersions(templateID, leftID, rightID)
if err != nil {
return err
}

if printer.Quiet {
// quiet mode prints chart names with differences, one per line.
// For diff, chart names are the stable identifiers in this context.
for _, ch := range diff.ChartDiffs {
if ch.HasDifferences {
fmt.Fprintln(printer.Writer, ch.ChartName)
}
}
return nil
}

switch printer.Format {
case output.FormatJSON:
return printer.PrintJSON(diff)
case output.FormatYAML:
return printer.PrintYAML(diff)
Comment on lines +613 to +614
default:
fmt.Fprintf(printer.Writer, "Comparing %s -> %s\n\n", diff.Left.Version, diff.Right.Version)
headers := []string{"CHART", "CHANGE", "REPO URL CHANGED", "VALUES CHANGED"}
rows := make([][]string, len(diff.ChartDiffs))
for i, ch := range diff.ChartDiffs {
repoChanged := "no"
if ch.LeftRepoURL != ch.RightRepoURL {
repoChanged = "yes"
}
valuesChanged := "no"
if ch.LeftValues != ch.RightValues {
valuesChanged = "yes"
}
rows[i] = []string{
ch.ChartName,
printer.StatusColor(ch.ChangeType),
repoChanged,
valuesChanged,
}
}
if err := printer.PrintTable(headers, rows); err != nil {
return err
}
// Render per-chart unified text diff for charts with values changes.
for _, ch := range diff.ChartDiffs {
if ch.LeftValues == ch.RightValues {
Comment on lines +639 to +640
continue
}
ud := difflib.UnifiedDiff{
A: difflib.SplitLines(ch.LeftValues),
B: difflib.SplitLines(ch.RightValues),
FromFile: fmt.Sprintf("%s (%s)", ch.ChartName, diff.Left.Version),
ToFile: fmt.Sprintf("%s (%s)", ch.ChartName, diff.Right.Version),
Context: 3,
}
text, err := difflib.GetUnifiedDiffString(ud)
if err != nil || text == "" {
continue
}
fmt.Fprintf(printer.Writer, "\n%s", text)
}
return nil
}
},
}

// printTemplate outputs a StackTemplate in the active format.
func printTemplate(tmpl *types.StackTemplate) error {
if printer.Quiet {
Expand Down Expand Up @@ -524,5 +739,9 @@ func init() {
templateCmd.AddCommand(templateCloneCmd)
templateCmd.AddCommand(templatePublishCmd)
templateCmd.AddCommand(templateUnpublishCmd)
templateVersionsCmd.AddCommand(templateVersionsListCmd)
templateVersionsCmd.AddCommand(templateVersionsGetCmd)
templateVersionsCmd.AddCommand(templateVersionsDiffCmd)
templateCmd.AddCommand(templateVersionsCmd)
rootCmd.AddCommand(templateCmd)
}
Loading