Skip to content

Commit

Permalink
feat: package upload/push
Browse files Browse the repository at this point in the history
feat: package list
feat: package versions
  • Loading branch information
borland authored Sep 29, 2022
1 parent 724f365 commit 28090f3
Show file tree
Hide file tree
Showing 16 changed files with 1,255 additions and 7 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div align="center">
<img alt="go-octopusdeploy Logo" src="https://user-images.githubusercontent.com/71493/133961475-fd4d769f-dc32-4723-a9bd-5529c5b12faf.png" height="140" />
<img alt="go-octopusdeploy Logo" src="logo.png" width="256" />
<h3 align="center">cli</h3>
<p align="center">Command Line Interface for <a href="https://octopus.com/">Octopus Deploy</a> 🐙</p>
<p align="center">
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ go 1.19
require (
github.com/AlecAivazis/survey/v2 v2.3.5
github.com/MakeNowJust/heredoc/v2 v2.0.1
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.5.0
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.6.0
github.com/briandowns/spinner v1.19.0
github.com/google/uuid v1.3.0
github.com/hashicorp/go-multierror v1.1.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ github.com/MakeNowJust/heredoc/v2 v2.0.1 h1:rlCHh70XXXv7toz95ajQWOWQnN4WNLt0TdpZ
github.com/MakeNowJust/heredoc/v2 v2.0.1/go.mod h1:6/2Abh5s+hc3g9nbWLe9ObDIOhaRrqsyY9MWy+4JdRM=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.5.0 h1:oyIAeLNcy1+BI8ok4TwWaP970LyMsmIBMxEVPtgEl9Y=
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.5.0/go.mod h1:XWqxyDUVElUlTaPqyCBblukpsHSnPcAKkAHgJgbsIAs=
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.6.0 h1:GlOFTpWS1B/wa0Lr3w/tL9xBcMzLmeFyFrN/LgH1b28=
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.6.0/go.mod h1:XWqxyDUVElUlTaPqyCBblukpsHSnPcAKkAHgJgbsIAs=
github.com/briandowns/spinner v1.19.0 h1:s8aq38H+Qju89yhp89b4iIiMzMm8YN3p6vGpwyh/a8E=
github.com/briandowns/spinner v1.19.0/go.mod h1:mQak9GHqbspjC/5iUx3qMlIho8xBS/ppAL/hX5SmPJU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
Expand Down
Binary file added logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
99 changes: 99 additions & 0 deletions pkg/cmd/package/list/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package list

import (
"github.com/MakeNowJust/heredoc/v2"
"github.com/OctopusDeploy/cli/pkg/factory"
"github.com/OctopusDeploy/cli/pkg/output"
"github.com/OctopusDeploy/cli/pkg/util/flag"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/packages"
"github.com/spf13/cobra"
"math"
)

const (
FlagLimit = "limit"
FlagFilter = "filter"
)

type ListFlags struct {
Limit *flag.Flag[int32]
Filter *flag.Flag[string]
}

func NewListFlags() *ListFlags {
return &ListFlags{
Limit: flag.New[int32](FlagLimit, false),
Filter: flag.New[string](FlagFilter, false),
}
}

func NewCmdList(f factory.Factory) *cobra.Command {
listFlags := NewListFlags()

cmd := &cobra.Command{
Use: "list",
Short: "List packages in Octopus Deploy",
Long: "List packages in Octopus Deploy.",
Example: heredoc.Doc(`
$ octopus package list
$ octopus package list --limit 50 --filter SomePackage
$ octopus package ls -n 30 -q SomePackage
`),
Aliases: []string{"ls"},
RunE: func(cmd *cobra.Command, args []string) error {

return listRun(cmd, f, listFlags)
},
}

flags := cmd.Flags()
flags.Int32VarP(&listFlags.Limit.Value, listFlags.Limit.Name, "n", 0, "limit the maximum number of results that will be returned")
flags.StringVarP(&listFlags.Filter.Value, listFlags.Filter.Name, "q", "", "filter packages to match only ones that contain the given string")
return cmd
}

type PackageViewModel struct {
ID string
Version string
Description string
}

func listRun(cmd *cobra.Command, f factory.Factory, flags *ListFlags) error {
limit := flags.Limit.Value
filter := flags.Filter.Value

octopus, err := f.GetSpacedClient()
if err != nil {
return err
}

// the underlying API has skip, take and paginated results, and will return 30 packages by default.
// this kind of behaviour isn't going to be the expected default for a CLI, so we instead default to
// returning "everything" if a limit is unspecified
if limit <= 0 {
limit = math.MaxInt32
}

page, err := packages.List(octopus, f.GetCurrentSpace().ID, filter, int(limit))
if err != nil {
return err
}

return output.PrintArray(page.Items, cmd, output.Mappers[*packages.Package]{
Json: func(item *packages.Package) any {
return PackageViewModel{
ID: item.PackageID,
Version: item.Version,
Description: item.Description,
}
},
Table: output.TableDefinition[*packages.Package]{
Header: []string{"ID", "HIGHEST VERSION", "DESCRIPTION"},
Row: func(item *packages.Package) []string {
return []string{item.PackageID, item.Version, item.Description}
}},
Basic: func(item *packages.Package) string {
return item.PackageID
},
})
}
152 changes: 152 additions & 0 deletions pkg/cmd/package/list/list_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package list_test

import (
"bytes"
"github.com/MakeNowJust/heredoc/v2"
cmdRoot "github.com/OctopusDeploy/cli/pkg/cmd/root"
"github.com/OctopusDeploy/cli/test/fixtures"
"github.com/OctopusDeploy/cli/test/testutil"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/packages"
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/resources"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"testing"
)

var rootResource = testutil.NewRootResource()

func TestPackageList(t *testing.T) {
const spaceID = "Spaces-1"
space1 := fixtures.NewSpace(spaceID, "Default Space")

tests := []struct {
name string
run func(t *testing.T, api *testutil.MockHttpServer, rootCmd *cobra.Command, stdOut *bytes.Buffer, stdErr *bytes.Buffer)
}{
{"list all packages by default", func(t *testing.T, api *testutil.MockHttpServer, rootCmd *cobra.Command, stdOut *bytes.Buffer, stdErr *bytes.Buffer) {
cmdReceiver := testutil.GoBegin2(func() (*cobra.Command, error) {
defer api.Close()
rootCmd.SetArgs([]string{"package", "list"})
return rootCmd.ExecuteC()
})

api.ExpectRequest(t, "GET", "/api").RespondWith(rootResource)
api.ExpectRequest(t, "GET", "/api/Spaces-1").RespondWith(rootResource)

api.ExpectRequest(t, "GET", "/api/Spaces-1/packages?take=2147483647").RespondWith(&resources.Resources[*packages.Package]{
Items: []*packages.Package{
{PackageID: "pterm", Version: "0.12.51", Description: "some sort of package"},
{PackageID: "NuGet.CommandLine", Version: "6.2.1"},
},
})

_, err := testutil.ReceivePair(cmdReceiver)
assert.Nil(t, err)
assert.Equal(t, heredoc.Doc(`
ID HIGHEST VERSION DESCRIPTION
pterm 0.12.51 some sort of package
NuGet.CommandLine 6.2.1
`), stdOut.String())
assert.Equal(t, "", stdErr.String())
}},

{"pass through filter and limit", func(t *testing.T, api *testutil.MockHttpServer, rootCmd *cobra.Command, stdOut *bytes.Buffer, stdErr *bytes.Buffer) {
cmdReceiver := testutil.GoBegin2(func() (*cobra.Command, error) {
defer api.Close()
rootCmd.SetArgs([]string{"package", "list", "--filter", "pterm", "--limit", "1"})
return rootCmd.ExecuteC()
})

api.ExpectRequest(t, "GET", "/api").RespondWith(rootResource)
api.ExpectRequest(t, "GET", "/api/Spaces-1").RespondWith(rootResource)

api.ExpectRequest(t, "GET", "/api/Spaces-1/packages?filter=pterm&take=1").RespondWith(&resources.Resources[*packages.Package]{
Items: []*packages.Package{
{PackageID: "pterm", Version: "0.12.51", Description: "some sort of package"},
},
})

_, err := testutil.ReceivePair(cmdReceiver)
assert.Nil(t, err)
assert.Equal(t, heredoc.Doc(`
ID HIGHEST VERSION DESCRIPTION
pterm 0.12.51 some sort of package
`), stdOut.String())
assert.Equal(t, "", stdErr.String())
}},

{"outputformat json", func(t *testing.T, api *testutil.MockHttpServer, rootCmd *cobra.Command, stdOut *bytes.Buffer, stdErr *bytes.Buffer) {
cmdReceiver := testutil.GoBegin2(func() (*cobra.Command, error) {
defer api.Close()
rootCmd.SetArgs([]string{"package", "list", "--output-format", "json"})
return rootCmd.ExecuteC()
})

api.ExpectRequest(t, "GET", "/api").RespondWith(rootResource)
api.ExpectRequest(t, "GET", "/api/Spaces-1").RespondWith(rootResource)

api.ExpectRequest(t, "GET", "/api/Spaces-1/packages?take=2147483647").RespondWith(&resources.Resources[*packages.Package]{
Items: []*packages.Package{
{PackageID: "pterm", Version: "0.12.51", Description: "some sort of package"},
{PackageID: "NuGet.CommandLine", Version: "6.2.1"},
},
})

_, err := testutil.ReceivePair(cmdReceiver)
assert.Nil(t, err)

type x struct {
ID string
Version string
Description string
}
parsedStdout, err := testutil.ParseJsonStrict[[]x](stdOut)
assert.Nil(t, err)

assert.Equal(t, []x{
{ID: "pterm", Version: "0.12.51", Description: "some sort of package"},
{ID: "NuGet.CommandLine", Version: "6.2.1"},
}, parsedStdout)

assert.Equal(t, "", stdErr.String())
}},

{"outputformat basic", func(t *testing.T, api *testutil.MockHttpServer, rootCmd *cobra.Command, stdOut *bytes.Buffer, stdErr *bytes.Buffer) {
cmdReceiver := testutil.GoBegin2(func() (*cobra.Command, error) {
defer api.Close()
rootCmd.SetArgs([]string{"package", "list", "--output-format", "basic"})
return rootCmd.ExecuteC()
})

api.ExpectRequest(t, "GET", "/api").RespondWith(rootResource)
api.ExpectRequest(t, "GET", "/api/Spaces-1").RespondWith(rootResource)

api.ExpectRequest(t, "GET", "/api/Spaces-1/packages?take=2147483647").RespondWith(&resources.Resources[*packages.Package]{
Items: []*packages.Package{
{PackageID: "pterm", Version: "0.12.51", Description: "some sort of package"},
{PackageID: "NuGet.CommandLine", Version: "6.2.1"},
},
})

_, err := testutil.ReceivePair(cmdReceiver)
assert.Nil(t, err)
assert.Equal(t, heredoc.Doc(`
pterm
NuGet.CommandLine
`), stdOut.String())
assert.Equal(t, "", stdErr.String())
}},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
stdout, stderr := &bytes.Buffer{}, &bytes.Buffer{}
api := testutil.NewMockHttpServer()
fac := testutil.NewMockFactoryWithSpaceAndPrompt(api, space1, nil)
rootCmd := cmdRoot.NewCmdRoot(fac, nil, nil)
rootCmd.SetOut(stdout)
rootCmd.SetErr(stderr)
test.run(t, api, rootCmd, stdout, stderr)
})
}
}
29 changes: 29 additions & 0 deletions pkg/cmd/package/package.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package _package

import (
"fmt"
cmdList "github.com/OctopusDeploy/cli/pkg/cmd/package/list"
cmdUpload "github.com/OctopusDeploy/cli/pkg/cmd/package/upload"
cmdVersions "github.com/OctopusDeploy/cli/pkg/cmd/package/versions"
"github.com/OctopusDeploy/cli/pkg/constants"
"github.com/OctopusDeploy/cli/pkg/constants/annotations"
"github.com/OctopusDeploy/cli/pkg/factory"
"github.com/spf13/cobra"
)

func NewCmdPackage(f factory.Factory) *cobra.Command {
cmd := &cobra.Command{
Use: "package <command>",
Short: "Manage packages",
Long: `Work with Octopus Deploy packages.`,
Example: fmt.Sprintf("$ %s package upload", constants.ExecutableName),
Annotations: map[string]string{
annotations.IsCore: "true",
},
}

cmd.AddCommand(cmdUpload.NewCmdUpload(f))
cmd.AddCommand(cmdList.NewCmdList(f))
cmd.AddCommand(cmdVersions.NewCmdVersions(f))
return cmd
}
Loading

0 comments on commit 28090f3

Please sign in to comment.