Skip to content

Commit

Permalink
feat: Add additional module commands for versions (#235)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomasmik authored Jul 19, 2024
1 parent bbd7948 commit ab41519
Show file tree
Hide file tree
Showing 3 changed files with 247 additions and 0 deletions.
119 changes: 119 additions & 0 deletions internal/cmd/module/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package module

import (
"context"
"fmt"
"sort"
"strings"

"github.com/pkg/errors"
"github.com/urfave/cli/v2"

"github.com/spacelift-io/spacectl/internal/cmd"
"github.com/spacelift-io/spacectl/internal/cmd/authenticated"
)

func listModules() cli.ActionFunc {
return func(cliCtx *cli.Context) error {
outputFormat, err := cmd.GetOutputFormat(cliCtx)
if err != nil {
return err
}

switch outputFormat {
case cmd.OutputFormatTable:
return listModulesTable(cliCtx)
case cmd.OutputFormatJSON:
return listModulesJSON(cliCtx.Context)
}

return fmt.Errorf("unknown output format: %v", outputFormat)
}
}

func listModulesJSON(ctx context.Context) error {
var query struct {
Modules []module `graphql:"modules" json:"modules,omitempty"`
}

if err := authenticated.Client.Query(ctx, &query, map[string]interface{}{}); err != nil {
return errors.Wrap(err, "failed to query list of modules")
}
return cmd.OutputJSON(query.Modules)
}

func listModulesTable(ctx *cli.Context) error {
var query struct {
Modules []struct {
ID string `graphql:"id" json:"id,omitempty"`
Name string `graphql:"name"`
Current struct {
ID string `graphql:"id"`
Number string `graphql:"number"`
State string `graphql:"state"`
Yanked bool `graphql:"yanked"`
} `graphql:"current"`
} `graphql:"modules"`
}

if err := authenticated.Client.Query(ctx.Context, &query, map[string]interface{}{}); err != nil {
return errors.Wrap(err, "failed to query list of modules")
}

sort.SliceStable(query.Modules, func(i, j int) bool {
return strings.Compare(strings.ToLower(query.Modules[i].Name), strings.ToLower(query.Modules[j].Name)) < 0
})

columns := []string{"Name", "ID", "Current Version", "Number", "State", "Yanked"}

tableData := [][]string{columns}
for _, module := range query.Modules {
row := []string{
module.Name,
module.ID,
module.Current.ID,
module.Current.Number,
module.Current.State,
fmt.Sprintf("%t", module.Current.Yanked),
}

tableData = append(tableData, row)
}

return cmd.OutputTable(tableData, true)
}

type module struct {
ID string `json:"id" graphql:"id"`
Administrative bool `json:"administrative" graphql:"administrative"`
APIHost string `json:"apiHost" graphql:"apiHost"`
Branch string `json:"branch" graphql:"branch"`
Description string `json:"description" graphql:"description"`
CanWrite bool `json:"canWrite" graphql:"canWrite"`
IsDisabled bool `json:"isDisabled" graphql:"isDisabled"`
CreatedAt int `json:"createdAt" graphql:"createdAt"`
Labels []string `json:"labels" graphql:"labels"`
Name string `json:"name" graphql:"name"`
Namespace string `json:"namespace" graphql:"namespace"`
ProjectRoot string `json:"projectRoot" graphql:"projectRoot"`
Provider string `json:"provider" graphql:"provider"`
Repository string `json:"repository" graphql:"repository"`
TerraformProvider string `json:"terraformProvider" graphql:"terraformProvider"`
LocalPreviewEnabled bool `json:"localPreviewEnabled,omitempty" graphql:"localPreviewEnabled"`
Current struct {
ID string `json:"id" graphql:"id"`
Number string `json:"number" graphql:"number"`
State string `json:"state" graphql:"state"`
Yanked bool `json:"yanked" graphql:"yanked"`
} `json:"current" graphql:"current"`
SpaceDetails struct {
ID string `json:"id" graphql:"id"`
Name string `json:"name" graphql:"name"`
AccessLevel string `json:"accessLevel" graphql:"accessLevel"`
} `json:"spaceDetails" graphql:"spaceDetails"`
WorkerPool struct {
ID string `graphql:"id" json:"id,omitempty"`
Name string `graphql:"name" json:"name,omitempty"`
} `graphql:"workerPool" json:"workerPool,omitempty"`
Starred bool `json:"starred" graphql:"starred"`
}
23 changes: 23 additions & 0 deletions internal/cmd/module/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,29 @@ func Command() *cli.Command {
Before: authenticated.Ensure,
ArgsUsage: cmd.EmptyArgsUsage,
},
{
Category: "Module management",
Name: "list",
Usage: "List all modules available and their current version",
Flags: []cli.Flag{
cmd.FlagOutputFormat,
},
Action: listModules(),
Before: authenticated.Ensure,
ArgsUsage: cmd.EmptyArgsUsage,
},
{
Category: "Module management",
Name: "list-versions",
Usage: "List 20 latest non failed versions for a module",
Flags: []cli.Flag{
flagModuleID,
cmd.FlagOutputFormat,
},
Action: listVersions(),
Before: authenticated.Ensure,
ArgsUsage: cmd.EmptyArgsUsage,
},
},
}
}
105 changes: 105 additions & 0 deletions internal/cmd/module/search_version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package module

import (
"fmt"

"github.com/pkg/errors"
"github.com/shurcooL/graphql"
"github.com/urfave/cli/v2"

"github.com/spacelift-io/spacectl/internal/cmd"
"github.com/spacelift-io/spacectl/internal/cmd/authenticated"
)

func listVersions() cli.ActionFunc {
return func(cliCtx *cli.Context) error {
outputFormat, err := cmd.GetOutputFormat(cliCtx)
if err != nil {
return err
}

switch outputFormat {
case cmd.OutputFormatTable:
return listVersionsTable(cliCtx)
case cmd.OutputFormatJSON:
return listVersionsJSON(cliCtx)
}

return fmt.Errorf("unknown output format: %v", outputFormat)
}
}

func listVersionsJSON(cliCtx *cli.Context) error {
var query struct {
Module struct {
Verions []version `graphql:"versions(includeFailed: $includeFailed)"`
} `graphql:"module(id: $id)"`
}

if err := authenticated.Client.Query(cliCtx.Context, &query, map[string]interface{}{
"id": cliCtx.String(flagModuleID.Name),
"includeFailed": graphql.Boolean(false),
}); err != nil {
return errors.Wrap(err, "failed to query list of modules")
}
return cmd.OutputJSON(query.Module.Verions)
}

func listVersionsTable(cliCtx *cli.Context) error {
var query struct {
Module struct {
Verions []version `graphql:"versions(includeFailed: $includeFailed)"`
} `graphql:"module(id: $id)"`
}

if err := authenticated.Client.Query(cliCtx.Context, &query, map[string]interface{}{
"id": cliCtx.String(flagModuleID.Name),
"includeFailed": graphql.Boolean(false),
}); err != nil {
return errors.Wrap(err, "failed to query list of modules")
}

columns := []string{"ID", "Author", "Message", "Number", "State", "Tests", "Timestamp"}
tableData := [][]string{columns}

if len(query.Module.Verions) > 20 {
query.Module.Verions = query.Module.Verions[:20]
}

// We print the versions in reverse order
// so the latest version is at the bottom, much easier to read in terminal.
for i := len(query.Module.Verions) - 1; i >= 0; i-- {
module := query.Module.Verions[i]
row := []string{
module.ID,
module.Commit.AuthorName,
module.Commit.Message,
module.Number,
module.State,
fmt.Sprintf("%d", module.VersionCount),
fmt.Sprintf("%d", module.Commit.Timestamp),
}

tableData = append(tableData, row)
}

return cmd.OutputTable(tableData, true)
}

type version struct {
ID string `json:"id" graphql:"id"`
Commit struct {
AuthorLogin string `json:"authorLogin" graphql:"authorLogin"`
AuthorName string `json:"authorName" graphql:"authorName"`
Hash string `json:"hash" graphql:"hash"`
Message string `json:"message" graphql:"message"`
Timestamp int `json:"timestamp" graphql:"timestamp"`
URL string `json:"url" graphql:"url"`
} `json:"commit" graphql:"commit"`
DownloadLink interface{} `json:"downloadLink" graphql:"downloadLink"`
Number string `json:"number" graphql:"number"`
SourceURL string `json:"sourceURL" graphql:"sourceURL"`
State string `json:"state" graphql:"state"`
VersionCount int `json:"versionCount" graphql:"versionCount"`
Yanked bool `json:"yanked" graphql:"yanked"`
}

0 comments on commit ab41519

Please sign in to comment.