Skip to content

Commit

Permalink
feat!: package
Browse files Browse the repository at this point in the history
  • Loading branch information
domenicsim1 authored Jan 5, 2023
1 parent 8fcc0ce commit 088df06
Show file tree
Hide file tree
Showing 19 changed files with 866 additions and 23 deletions.
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/AlecAivazis/survey/v2 v2.3.6
github.com/MakeNowJust/heredoc/v2 v2.0.1
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.20.0
github.com/bmatcuk/doublestar/v4 v4.4.0
github.com/briandowns/spinner v1.19.0
github.com/google/uuid v1.3.0
github.com/hashicorp/go-multierror v1.1.1
Expand All @@ -18,6 +19,7 @@ require (
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.14.0
github.com/stretchr/testify v1.8.1
golang.org/x/exp v0.0.0-20221217163422-3c43f8badb15
golang.org/x/term v0.3.0
)

Expand Down Expand Up @@ -50,7 +52,6 @@ require (
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/subosito/gotenv v1.4.1 // indirect
golang.org/x/crypto v0.4.0 // indirect
golang.org/x/exp v0.0.0-20221217163422-3c43f8badb15 // indirect
golang.org/x/sys v0.3.0 // indirect
golang.org/x/text v0.5.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63n
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.20.0 h1:3IpsaL5PJepA/5WEhUBn7GmXS2uT4GZ0n5N/iQV6JhQ=
github.com/OctopusDeploy/go-octopusdeploy/v2 v2.20.0/go.mod h1:++gnwUI5HaG31oDMaUXrNNjsXfmLn/zWyMmy/5GaFSA=
github.com/bmatcuk/doublestar/v4 v4.4.0 h1:LmAwNwhjEbYtyVLzjcP/XeVw4nhuScHGkF/XWXnvIic=
github.com/bmatcuk/doublestar/v4 v4.4.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
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
316 changes: 316 additions & 0 deletions pkg/cmd/package/nuget/create/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,316 @@
package create

import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"time"

"github.com/AlecAivazis/survey/v2"
"github.com/MakeNowJust/heredoc/v2"
pack "github.com/OctopusDeploy/cli/pkg/cmd/package/support"
"github.com/OctopusDeploy/cli/pkg/constants"
"github.com/OctopusDeploy/cli/pkg/factory"
"github.com/OctopusDeploy/cli/pkg/surveyext"
"github.com/OctopusDeploy/cli/pkg/util"
"github.com/OctopusDeploy/cli/pkg/util/flag"
"github.com/spf13/cobra"
)

const (
FlagAuthor = "author"
FlagTitle = "title"
FlagDescription = "description"
FlagReleaseNotes = "releaseNotes"
FlagReleaseNotesFile = "releaseNotesFile"
)

type NuPkgCreateFlags struct {
Author *flag.Flag[[]string] // this need to be multiple and default to current user
Title *flag.Flag[string]
Description *flag.Flag[string]
ReleaseNotes *flag.Flag[string]
ReleaseNotesFile *flag.Flag[string]
}

type NuPkgCreateOptions struct {
*NuPkgCreateFlags
*pack.PackageCreateOptions
}

func NewNuPkgCreateFlags() *NuPkgCreateFlags {
return &NuPkgCreateFlags{
Author: flag.New[[]string](FlagAuthor, false),
Title: flag.New[string](FlagTitle, false),
Description: flag.New[string](FlagDescription, false),
ReleaseNotes: flag.New[string](FlagReleaseNotes, false),
ReleaseNotesFile: flag.New[string](FlagReleaseNotesFile, false),
}
}

func NewCmdCreate(f factory.Factory) *cobra.Command {
createFlags := NewNuPkgCreateFlags()
packFlags := pack.NewPackageCreateFlags()

cmd := &cobra.Command{
Use: "create",
Short: "Create nuget",
Long: "Create nuget package",
Example: heredoc.Docf(`
$ %[1]s package nuget create --id SomePackage --version 1.0.0
`, constants.ExecutableName),
RunE: func(cmd *cobra.Command, _ []string) error {
opts := &NuPkgCreateOptions{
NuPkgCreateFlags: createFlags,
PackageCreateOptions: pack.NewPackageCreateOptions(f, packFlags, cmd),
}
return createRun(opts)
},
}

flags := cmd.Flags()
flags.StringVar(&packFlags.Id.Value, packFlags.Id.Name, "", "The ID of the package")
flags.StringVarP(&packFlags.Version.Value, packFlags.Version.Name, "v", "", "The version of the package, must be a valid SemVer.")
flags.StringVar(&packFlags.BasePath.Value, packFlags.BasePath.Name, "", "Root folder containing the contents to zip.")
flags.StringVar(&packFlags.OutFolder.Value, packFlags.OutFolder.Name, "", "Folder into which the zip file will be written.")
flags.StringSliceVar(&packFlags.Include.Value, packFlags.Include.Name, []string{}, "Add a file pattern to include, relative to the base path e.g. /bin/*.dll; defaults to \"**\".")
flags.BoolVar(&packFlags.Verbose.Value, packFlags.Verbose.Name, false, "Verbose output.")
flags.BoolVar(&packFlags.Overwrite.Value, packFlags.Overwrite.Name, false, "Allow an existing package file of the same ID/version to be overwritten.")
flags.StringSliceVar(&createFlags.Author.Value, createFlags.Author.Name, []string{}, "Add author/s to the package metadata.")
flags.StringVar(&createFlags.Title.Value, createFlags.Title.Name, "", "The title of the package.")
flags.StringVar(&createFlags.Description.Value, createFlags.Description.Name, "", "A description of the package, defaults to \"A deployment package created from files on disk.\".")
flags.StringVar(&createFlags.ReleaseNotes.Value, createFlags.ReleaseNotes.Name, "", "Release notes for this version of the package.")
flags.StringVar(&createFlags.ReleaseNotesFile.Value, createFlags.ReleaseNotesFile.Name, "", "A file containing release notes for this version of the package.")
flags.SortFlags = false

return cmd
}

func createRun(opts *NuPkgCreateOptions) error {
if !opts.NoPrompt {
if err := pack.PackageCreatePromptMissing(opts.PackageCreateOptions); err != nil {
return err
}
if err := PromptMissing(opts); err != nil {
return err
}
}

if opts.Id.Value == "" {
return errors.New("must supply a package ID")
}

err := applyDefaultsToUnspecifiedPackageOptions(opts)
if err != nil {
return err
}

nuspecFilePath := ""
if shouldGenerateNuSpec(opts) {
defer func() {
if nuspecFilePath != "" {
err := os.Remove(nuspecFilePath)
if err != nil {
panic(err)
}
}
}()
nuspecFilePath, err = GenerateNuSpec(opts)
if err != nil {
return err
}
opts.Include.Value = append(opts.Include.Value, opts.Id.Value+".nuspec")
}

pack.VerboseOut(opts.Writer, opts.Verbose.Value, "Packing \"%s\" version \"%s\"...\n", opts.Id.Value, opts.Version.Value)
outFilePath := pack.BuildOutFileName("nupkg", opts.Id.Value, opts.Version.Value)

err = pack.BuildPackage(opts.PackageCreateOptions, outFilePath)
if err != nil {
return err
}

if !opts.NoPrompt {
autoCmd := flag.GenerateAutomationCmd(
opts.CmdPath,
opts.Author,
opts.Title,
opts.Description,
opts.ReleaseNotes,
opts.ReleaseNotesFile,
opts.Id,
opts.Version,
opts.BasePath,
opts.OutFolder,
opts.Include,
opts.Verbose,
opts.Overwrite,
)
fmt.Fprintf(opts.Writer, "\nAutomation Command: %s\n", autoCmd)
}

return nil
}

func PromptMissing(opts *NuPkgCreateOptions) error {
if len(opts.Author.Value) == 0 {
for {
author := ""
if err := opts.Ask(&survey.Input{
Message: "Author (leave blank to continue)",
Help: "Add an author to the package metadata.",
}, &author); err != nil {
return err
}
if strings.TrimSpace(author) == "" {
break
}
opts.Author.Value = append(opts.Author.Value, author)
}
}

if len(opts.Author.Value) > 0 {
if opts.Title.Value == "" {
if err := opts.Ask(&survey.Input{
Message: "Nuspec title",
Help: "The title to include in the Nuspec file.",
}, &opts.Title.Value); err != nil {
return err
}
}

if opts.Description.Value == "" {
if err := opts.Ask(&survey.Input{
Message: "Nuspec description",
Help: "The description to include in the Nuspec file.",
Default: "A deployment package created from files on disk.",
}, &opts.Description.Value); err != nil {
return err
}
}

if opts.ReleaseNotes.Value == "" {
if err := opts.Ask(&surveyext.OctoEditor{
Editor: &survey.Editor{
Message: "Nuspec release notes",
Help: "The release notes to include in the Nuspec file.",
},
Optional: true,
}, &opts.ReleaseNotes.Value); err != nil {
return err
}
}

if opts.ReleaseNotesFile.Value == "" {
if err := opts.Ask(&survey.Input{
Message: "Nuspec release notes file",
Help: "A path to a release notes file whose contents will be included in the Nuspec file's release notes.",
}, &opts.ReleaseNotesFile.Value); err != nil {
return err
}
}
}

return nil
}

func applyDefaultsToUnspecifiedPackageOptions(opts *NuPkgCreateOptions) error {
if opts.Version.Value == "" {
opts.Version.Value = pack.BuildTimestampSemVer(time.Now())
}

if opts.BasePath.Value == "" {
opts.BasePath.Value = "."
}

if opts.OutFolder.Value == "" {
opts.OutFolder.Value = "."
}

if len(opts.Include.Value) == 0 {
opts.Include.Value = append(opts.Include.Value, "**")
}

if len(opts.Author.Value) > 0 {
if opts.Description.Value == "" {
opts.Description.Value = "A deployment package created from files on disk."
}
}

return nil
}

func getReleaseNotesFromFile(filePath string) (string, error) {
_, err := os.Stat(filePath)
if err != nil {
return "", err
}

notes, err := os.ReadFile(filePath)
if err != nil {
return "", err
}

return string(notes), nil
}

func shouldGenerateNuSpec(opts *NuPkgCreateOptions) bool {
return opts.Description.Value != "" ||
opts.Title.Value != "" ||
opts.ReleaseNotes.Value != "" ||
opts.ReleaseNotesFile.Value != "" ||
!util.Empty(opts.Author.Value)
}

func GenerateNuSpec(opts *NuPkgCreateOptions) (string, error) {

if opts.Description.Value == "" {
return "", errors.New("description is required when generating nuspec metadata")
}
if len(opts.Author.Value) == 0 {
return "", errors.New("at least one author is required when generating nuspec metadata")
}

releaseNotes := opts.ReleaseNotes.Value
if opts.ReleaseNotesFile.Value != "" {
if releaseNotes != "" {
return "", errors.New(`cannot specify both "Nuspec release notes" and "Nuspec release notes file"`)
}

notes, err := getReleaseNotesFromFile(opts.ReleaseNotesFile.Value)
releaseNotes = notes
if err != nil {
return "", err
}
}

filePath := filepath.Join(opts.BasePath.Value, opts.Id.Value+".nuspec")

var sb strings.Builder
sb.WriteString(`<?xml version="1.0" encoding="utf-8"?>` + "\n")
sb.WriteString(`<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">` + "\n")
sb.WriteString(" <metadata>\n")
sb.WriteString(" <id>" + opts.Id.Value + "</id>\n")
sb.WriteString(" <version>" + opts.Version.Value + "</version>\n")
sb.WriteString(" <description>" + opts.Description.Value + "</description>\n")
sb.WriteString(" <authors>" + strings.Join(opts.Author.Value, ",") + "</authors>\n")
if releaseNotes != "" {
sb.WriteString(" <releaseNotes>" + releaseNotes + "</releaseNotes>\n")
}
sb.WriteString(" </metadata>\n")
sb.WriteString("</package>\n")

file, err := os.Create(filePath)
if err != nil {
return "", err
}

_, err = file.WriteString(sb.String())
if err != nil {
return "", err
}

return filePath, file.Close()
}
22 changes: 22 additions & 0 deletions pkg/cmd/package/nuget/nuget.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package nuget

import (
"github.com/MakeNowJust/heredoc/v2"
cmdNugetCreate "github.com/OctopusDeploy/cli/pkg/cmd/package/nuget/create"
"github.com/OctopusDeploy/cli/pkg/constants"
"github.com/OctopusDeploy/cli/pkg/factory"
"github.com/spf13/cobra"
)

func NewCmdPackageNuget(f factory.Factory) *cobra.Command {
cmd := &cobra.Command{
Use: "nuget <command>",
Short: "Package as NuPkg",
Long: "Package as NuPkg for Octopus Deploy",
Example: heredoc.Docf("$ %s package nuget create", constants.ExecutableName),
}

cmd.AddCommand(cmdNugetCreate.NewCmdCreate(f))

return cmd
}
5 changes: 4 additions & 1 deletion pkg/cmd/package/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package _package

import (
"fmt"

cmdList "github.com/OctopusDeploy/cli/pkg/cmd/package/list"
cmdNuget "github.com/OctopusDeploy/cli/pkg/cmd/package/nuget"
cmdUpload "github.com/OctopusDeploy/cli/pkg/cmd/package/upload"
cmdVersions "github.com/OctopusDeploy/cli/pkg/cmd/package/versions"
cmdZip "github.com/OctopusDeploy/cli/pkg/cmd/package/zip"
"github.com/OctopusDeploy/cli/pkg/constants"
"github.com/OctopusDeploy/cli/pkg/constants/annotations"
"github.com/OctopusDeploy/cli/pkg/factory"
Expand All @@ -26,5 +27,7 @@ func NewCmdPackage(f factory.Factory) *cobra.Command {
cmd.AddCommand(cmdUpload.NewCmdUpload(f))
cmd.AddCommand(cmdList.NewCmdList(f))
cmd.AddCommand(cmdVersions.NewCmdVersions(f))
cmd.AddCommand(cmdNuget.NewCmdPackageNuget(f))
cmd.AddCommand(cmdZip.NewCmdPackageZip(f))
return cmd
}
Loading

0 comments on commit 088df06

Please sign in to comment.