Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(module-versions): Use searchModuleVersions instead of versions when fetching module versions #255

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
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
GOLANGCI_LINT_VERSION=v1.61.0
4 changes: 4 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ jobs:
- name: Mark source directory as safe.
run: git config --global --add safe.directory $GITHUB_WORKSPACE

- name: Load .env file to env vars
run: cat .env >> $GITHUB_ENV

- name: Set up Go
uses: actions/setup-go@v5
with: { go-version-file: go.mod }
Expand All @@ -34,3 +37,4 @@ jobs:
uses: golangci/golangci-lint-action@v6
with:
args: --verbose
version: ${{ env.GOLANGCI_LINT_VERSION }}
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
include .env

lint:
docker run --rm -v $(PWD):/app -w /app golangci/golangci-lint:$(GOLANGCI_LINT_VERSION)-alpine golangci-lint run
1 change: 1 addition & 0 deletions client/structs/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type QueryOrder struct {
type QueryPredicate struct {
Field graphql.String `json:"field"`
Constraint QueryFieldConstraint `json:"constraint"`
Exclude graphql.Boolean `json:"exclude"`
}

// QueryFieldConstraint is a constraint used
Expand Down
147 changes: 111 additions & 36 deletions internal/cmd/module/search_version.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,22 @@ package module

import (
"fmt"
"slices"

"github.com/pkg/errors"
"github.com/shurcooL/graphql"
"github.com/spacelift-io/spacectl/client/structs"
"github.com/spacelift-io/spacectl/internal/cmd"
"github.com/spacelift-io/spacectl/internal/cmd/authenticated"
"github.com/urfave/cli/v2"
)

const (
maxSearchModuleVersionsPageSize = 50
moduleVersionsTableLimit = 20
moduleVersionsJSONLimit = 0 // no limit
)

func listVersions() cli.ActionFunc {
return func(cliCtx *cli.Context) error {
outputFormat, err := cmd.GetOutputFormat(cliCtx)
Expand All @@ -19,70 +27,137 @@ func listVersions() cli.ActionFunc {

switch outputFormat {
case cmd.OutputFormatTable:
return listVersionsTable(cliCtx)
versions, err := getModuleVersions(cliCtx, moduleVersionsTableLimit)
if err != nil {
return err
}

return formatModuleVersionsTable(versions)
case cmd.OutputFormatJSON:
return listVersionsJSON(cliCtx)
versions, err := getModuleVersions(cliCtx, moduleVersionsJSONLimit)
if err != nil {
return err
}

return formatModuleVersionsJSON(versions)
}

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)"`
func getModuleVersions(cliCtx *cli.Context, limit int) ([]version, error) {
if limit < 0 {
return nil, errors.New("limit must be greater or equal to 0")
}

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")
var cursor string
var versions []version

fetchAll := limit == 0

var pageSize int

for {
if fetchAll {
pageSize = maxSearchModuleVersionsPageSize
} else {
pageSize = slices.Min([]int{maxSearchModuleVersionsPageSize, limit - len(versions)})
}

result, err := getSearchModuleVersions(cliCtx, cursor, pageSize)
if err != nil {
return nil, err
}

for _, edge := range result.Edges {
versions = append(versions, edge.Node)
}

if result.PageInfo.HasNextPage && (fetchAll || limit > len(versions)) {
cursor = result.PageInfo.EndCursor
} else {
break
}
}
return cmd.OutputJSON(query.Module.Verions)

return versions, nil
}

func listVersionsTable(cliCtx *cli.Context) error {
func getSearchModuleVersions(cliCtx *cli.Context, cursor string, limit int) (searchModuleVersions, error) {
if limit <= 0 || limit > maxSearchModuleVersionsPageSize {
return searchModuleVersions{}, errors.New("limit must be between 1 and 50")
}

var query struct {
Module struct {
Verions []version `graphql:"versions(includeFailed: $includeFailed)"`
SearchModuleVersions searchModuleVersions `graphql:"searchModuleVersions(input: $input)"`
} `graphql:"module(id: $id)"`
}

var after *graphql.String
if cursor != "" {
after = graphql.NewString(graphql.String(cursor))
}

if err := authenticated.Client.Query(cliCtx.Context, &query, map[string]interface{}{
"id": cliCtx.String(flagModuleID.Name),
"includeFailed": graphql.Boolean(false),
"id": cliCtx.String(flagModuleID.Name),
"input": structs.SearchInput{
First: graphql.NewInt(graphql.Int(int32(limit))), //nolint: gosec
After: after,
OrderBy: &structs.QueryOrder{
Field: "createdAt",
Direction: "DESC",
},
Predicates: &[]structs.QueryPredicate{
{
Field: "state",
Exclude: true,
Constraint: structs.QueryFieldConstraint{
EnumEquals: &[]graphql.String{
"FAILED",
},
},
},
},
},
}); err != nil {
return errors.Wrap(err, "failed to query list of modules")
return searchModuleVersions{}, errors.Wrap(err, "failed to query list of modules")
}

return query.Module.SearchModuleVersions, nil
}

func formatModuleVersionsJSON(versions []version) error {
return cmd.OutputJSON(versions)
}

func formatModuleVersionsTable(versions []version) error {
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]
for _, v := range versions {
tableData = append(tableData, []string{
v.ID,
v.Commit.AuthorName,
v.Commit.Message,
v.Number,
v.State,
fmt.Sprintf("%d", v.VersionCount),
fmt.Sprintf("%d", v.Commit.Timestamp),
})
}

// 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),
}
return cmd.OutputTable(tableData, true)
}

tableData = append(tableData, row)
}
type searchModuleVersions struct {
PageInfo structs.PageInfo `graphql:"pageInfo"`
Edges []searchModuleVersionEdge `graphql:"edges"`
}

return cmd.OutputTable(tableData, true)
type searchModuleVersionEdge struct {
Node version `graphql:"node"`
}

type version struct {
Expand Down
1 change: 1 addition & 0 deletions internal/cmd/profile/login_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ func getCredentialsType(ctx *cli.Context) (session.CredentialsType, error) {
return 0, err
}

//nolint: gosec
return session.CredentialsType(result + 1), nil
}

Expand Down
Loading