From 6ef523a711474e0112f4aac1b1dc641c9ee41d43 Mon Sep 17 00:00:00 2001 From: Robi Nino Date: Thu, 29 Jun 2023 12:20:04 +0300 Subject: [PATCH 01/13] Add Lifecycle Release Bundles Support --- .github/workflows/lifeCycleTests.yml | 43 +++ artifactory/cli.go | 19 +- artifactory/cli_test.go | 34 +-- artifactory_test.go | 2 +- distribution/cli.go | 12 +- docs/artifactory/releasebundlecreate/help.go | 2 +- docs/artifactory/releasebundledelete/help.go | 2 +- .../releasebundledistribute/help.go | 2 +- docs/artifactory/releasebundlesign/help.go | 2 +- docs/artifactory/releasebundleupdate/help.go | 2 +- docs/lifecycle/create/help.go | 18 ++ docs/lifecycle/promote/help.go | 18 ++ go.mod | 4 +- go.sum | 4 - lifecycle/cli.go | 130 +++++++++ lifecycle/cli_test.go | 38 +++ lifecycle_test.go | 257 ++++++++++++++++++ main.go | 2 + main_test.go | 47 ++-- maven_test.go | 30 +- testdata/dev_repo_repository_config.json | 6 + testdata/filespecs/search_all_prod_repo.json | 7 + testdata/filespecs/upload_dev_spec_a.json | 10 + testdata/filespecs/upload_dev_spec_b.json | 10 + testdata/filespecs/upload_dev_spec_c.json | 10 + testdata/lifecycle/builds-spec-1-2.json | 13 + testdata/lifecycle/builds-spec-3.json | 8 + testdata/lifecycle/keys/private.txt | 83 ++++++ testdata/lifecycle/keys/public.txt | 41 +++ testdata/lifecycle/release-bundles-spec.json | 12 + testdata/prod_repo_repository_config.json | 6 + utils/cliutils/commandsflags.go | 113 +++++--- utils/cliutils/utils.go | 18 +- utils/tests/consts.go | 71 ++--- utils/tests/utils.go | 43 +++ 35 files changed, 952 insertions(+), 167 deletions(-) create mode 100644 .github/workflows/lifeCycleTests.yml create mode 100644 docs/lifecycle/create/help.go create mode 100644 docs/lifecycle/promote/help.go create mode 100644 lifecycle/cli.go create mode 100644 lifecycle/cli_test.go create mode 100644 lifecycle_test.go create mode 100644 testdata/dev_repo_repository_config.json create mode 100644 testdata/filespecs/search_all_prod_repo.json create mode 100644 testdata/filespecs/upload_dev_spec_a.json create mode 100644 testdata/filespecs/upload_dev_spec_b.json create mode 100644 testdata/filespecs/upload_dev_spec_c.json create mode 100644 testdata/lifecycle/builds-spec-1-2.json create mode 100644 testdata/lifecycle/builds-spec-3.json create mode 100644 testdata/lifecycle/keys/private.txt create mode 100644 testdata/lifecycle/keys/public.txt create mode 100644 testdata/lifecycle/release-bundles-spec.json create mode 100644 testdata/prod_repo_repository_config.json diff --git a/.github/workflows/lifeCycleTests.yml b/.github/workflows/lifeCycleTests.yml new file mode 100644 index 000000000..74e2a8ff4 --- /dev/null +++ b/.github/workflows/lifeCycleTests.yml @@ -0,0 +1,43 @@ +name: Lifecycle Tests +on: + push: + # Triggers the workflow on labeled PRs only. + pull_request_target: + types: [labeled] +# Ensures that only the latest commit is running for each PR at a time. +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}-${{ github.ref }} + cancel-in-progress: true +jobs: + Life-Cycle-Tests: + if: contains(github.event.pull_request.labels.*.name, 'safe to test') || github.event_name == 'push' + name: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + runs-on: ${{ matrix.os }} + steps: + - name: Install Go + uses: actions/setup-go@v3 + with: + go-version: 1.20.x + - name: Checkout code + uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha }} + - name: Go Cache + uses: actions/cache@v3 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: ${{ runner.os }}-go- + - name: Setup Artifactory + run: | + go install github.com/jfrog/jfrog-testing-infra/local-rt-setup@latest + ~/go/bin/local-rt-setup + env: + RTLIC: ${{secrets.RTLIC}} + GOPROXY: direct + - name: Run Lifecycle tests + run: go test -v github.com/jfrog/jfrog-cli --timeout 0 --test.rb diff --git a/artifactory/cli.go b/artifactory/cli.go index 6e35b64ad..6842aa046 100644 --- a/artifactory/cli.go +++ b/artifactory/cli.go @@ -2412,7 +2412,7 @@ func createDefaultCopyMoveSpec(c *cli.Context) (*spec.SpecFiles, error) { Props(c.String("props")). ExcludeProps(c.String("exclude-props")). Build(c.String("build")). - Project(getProject(c)). + Project(cliutils.GetProject(c)). ExcludeArtifacts(c.Bool("exclude-artifacts")). IncludeDeps(c.Bool("include-deps")). Bundle(c.String("bundle")). @@ -2439,7 +2439,7 @@ func createDefaultDeleteSpec(c *cli.Context) (*spec.SpecFiles, error) { Props(c.String("props")). ExcludeProps(c.String("exclude-props")). Build(c.String("build")). - Project(getProject(c)). + Project(cliutils.GetProject(c)). ExcludeArtifacts(c.Bool("exclude-artifacts")). IncludeDeps(c.Bool("include-deps")). Bundle(c.String("bundle")). @@ -2463,7 +2463,7 @@ func createDefaultSearchSpec(c *cli.Context) (*spec.SpecFiles, error) { Props(c.String("props")). ExcludeProps(c.String("exclude-props")). Build(c.String("build")). - Project(getProject(c)). + Project(cliutils.GetProject(c)). ExcludeArtifacts(c.Bool("exclude-artifacts")). IncludeDeps(c.Bool("include-deps")). Bundle(c.String("bundle")). @@ -2490,7 +2490,7 @@ func createDefaultPropertiesSpec(c *cli.Context) (*spec.SpecFiles, error) { Props(c.String("props")). ExcludeProps(c.String("exclude-props")). Build(c.String("build")). - Project(getProject(c)). + Project(cliutils.GetProject(c)). ExcludeArtifacts(c.Bool("exclude-artifacts")). IncludeDeps(c.Bool("include-deps")). Bundle(c.String("bundle")). @@ -2586,7 +2586,7 @@ func createDefaultDownloadSpec(c *cli.Context) (*spec.SpecFiles, error) { Props(c.String("props")). ExcludeProps(c.String("exclude-props")). Build(c.String("build")). - Project(getProject(c)). + Project(cliutils.GetProject(c)). ExcludeArtifacts(c.Bool("exclude-artifacts")). IncludeDeps(c.Bool("include-deps")). Bundle(c.String("bundle")). @@ -2722,12 +2722,3 @@ func getOffsetAndLimitValues(c *cli.Context) (offset, limit int, err error) { return } - -// Get project key from flag or environment variable -func getProject(c *cli.Context) string { - project := c.String("project") - if project == "" { - project = os.Getenv(coreutils.Project) - } - return project -} diff --git a/artifactory/cli_test.go b/artifactory/cli_test.go index fb848c0d4..e951be591 100644 --- a/artifactory/cli_test.go +++ b/artifactory/cli_test.go @@ -2,15 +2,12 @@ package artifactory import ( "bytes" - "flag" "github.com/jfrog/jfrog-cli-core/v2/common/spec" - "path/filepath" - "strings" - "testing" - "github.com/jfrog/jfrog-cli/utils/tests" "github.com/stretchr/testify/assert" "github.com/urfave/cli" + "path/filepath" + "testing" ) func TestPrepareSearchDownloadDeleteCommands(t *testing.T) { @@ -34,7 +31,7 @@ func TestPrepareSearchDownloadDeleteCommands(t *testing.T) { for _, test := range testRuns { t.Run(test.name, func(t *testing.T) { - context, buffer := createContext(t, test.flags, test.args) + context, buffer := tests.CreateContext(t, test.flags, test.args) funcArray := []func(c *cli.Context) (*spec.SpecFiles, error){ prepareSearchCommand, prepareDownloadCommand, prepareDeleteCommand, } @@ -67,7 +64,7 @@ func TestPrepareCopyMoveCommand(t *testing.T) { for _, test := range testRuns { t.Run(test.name, func(t *testing.T) { - context, buffer := createContext(t, test.flags, test.args) + context, buffer := tests.CreateContext(t, test.flags, test.args) specFiles, err := prepareCopyMoveCommand(context) assertGenericCommand(t, err, buffer, test.expectError, test.expectedPattern, test.expectedBuild, test.expectedBundle, specFiles) }) @@ -96,7 +93,7 @@ func TestPreparePropsCmd(t *testing.T) { for _, test := range testRuns { t.Run(test.name, func(t *testing.T) { - context, buffer := createContext(t, test.flags, test.args) + context, buffer := tests.CreateContext(t, test.flags, test.args) propsCommand, err := preparePropsCmd(context) var actualSpec *spec.SpecFiles if propsCommand != nil { @@ -119,27 +116,6 @@ func assertGenericCommand(t *testing.T, err error, buffer *bytes.Buffer, expectE } } -func createContext(t *testing.T, testFlags, testArgs []string) (*cli.Context, *bytes.Buffer) { - flagSet := createFlagSet(t, testFlags, testArgs) - app := cli.NewApp() - app.Writer = &bytes.Buffer{} - return cli.NewContext(app, flagSet, nil), &bytes.Buffer{} -} - func getSpecPath(spec string) string { return filepath.Join("..", "testdata", "filespecs", spec) } - -// Create flag set with input flags and arguments. -func createFlagSet(t *testing.T, flags []string, args []string) *flag.FlagSet { - flagSet := flag.NewFlagSet("TestFlagSet", flag.ContinueOnError) - flags = append(flags, "url=http://127.0.0.1:8081/artifactory") - var cmdFlags []string - for _, curFlag := range flags { - flagSet.String(strings.Split(curFlag, "=")[0], "", "") - cmdFlags = append(cmdFlags, "--"+curFlag) - } - cmdFlags = append(cmdFlags, args...) - assert.NoError(t, flagSet.Parse(cmdFlags)) - return flagSet -} diff --git a/artifactory_test.go b/artifactory_test.go index 250545b05..9a5fbe194 100644 --- a/artifactory_test.go +++ b/artifactory_test.go @@ -4549,7 +4549,7 @@ func validateArtifactoryVersion(t *testing.T, minVersion string) { return } if !rtVersion.AtLeast(minVersion) { - t.Skip("Skipping artifactory project test. Artifactory version not supported.") + t.Skip("Skipping test. Artifactory version not supported.") } } diff --git a/distribution/cli.go b/distribution/cli.go index 81d04c537..682156767 100644 --- a/distribution/cli.go +++ b/distribution/cli.go @@ -28,7 +28,7 @@ func GetCommands() []cli.Command { return cliutils.GetSortedCommands(cli.CommandsByName{ { Name: "release-bundle-create", - Flags: cliutils.GetCommandFlags(cliutils.ReleaseBundleCreate), + Flags: cliutils.GetCommandFlags(cliutils.ReleaseBundleV1Create), Aliases: []string{"rbc"}, Usage: releasebundlecreate.GetDescription(), HelpName: coreCommonDocs.CreateUsage("ds rbc", releasebundlecreate.GetDescription(), releasebundlecreate.Usage), @@ -39,7 +39,7 @@ func GetCommands() []cli.Command { }, { Name: "release-bundle-update", - Flags: cliutils.GetCommandFlags(cliutils.ReleaseBundleUpdate), + Flags: cliutils.GetCommandFlags(cliutils.ReleaseBundleV1Update), Aliases: []string{"rbu"}, Usage: releasebundleupdate.GetDescription(), HelpName: coreCommonDocs.CreateUsage("ds rbu", releasebundleupdate.GetDescription(), releasebundleupdate.Usage), @@ -50,7 +50,7 @@ func GetCommands() []cli.Command { }, { Name: "release-bundle-sign", - Flags: cliutils.GetCommandFlags(cliutils.ReleaseBundleSign), + Flags: cliutils.GetCommandFlags(cliutils.ReleaseBundleV1Sign), Aliases: []string{"rbs"}, Usage: releasebundlesign.GetDescription(), HelpName: coreCommonDocs.CreateUsage("ds rbs", releasebundlesign.GetDescription(), releasebundlesign.Usage), @@ -61,7 +61,7 @@ func GetCommands() []cli.Command { }, { Name: "release-bundle-distribute", - Flags: cliutils.GetCommandFlags(cliutils.ReleaseBundleDistribute), + Flags: cliutils.GetCommandFlags(cliutils.ReleaseBundleV1Distribute), Aliases: []string{"rbd"}, Usage: releasebundledistribute.GetDescription(), HelpName: coreCommonDocs.CreateUsage("ds rbd", releasebundledistribute.GetDescription(), releasebundledistribute.Usage), @@ -72,7 +72,7 @@ func GetCommands() []cli.Command { }, { Name: "release-bundle-delete", - Flags: cliutils.GetCommandFlags(cliutils.ReleaseBundleDelete), + Flags: cliutils.GetCommandFlags(cliutils.ReleaseBundleV1Delete), Aliases: []string{"rbdel"}, Usage: releasebundledelete.GetDescription(), HelpName: coreCommonDocs.CreateUsage("ds rbdel", releasebundledelete.GetDescription(), releasebundledelete.Usage), @@ -328,7 +328,7 @@ func populateReleaseNotesSyntax(c *cli.Context) (distributionServicesUtils.Relea return distributionServicesUtils.PlainText, errorutils.CheckErrorf("--release-notes-syntax must be one of: markdown, asciidoc or plain_text.") } } - // If the file extension is ".md" or ".markdown", use the markdown syntax + // If the file extension is ".md" or ".markdown", use the Markdown syntax extension := strings.ToLower(filepath.Ext(c.String("release-notes-path"))) if extension == ".md" || extension == ".markdown" { return distributionServicesUtils.Markdown, nil diff --git a/docs/artifactory/releasebundlecreate/help.go b/docs/artifactory/releasebundlecreate/help.go index 5448b96ed..91df44ceb 100644 --- a/docs/artifactory/releasebundlecreate/help.go +++ b/docs/artifactory/releasebundlecreate/help.go @@ -4,7 +4,7 @@ var Usage = []string{"ds rbc [command options] [command options] "} func GetDescription() string { - return "Create a release bundle." + return "Create a release bundle v1." } func GetArguments() string { diff --git a/docs/artifactory/releasebundledelete/help.go b/docs/artifactory/releasebundledelete/help.go index ce377d18b..ec9e1fe7a 100644 --- a/docs/artifactory/releasebundledelete/help.go +++ b/docs/artifactory/releasebundledelete/help.go @@ -3,7 +3,7 @@ package releasebundledelete var Usage = []string{"ds rbdel [command options] "} func GetDescription() string { - return "Delete a release bundle." + return "Delete a release bundle v1." } func GetArguments() string { diff --git a/docs/artifactory/releasebundledistribute/help.go b/docs/artifactory/releasebundledistribute/help.go index 0fe5459ec..b277a84d3 100644 --- a/docs/artifactory/releasebundledistribute/help.go +++ b/docs/artifactory/releasebundledistribute/help.go @@ -3,7 +3,7 @@ package releasebundledistribute var Usage = []string{"ds rbd [command options] "} func GetDescription() string { - return "Distribute a release bundle." + return "Distribute a release bundle v1." } func GetArguments() string { diff --git a/docs/artifactory/releasebundlesign/help.go b/docs/artifactory/releasebundlesign/help.go index d576353a6..977874bd8 100644 --- a/docs/artifactory/releasebundlesign/help.go +++ b/docs/artifactory/releasebundlesign/help.go @@ -3,7 +3,7 @@ package releasebundlesign var Usage = []string{"ds rbs [command options] "} func GetDescription() string { - return "Sign a release bundle." + return "Sign a release bundle v1." } func GetArguments() string { diff --git a/docs/artifactory/releasebundleupdate/help.go b/docs/artifactory/releasebundleupdate/help.go index 0f80fc4af..d7cd5541f 100644 --- a/docs/artifactory/releasebundleupdate/help.go +++ b/docs/artifactory/releasebundleupdate/help.go @@ -4,7 +4,7 @@ var Usage = []string{"ds rbu [command options] [command options] "} func GetDescription() string { - return "Updates an existing unsigned release bundle version." + return "Updates an existing unsigned release bundle v1 version." } func GetArguments() string { diff --git a/docs/lifecycle/create/help.go b/docs/lifecycle/create/help.go new file mode 100644 index 000000000..66a2e78e2 --- /dev/null +++ b/docs/lifecycle/create/help.go @@ -0,0 +1,18 @@ +package create + +var Usage = []string{"rbc [command options] "} + +func GetDescription() string { + return "Create a release bundle from builds or release bundles" +} + +func GetArguments() string { + return ` release bundle name + Name of the newly created Release Bundle. + + release bundle version + Version of the newly created Release Bundle. + + signing key name + The GPG/RSA key-pair name given in Artifactory.` +} diff --git a/docs/lifecycle/promote/help.go b/docs/lifecycle/promote/help.go new file mode 100644 index 000000000..762f6e946 --- /dev/null +++ b/docs/lifecycle/promote/help.go @@ -0,0 +1,18 @@ +package promote + +var Usage = []string{"rbp [command options] "} + +func GetDescription() string { + return "Promote a release bundle" +} + +func GetArguments() string { + return ` release bundle name + Name of the Release Bundle to promote. + + release bundle version + Version of the Release Bundle to promote. + + signing key name + The GPG/RSA key-pair name given in Artifactory.` +} diff --git a/go.mod b/go.mod index f93d7a975..d6eaf5fd6 100644 --- a/go.mod +++ b/go.mod @@ -123,8 +123,8 @@ require ( // replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go -// replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20230613104333-33061fa53a01 +replace github.com/jfrog/jfrog-cli-core/v2 => ../jfrog-cli-core // replace github.com/jfrog/gofrog => github.com/jfrog/gofrog v1.2.6-0.20230418122323-2bf299dd6d27 -// replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20230618140310-d7dc9bc462c2 +replace github.com/jfrog/jfrog-client-go => ../jfrog-client-go diff --git a/go.sum b/go.sum index e6454b94f..00ff0d371 100644 --- a/go.sum +++ b/go.sum @@ -238,10 +238,6 @@ github.com/jfrog/build-info-go v1.9.6 h1:lCJ2j5uXAlJsSwDe5J8WD7Co1f/hUlZvMfwfb5A github.com/jfrog/build-info-go v1.9.6/go.mod h1:GbuFS+viHCKZYx9nWHYu7ab1DgQkFdtVN3BJPUNb2D4= github.com/jfrog/gofrog v1.3.0 h1:o4zgsBZE4QyDbz2M7D4K6fXPTBJht+8lE87mS9bw7Gk= github.com/jfrog/gofrog v1.3.0/go.mod h1:IFMc+V/yf7rA5WZ74CSbXe+Lgf0iApEQLxRZVzKRUR0= -github.com/jfrog/jfrog-cli-core/v2 v2.36.1 h1:O67ovxouqXYljE2Lo890ytM9fDiYsMCZJ8F/DHdSY7w= -github.com/jfrog/jfrog-cli-core/v2 v2.36.1/go.mod h1:Wvf/XWVcRSu1ZuloLOofkifuM8BLZZ2LiYYzmdUn80Y= -github.com/jfrog/jfrog-client-go v1.30.1 h1:wASYBrFkpWzQHTNnCIIfqpDLtQF5oNcwQK9rrv8I8AA= -github.com/jfrog/jfrog-client-go v1.30.1/go.mod h1:qEJxoe68sUtqHJ1YhXv/7pKYP/9p1D5tJrruzJKYeoI= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jszwec/csvutil v1.8.0 h1:G7vS2LGdpZZDH1HmHeNbxOaJ/ZnJlpwGFvOkTkJzzNk= diff --git a/lifecycle/cli.go b/lifecycle/cli.go new file mode 100644 index 000000000..9681e16ac --- /dev/null +++ b/lifecycle/cli.go @@ -0,0 +1,130 @@ +package lifecycle + +import ( + "errors" + "github.com/jfrog/jfrog-cli-core/v2/common/commands" + coreCommon "github.com/jfrog/jfrog-cli-core/v2/docs/common" + "github.com/jfrog/jfrog-cli-core/v2/lifecycle" + coreConfig "github.com/jfrog/jfrog-cli-core/v2/utils/config" + "github.com/jfrog/jfrog-cli/docs/common" + rbCreate "github.com/jfrog/jfrog-cli/docs/lifecycle/create" + rbPromote "github.com/jfrog/jfrog-cli/docs/lifecycle/promote" + "github.com/jfrog/jfrog-cli/utils/cliutils" + "github.com/jfrog/jfrog-client-go/utils" + "github.com/jfrog/jfrog-client-go/utils/errorutils" + "github.com/urfave/cli" +) + +const lcCategory = "Lifecycle" + +func GetCommands() []cli.Command { + return cliutils.GetSortedCommands(cli.CommandsByName{ + { + Name: "release-bundle-create", + Aliases: []string{"rbc"}, + Flags: cliutils.GetCommandFlags(cliutils.ReleaseBundleCreate), + Usage: rbCreate.GetDescription(), + HelpName: coreCommon.CreateUsage("release-bundle-create", rbCreate.GetDescription(), rbCreate.Usage), + UsageText: rbCreate.GetArguments(), + ArgsUsage: common.CreateEnvVars(), + BashComplete: coreCommon.CreateBashCompletionFunc(), + Category: lcCategory, + Action: func(c *cli.Context) error { + return create(c) + }, + }, + { + Name: "release-bundle-promote", + Aliases: []string{"rbp"}, + Flags: cliutils.GetCommandFlags(cliutils.ReleaseBundlePromote), + Usage: rbPromote.GetDescription(), + HelpName: coreCommon.CreateUsage("release-bundle-promote", rbPromote.GetDescription(), rbPromote.Usage), + UsageText: rbPromote.GetArguments(), + ArgsUsage: common.CreateEnvVars(), + BashComplete: coreCommon.CreateBashCompletionFunc(), + Category: lcCategory, + Action: func(c *cli.Context) error { + return promote(c) + }, + }, + }) +} + +func validateCreateReleaseBundleContext(c *cli.Context) error { + if show, err := cliutils.ShowCmdHelpIfNeeded(c, c.Args()); show || err != nil { + return err + } + + if c.NArg() != 3 { + return cliutils.WrongNumberOfArgumentsHandler(c) + } + + buildsSourceProvided := c.String(cliutils.Builds) != "" + releaseBundlesSourceProvided := c.String(cliutils.ReleaseBundles) != "" + if (buildsSourceProvided && releaseBundlesSourceProvided) || + !(buildsSourceProvided || releaseBundlesSourceProvided) { + return errorutils.CheckErrorf("exactly one of the following options must be supplied: --%s or --%s", cliutils.Builds, cliutils.ReleaseBundles) + } + return nil +} + +func create(c *cli.Context) (err error) { + if err = validateCreateReleaseBundleContext(c); err != nil { + return err + } + + lcDetails, err := createLifecycleDetailsByFlags(c) + if err != nil { + return + } + + createCmd := lifecycle.NewReleaseBundleCreate().SetServerDetails(lcDetails).SetReleaseBundleName(c.Args().Get(0)). + SetReleaseBundleVersion(c.Args().Get(1)).SetSigningKeyName(c.Args().Get(2)).SetAsync(c.BoolT(cliutils.Async)). + SetReleaseBundleProject(cliutils.GetProject(c)).SetBuildsSpecPath(c.String(cliutils.Builds)). + SetReleaseBundlesSpecPath(c.String(cliutils.ReleaseBundles)) + return commands.Exec(createCmd) +} + +func promote(c *cli.Context) (err error) { + if show, err := cliutils.ShowCmdHelpIfNeeded(c, c.Args()); show || err != nil { + return err + } + + if c.NArg() != 3 { + return cliutils.WrongNumberOfArgumentsHandler(c) + } + + env := c.String(cliutils.Environment) + if env == "" { + return errorutils.CheckErrorf("the --%s option is mandatory", cliutils.Environment) + } + + lcDetails, err := createLifecycleDetailsByFlags(c) + if err != nil { + return + } + + createCmd := lifecycle.NewReleaseBundlePromote().SetServerDetails(lcDetails).SetReleaseBundleName(c.Args().Get(0)). + SetReleaseBundleVersion(c.Args().Get(1)).SetSigningKeyName(c.Args().Get(2)).SetAsync(c.BoolT(cliutils.Async)). + SetReleaseBundleProject(cliutils.GetProject(c)).SetEnvironment(env). + SetOverwrite(c.Bool(cliutils.Overwrite)) + return commands.Exec(createCmd) +} + +func createLifecycleDetailsByFlags(c *cli.Context) (*coreConfig.ServerDetails, error) { + lcDetails, err := cliutils.CreateServerDetailsWithConfigOffer(c, false, cliutils.Platform) + if err != nil { + return nil, err + } + if lcDetails.Url == "" { + return nil, errors.New("platform URL is mandatory for lifecycle commands") + } + PlatformToLifecycleUrls(lcDetails) + return lcDetails, nil +} + +func PlatformToLifecycleUrls(lcDetails *coreConfig.ServerDetails) { + lcDetails.ArtifactoryUrl = utils.AddTrailingSlashIfNeeded(lcDetails.Url) + "artifactory/" + lcDetails.LifecycleUrl = utils.AddTrailingSlashIfNeeded(lcDetails.Url) + "lifecycle/" + lcDetails.Url = "" +} diff --git a/lifecycle/cli_test.go b/lifecycle/cli_test.go new file mode 100644 index 000000000..e8f41ace4 --- /dev/null +++ b/lifecycle/cli_test.go @@ -0,0 +1,38 @@ +package lifecycle + +import ( + "github.com/jfrog/jfrog-cli/utils/cliutils" + "github.com/jfrog/jfrog-cli/utils/tests" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestValidateCreateReleaseBundleContext(t *testing.T) { + testRuns := []struct { + name string + args []string + flags []string + expectError bool + }{ + {"withoutArgs", []string{}, []string{}, true}, + {"oneArg", []string{"one"}, []string{}, true}, + {"twoArgs", []string{"one", "two"}, []string{}, true}, + {"extraArgs", []string{"one", "two", "three", "four"}, []string{}, true}, + {"bothSources", []string{"one", "two", "three"}, []string{cliutils.Builds + "=/path/to/file", cliutils.ReleaseBundles + "=/path/to/file"}, true}, + {"noSources", []string{"one", "two", "three"}, []string{}, true}, + {"builds", []string{"one", "two", "three"}, []string{cliutils.Builds + "=/path/to/file"}, false}, + {"releaseBundles", []string{"one", "two", "three"}, []string{cliutils.ReleaseBundles + "=/path/to/file"}, false}, + } + + for _, test := range testRuns { + t.Run(test.name, func(t *testing.T) { + context, buffer := tests.CreateContext(t, test.flags, test.args) + err := validateCreateReleaseBundleContext(context) + if test.expectError { + assert.Error(t, err, buffer) + } else { + assert.NoError(t, err, buffer) + } + }) + } +} diff --git a/lifecycle_test.go b/lifecycle_test.go new file mode 100644 index 000000000..49bb4834c --- /dev/null +++ b/lifecycle_test.go @@ -0,0 +1,257 @@ +package main + +import ( + "encoding/json" + "fmt" + "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils" + configUtils "github.com/jfrog/jfrog-cli-core/v2/utils/config" + "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" + "github.com/jfrog/jfrog-cli/inttestutils" + lifecycleCli "github.com/jfrog/jfrog-cli/lifecycle" + "github.com/jfrog/jfrog-cli/utils/cliutils" + "github.com/jfrog/jfrog-cli/utils/tests" + "github.com/jfrog/jfrog-client-go/http/httpclient" + "github.com/jfrog/jfrog-client-go/lifecycle" + "github.com/jfrog/jfrog-client-go/lifecycle/services" + clientUtils "github.com/jfrog/jfrog-client-go/utils" + "github.com/jfrog/jfrog-client-go/utils/errorutils" + "github.com/stretchr/testify/assert" + "net/http" + "os" + "path/filepath" + "strconv" + "testing" +) + +const ( + rbMinVersion = "7.45.0" + gpgKeyPairName = "lc-tests-key-pair" + lcTestdataPath = "lifecycle" + releaseBundlesSpec = "release-bundles-spec.json" + buildsSpec12 = "builds-spec-1-2.json" + buildsSpec3 = "builds-spec-3.json" + number1, number2, number3 = "111", "222", "333" +) + +var ( + lcDetails *configUtils.ServerDetails + lcCli *tests.JfrogCli +) + +func TestLifecycle(t *testing.T) { + initLifecycleTest(t) + defer cleanLifecycleTests(t) + lcManager := getLcServiceManager(t) + + // Upload builds to create release bundles from. + deleteBuilds := uploadBuilds(t) + defer deleteBuilds() + + // Create release bundles from builds synchronously. + createRb(t, buildsSpec12, cliutils.Builds, tests.LcRbName1, number1, false) + defer deleteReleaseBundle(t, lcManager, tests.LcRbName1, number1) + + // Create release bundles from builds asynchronously and assert status. + createRb(t, buildsSpec3, cliutils.Builds, tests.LcRbName2, number2, true) + defer deleteReleaseBundle(t, lcManager, tests.LcRbName2, number2) + assertStatusCompleted(t, lcManager, tests.LcRbName2, number2, "") + + // Create a combined release bundle from the two previous release bundle. + createRb(t, releaseBundlesSpec, cliutils.ReleaseBundles, tests.LcRbName3, number3, false) + defer deleteReleaseBundle(t, lcManager, tests.LcRbName3, number3) + + // Promote the last release bundle. + promoteRb(t, lcManager, number3) + + // Verify the artifacts of both the initial release bundles made it to the prod repo. + searchSpec, err := tests.CreateSpec(tests.SearchAllProdRepo) + assert.NoError(t, err) + inttestutils.VerifyExistInArtifactory(tests.GetExpectedLifecycleArtifacts(), searchSpec, serverDetails, t) +} + +func uploadBuilds(t *testing.T) func() { + uploadBuild(t, tests.UploadDevSpecA, tests.LcBuildName1, number1) + uploadBuild(t, tests.UploadDevSpecB, tests.LcBuildName2, number2) + uploadBuild(t, tests.UploadDevSpecC, tests.LcBuildName3, number3) + return func() { + inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, tests.LcBuildName1, artHttpDetails) + inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, tests.LcBuildName2, artHttpDetails) + inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, tests.LcBuildName3, artHttpDetails) + } +} + +func createRb(t *testing.T, specName, sourceOption, buildName, buildNumber string, async bool) { + specFile, err := getSpecFile(specName) + assert.NoError(t, err) + assert.NoError(t, lcCli.Exec("rbc", buildName, buildNumber, gpgKeyPairName, "--"+sourceOption+"="+specFile, "--async="+strconv.FormatBool(async))) +} + +func promoteRb(t *testing.T, lcManager *lifecycle.LifecycleServicesManager, rbVersion string) { + output := lcCli.RunCliCmdWithOutput(t, "rbp", tests.LcRbName3, rbVersion, gpgKeyPairName, "--env=PROD", "--overwrite=true", "--async=true", "--project=default") + var promotionResp services.RbPromotionResp + if !assert.NoError(t, json.Unmarshal([]byte(output), &promotionResp)) { + return + } + assertStatusCompleted(t, lcManager, tests.LcRbName3, rbVersion, promotionResp.CreatedMillis.String()) +} + +func getSpecFile(fileName string) (string, error) { + source := filepath.Join(tests.GetTestResourcesPath(), lcTestdataPath, fileName) + return tests.ReplaceTemplateVariables(source, "") +} + +// If createdMillis is provided, assert status for promotion. If blank, assert for creation. +func assertStatusCompleted(t *testing.T, lcManager *lifecycle.LifecycleServicesManager, rbName, rbVersion, createdMillis string) { + resp, err := getStatus(lcManager, rbName, rbVersion, createdMillis) + if !assert.NoError(t, err) { + return + } + assert.Equal(t, services.Completed, resp.Status) +} + +func getLcServiceManager(t *testing.T) *lifecycle.LifecycleServicesManager { + lcManager, err := utils.CreateLifecycleServiceManager(lcDetails, false) + assert.NoError(t, err) + return lcManager +} + +func authenticateLifecycle() string { + *tests.JfrogUrl = clientUtils.AddTrailingSlashIfNeeded(*tests.JfrogUrl) + lcDetails = &configUtils.ServerDetails{ + Url: *tests.JfrogUrl} + lifecycleCli.PlatformToLifecycleUrls(lcDetails) + + cred := fmt.Sprintf("--url=%s", *tests.JfrogUrl) + if *tests.JfrogAccessToken != "" { + lcDetails.AccessToken = *tests.JfrogAccessToken + cred += fmt.Sprintf(" --access-token=%s", lcDetails.AccessToken) + } else { + lcDetails.User = *tests.JfrogUser + lcDetails.Password = *tests.JfrogPassword + cred += fmt.Sprintf(" --user=%s --password=%s", lcDetails.User, lcDetails.Password) + } + return cred +} + +func getStatus(lcManager *lifecycle.LifecycleServicesManager, rbName, rbVersion, createdMillis string) (services.ReleaseBundleStatusResponse, error) { + rbDetails := services.ReleaseBundleDetails{ + ReleaseBundleName: rbName, + ReleaseBundleVersion: rbVersion, + } + + if createdMillis == "" { + return lcManager.GetReleaseBundleCreateStatus(rbDetails, "", true) + } + return lcManager.GetReleaseBundlePromotionStatus(rbDetails, "", createdMillis, true) +} + +func deleteReleaseBundle(t *testing.T, lcManager *lifecycle.LifecycleServicesManager, rbName, rbVersion string) { + rbDetails := services.ReleaseBundleDetails{ + ReleaseBundleName: rbName, + ReleaseBundleVersion: rbVersion, + } + + assert.NoError(t, lcManager.DeleteReleaseBundle(rbDetails, services.ReleaseBundleQueryParams{Async: false})) +} + +func uploadBuild(t *testing.T, specFileName, buildName, buildNumber string) { + specFile, err := tests.CreateSpec(specFileName) + assert.NoError(t, err) + runRt(t, "upload", "--spec="+specFile, "--build-name="+buildName, "--build-number="+buildNumber) + runRt(t, "build-publish", buildName, buildNumber) +} + +func initLifecycleTest(t *testing.T) { + if !*tests.TestLifecycle { + t.Skip("Skipping lifecycle test. To run release bundle test add the '-test.lc=true' option.") + } + validateArtifactoryVersion(t, rbMinVersion) + + if !isLifecycleSupported(t) { + t.Skip("Skipping lifecycle test because the functionality is not enabled on the provided JPD.") + } +} + +func isLifecycleSupported(t *testing.T) (skip bool) { + client, err := httpclient.ClientBuilder().Build() + assert.NoError(t, err) + + resp, _, _, err := client.SendGet(serverDetails.ArtifactoryUrl+"api/release_bundles/records/non-existing-rb", true, artHttpDetails, "") + if !assert.NoError(t, err) { + return + } + return resp.StatusCode != http.StatusNotImplemented +} + +func InitLifecycleTests() { + initArtifactoryCli() + initLifecycleCli() + cleanUpOldBuilds() + cleanUpOldRepositories() + cleanUpOldUsers() + tests.AddTimestampToGlobalVars() + createRequiredRepos() + sendGpgKeyPair() +} + +func initLifecycleCli() { + if lcCli != nil { + return + } + lcCli = tests.NewJfrogCli(execMain, "jfrog", authenticateLifecycle()) +} + +func CleanLifecycleTests() { + deleteCreatedRepos() +} + +func cleanLifecycleTests(t *testing.T) { + deleteFilesFromRepo(t, tests.RtDevRepo) + deleteFilesFromRepo(t, tests.RtProdRepo) + tests.CleanFileSystem() +} + +func sendGpgKeyPair() { + // Create http client + client, err := httpclient.ClientBuilder().Build() + coreutils.ExitOnErr(err) + + // Check if one already exists + resp, body, _, err := client.SendGet(*tests.JfrogUrl+"artifactory/api/security/keypair/"+gpgKeyPairName, true, artHttpDetails, "") + coreutils.ExitOnErr(err) + if resp.StatusCode == http.StatusOK { + return + } + coreutils.ExitOnErr(errorutils.CheckResponseStatusWithBody(resp, body, http.StatusNotFound)) + + // Read gpg public and private keys + keysDir := filepath.Join(tests.GetTestResourcesPath(), lcTestdataPath, "keys") + publicKey, err := os.ReadFile(filepath.Join(keysDir, "public.txt")) + coreutils.ExitOnErr(err) + privateKey, err := os.ReadFile(filepath.Join(keysDir, "private.txt")) + coreutils.ExitOnErr(err) + + // Send keys to Artifactory + payload := KeyPairPayload{ + PairName: gpgKeyPairName, + PairType: "GPG", + Alias: gpgKeyPairName + "-alias", + Passphrase: "password", + PublicKey: string(publicKey), + PrivateKey: string(privateKey), + } + content, err := json.Marshal(payload) + coreutils.ExitOnErr(err) + resp, body, err = client.SendPost(*tests.JfrogUrl+"artifactory/api/security/keypair", content, artHttpDetails, "") + coreutils.ExitOnErr(err) + coreutils.ExitOnErr(errorutils.CheckResponseStatusWithBody(resp, body, http.StatusCreated)) +} + +type KeyPairPayload struct { + PairName string `json:"pairName,omitempty"` + PairType string `json:"pairType,omitempty"` + Alias string `json:"alias,omitempty"` + Passphrase string `json:"passphrase,omitempty"` + PublicKey string `json:"publicKey,omitempty"` + PrivateKey string `json:"privateKey,omitempty"` +} diff --git a/main.go b/main.go index d7d904495..42c3c804c 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "github.com/jfrog/jfrog-cli/lifecycle" "golang.org/x/exp/slices" "os" "runtime" @@ -269,6 +270,7 @@ func getCommands() []cli.Command { allCommands := append(slices.Clone(cliNameSpaces), utils.GetPlugins()...) allCommands = append(allCommands, scan.GetCommands()...) allCommands = append(allCommands, buildtools.GetCommands()...) + allCommands = append(allCommands, lifecycle.GetCommands()...) return append(allCommands, buildtools.GetBuildToolsHelpCommands()...) } diff --git a/main_test.go b/main_test.go index 7bab29827..bc6684073 100644 --- a/main_test.go +++ b/main_test.go @@ -4,34 +4,30 @@ import ( "errors" "flag" "fmt" - "os" - "path/filepath" - "strconv" - "strings" - "testing" - - "github.com/jfrog/jfrog-cli-core/v2/common/spec" - "github.com/jfrog/jfrog-cli-core/v2/utils/config" - coreTests "github.com/jfrog/jfrog-cli-core/v2/utils/tests" - xrayutils "github.com/jfrog/jfrog-cli-core/v2/xray/utils" - "github.com/urfave/cli" - buildinfo "github.com/jfrog/build-info-go/entities" - clientTestUtils "github.com/jfrog/jfrog-client-go/utils/tests" - + commandUtils "github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/utils" + artifactoryUtils "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils" "github.com/jfrog/jfrog-cli-core/v2/common/commands" + "github.com/jfrog/jfrog-cli-core/v2/common/spec" + "github.com/jfrog/jfrog-cli-core/v2/utils/config" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" "github.com/jfrog/jfrog-cli-core/v2/utils/log" - clientlog "github.com/jfrog/jfrog-client-go/utils/log" - - commandUtils "github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/utils" - artifactoryUtils "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils" + coreTests "github.com/jfrog/jfrog-cli-core/v2/utils/tests" + xrayutils "github.com/jfrog/jfrog-cli-core/v2/xray/utils" "github.com/jfrog/jfrog-cli/artifactory" "github.com/jfrog/jfrog-cli/inttestutils" "github.com/jfrog/jfrog-cli/utils/tests" "github.com/jfrog/jfrog-client-go/utils" + clientlog "github.com/jfrog/jfrog-client-go/utils/log" + clientTestUtils "github.com/jfrog/jfrog-client-go/utils/tests" "github.com/stretchr/testify/assert" + "github.com/urfave/cli" "gopkg.in/yaml.v2" + "os" + "path/filepath" + "strconv" + "strings" + "testing" ) func TestMain(m *testing.M) { @@ -80,6 +76,9 @@ func setupIntegrationTests() { if *tests.TestTransfer { InitTransferTests() } + if *tests.TestLifecycle { + InitLifecycleTests() + } } func tearDownIntegrationTests() { @@ -98,6 +97,9 @@ func tearDownIntegrationTests() { if *tests.TestTransfer { CleanTransferTests() } + if *tests.TestLifecycle { + CleanLifecycleTests() + } } func InitBuildToolsTests() { @@ -180,7 +182,8 @@ func initArtifactoryCli() { } *tests.JfrogUrl = utils.AddTrailingSlashIfNeeded(*tests.JfrogUrl) artifactoryCli = tests.NewJfrogCli(execMain, "jfrog rt", authenticate(false)) - if (*tests.TestArtifactory && !*tests.TestArtifactoryProxy) || *tests.TestPlugins || *tests.TestArtifactoryProject || *tests.TestAccess || *tests.TestTransfer { + if (*tests.TestArtifactory && !*tests.TestArtifactoryProxy) || *tests.TestPlugins || *tests.TestArtifactoryProject || + *tests.TestAccess || *tests.TestTransfer || *tests.TestLifecycle { configCli = createConfigJfrogCLI(authenticate(true)) platformCli = tests.NewJfrogCli(execMain, "jfrog", authenticate(false)) } @@ -331,6 +334,12 @@ func initDeploymentViewTest(t *testing.T) (assertDeploymentViewFunc func(), clea return } +func deleteFilesFromRepo(t *testing.T, repoName string) { + deleteSpec := spec.NewBuilder().Pattern(repoName).BuildSpec() + _, _, err := tests.DeleteFiles(deleteSpec, serverDetails) + assert.NoError(t, err) +} + func TestIntro(t *testing.T) { buffer, _, previousLog := coreTests.RedirectLogOutputToBuffer() defer clientlog.SetLogger(previousLog) diff --git a/maven_test.go b/maven_test.go index a41cfc498..e7de3a582 100644 --- a/maven_test.go +++ b/maven_test.go @@ -2,29 +2,25 @@ package main import ( "fmt" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/jfrog/jfrog-cli/inttestutils" - "github.com/jfrog/jfrog-cli/utils/tests/proxy/server/certificate" - "github.com/jfrog/jfrog-client-go/utils/log" - clientTestUtils "github.com/jfrog/jfrog-client-go/utils/tests" - buildinfo "github.com/jfrog/build-info-go/entities" - "github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/mvn" "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils" "github.com/jfrog/jfrog-cli-core/v2/common/commands" "github.com/jfrog/jfrog-cli-core/v2/common/spec" - "github.com/jfrog/jfrog-client-go/utils/io/fileutils" - "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" coreTests "github.com/jfrog/jfrog-cli-core/v2/utils/tests" + "github.com/jfrog/jfrog-cli/inttestutils" "github.com/jfrog/jfrog-cli/utils/tests" cliproxy "github.com/jfrog/jfrog-cli/utils/tests/proxy/server" + "github.com/jfrog/jfrog-cli/utils/tests/proxy/server/certificate" + "github.com/jfrog/jfrog-client-go/utils/io/fileutils" + "github.com/jfrog/jfrog-client-go/utils/log" + clientTestUtils "github.com/jfrog/jfrog-client-go/utils/tests" "github.com/stretchr/testify/assert" + "os" + "path/filepath" + "strings" + "testing" ) const mavenTestsProxyPort = "1028" @@ -34,12 +30,8 @@ var localRepoDir string func cleanMavenTest(t *testing.T) { clientTestUtils.UnSetEnvAndAssert(t, coreutils.HomeDir) - deleteSpec := spec.NewBuilder().Pattern(tests.MvnRepo1).BuildSpec() - _, _, err := tests.DeleteFiles(deleteSpec, serverDetails) - assert.NoError(t, err) - deleteSpec = spec.NewBuilder().Pattern(tests.MvnRepo2).BuildSpec() - _, _, err = tests.DeleteFiles(deleteSpec, serverDetails) - assert.NoError(t, err) + deleteFilesFromRepo(t, tests.MvnRepo1) + deleteFilesFromRepo(t, tests.MvnRepo2) tests.CleanFileSystem() } diff --git a/testdata/dev_repo_repository_config.json b/testdata/dev_repo_repository_config.json new file mode 100644 index 000000000..de2297cfd --- /dev/null +++ b/testdata/dev_repo_repository_config.json @@ -0,0 +1,6 @@ +{ + "key": "${DEV_REPO}", + "rclass": "local", + "packageType": "generic", + "environments":["DEV"] +} diff --git a/testdata/filespecs/search_all_prod_repo.json b/testdata/filespecs/search_all_prod_repo.json new file mode 100644 index 000000000..2930acf50 --- /dev/null +++ b/testdata/filespecs/search_all_prod_repo.json @@ -0,0 +1,7 @@ +{ + "files": [ + { + "pattern": "${PROD_REPO}/*" + } + ] +} \ No newline at end of file diff --git a/testdata/filespecs/upload_dev_spec_a.json b/testdata/filespecs/upload_dev_spec_a.json new file mode 100644 index 000000000..d816a4499 --- /dev/null +++ b/testdata/filespecs/upload_dev_spec_a.json @@ -0,0 +1,10 @@ +{ + "files": [ + { + "pattern": "testdata/a/*", + "target": "${DEV_REPO}/", + "flat": "true", + "recursive": "false" + } + ] +} \ No newline at end of file diff --git a/testdata/filespecs/upload_dev_spec_b.json b/testdata/filespecs/upload_dev_spec_b.json new file mode 100644 index 000000000..e5419e101 --- /dev/null +++ b/testdata/filespecs/upload_dev_spec_b.json @@ -0,0 +1,10 @@ +{ + "files": [ + { + "pattern": "testdata/a/b/*", + "target": "${DEV_REPO}/", + "flat": "true", + "recursive": "false" + } + ] +} \ No newline at end of file diff --git a/testdata/filespecs/upload_dev_spec_c.json b/testdata/filespecs/upload_dev_spec_c.json new file mode 100644 index 000000000..db8e8177f --- /dev/null +++ b/testdata/filespecs/upload_dev_spec_c.json @@ -0,0 +1,10 @@ +{ + "files": [ + { + "pattern": "testdata/a/b/c/*", + "target": "${DEV_REPO}/", + "flat": "true", + "recursive": "false" + } + ] +} \ No newline at end of file diff --git a/testdata/lifecycle/builds-spec-1-2.json b/testdata/lifecycle/builds-spec-1-2.json new file mode 100644 index 000000000..1d31239ca --- /dev/null +++ b/testdata/lifecycle/builds-spec-1-2.json @@ -0,0 +1,13 @@ +{ + "builds": [ + { + "name": "${LC_BUILD_NAME1}", + "number": "111" + }, + { + "name": "${LC_BUILD_NAME2}", + "number": "", + "project": "default" + } + ] +} diff --git a/testdata/lifecycle/builds-spec-3.json b/testdata/lifecycle/builds-spec-3.json new file mode 100644 index 000000000..b188d7eb6 --- /dev/null +++ b/testdata/lifecycle/builds-spec-3.json @@ -0,0 +1,8 @@ +{ + "builds": [ + { + "name": "${LC_BUILD_NAME3}", + "number": "" + } + ] +} diff --git a/testdata/lifecycle/keys/private.txt b/testdata/lifecycle/keys/private.txt new file mode 100644 index 000000000..302ccf64a --- /dev/null +++ b/testdata/lifecycle/keys/private.txt @@ -0,0 +1,83 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +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 +=lBLn +-----END PGP PRIVATE KEY BLOCK----- diff --git a/testdata/lifecycle/keys/public.txt b/testdata/lifecycle/keys/public.txt new file mode 100644 index 000000000..00e214e35 --- /dev/null +++ b/testdata/lifecycle/keys/public.txt @@ -0,0 +1,41 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +m0maxaPoW55LRqmm +=S8pT +-----END PGP PUBLIC KEY BLOCK----- diff --git a/testdata/lifecycle/release-bundles-spec.json b/testdata/lifecycle/release-bundles-spec.json new file mode 100644 index 000000000..36fac6eea --- /dev/null +++ b/testdata/lifecycle/release-bundles-spec.json @@ -0,0 +1,12 @@ +{ + "releaseBundles": [ + { + "name": "${RB_NAME1}", + "version": "111" + }, + { + "name": "${RB_NAME2}", + "version": "222" + } + ] +} diff --git a/testdata/prod_repo_repository_config.json b/testdata/prod_repo_repository_config.json new file mode 100644 index 000000000..6187c443e --- /dev/null +++ b/testdata/prod_repo_repository_config.json @@ -0,0 +1,6 @@ +{ + "key": "${PROD_REPO}", + "rclass": "local", + "packageType": "generic", + "environments":["PROD"] +} diff --git a/utils/cliutils/commandsflags.go b/utils/cliutils/commandsflags.go index 68d9a2a7e..fda4bdca1 100644 --- a/utils/cliutils/commandsflags.go +++ b/utils/cliutils/commandsflags.go @@ -88,11 +88,11 @@ const ( passphrase = "passphrase" // Distribution's Command Keys - ReleaseBundleCreate = "release-bundle-create" - ReleaseBundleUpdate = "release-bundle-update" - ReleaseBundleSign = "release-bundle-sign" - ReleaseBundleDistribute = "release-bundle-distribute" - ReleaseBundleDelete = "release-bundle-delete" + ReleaseBundleV1Create = "release-bundle-v1-create" + ReleaseBundleV1Update = "release-bundle-v1-update" + ReleaseBundleV1Sign = "release-bundle-v1-sign" + ReleaseBundleV1Distribute = "release-bundle-v1-distribute" + ReleaseBundleV1Delete = "release-bundle-v1-delete" // MC's Commands Keys McConfig = "mc-config" @@ -130,6 +130,10 @@ const ( // TransferInstall commands keys TransferInstall = "transfer-plugin-install" + // Lifecycle commands keys + ReleaseBundleCreate = "release-bundle-create" + ReleaseBundlePromote = "release-bundle-promote" + // *** Artifactory Commands' flags *** // Base flags url = "url" @@ -299,11 +303,11 @@ const ( copyFlag = "copy" failFast = "fail-fast" - async = "async" + Async = "async" // Unique build-discard flags buildDiscardPrefix = "bdi-" - bdiAsync = buildDiscardPrefix + async + bdiAsync = buildDiscardPrefix + Async maxDays = "max-days" maxBuilds = "max-builds" excludeBuilds = "exclude-builds" @@ -406,24 +410,24 @@ const ( distUrl = "dist-url" // Unique release-bundle-* flags - releaseBundlePrefix = "rb-" - rbDryRun = releaseBundlePrefix + dryRun - rbRepo = releaseBundlePrefix + repo - rbPassphrase = releaseBundlePrefix + passphrase - distTarget = releaseBundlePrefix + target - rbDetailedSummary = releaseBundlePrefix + detailedSummary - sign = "sign" - desc = "desc" - releaseNotesPath = "release-notes-path" - releaseNotesSyntax = "release-notes-syntax" - distRules = "dist-rules" - site = "site" - city = "city" - countryCodes = "country-codes" - sync = "sync" - maxWaitMinutes = "max-wait-minutes" - deleteFromDist = "delete-from-dist" - createRepo = "create-repo" + releaseBundleV1Prefix = "rbv1-" + rbDryRun = releaseBundleV1Prefix + dryRun + rbRepo = releaseBundleV1Prefix + repo + rbPassphrase = releaseBundleV1Prefix + passphrase + distTarget = releaseBundleV1Prefix + target + rbDetailedSummary = releaseBundleV1Prefix + detailedSummary + sign = "sign" + desc = "desc" + releaseNotesPath = "release-notes-path" + releaseNotesSyntax = "release-notes-syntax" + distRules = "dist-rules" + site = "site" + city = "city" + countryCodes = "country-codes" + sync = "sync" + maxWaitMinutes = "max-wait-minutes" + deleteFromDist = "delete-from-dist" + createRepo = "create-repo" // *** Xray Commands' flags *** // Base flags @@ -532,6 +536,19 @@ const ( installPluginVersion = installPluginPrefix + Version InstallPluginSrcDir = "dir" InstallPluginHomeDir = "home-dir" + + // Unique lifecycle flags + lifecyclePrefix = "lc-" + lcUrl = lifecyclePrefix + url + lcAsync = lifecyclePrefix + Async + lcProject = lifecyclePrefix + project + Builds = "builds" + lcBuilds = lifecyclePrefix + Builds + ReleaseBundles = "release-bundles" + lcReleaseBundles = lifecyclePrefix + ReleaseBundles + Environment = "env" + lcEnvironment = lifecyclePrefix + Environment + lcOverwrite = lifecyclePrefix + Overwrite ) var flagsMap = map[string]cli.Flag{ @@ -987,7 +1004,7 @@ var flagsMap = map[string]cli.Flag{ Usage: "[Default: false] If set to true, automatically removes build artifacts stored in Artifactory.` `", }, bdiAsync: cli.BoolFlag{ - Name: async, + Name: Async, Usage: "[Default: false] If set to true, build discard will run asynchronously and will not wait for response.` `", }, refs: cli.StringFlag{ @@ -1556,6 +1573,34 @@ var flagsMap = map[string]cli.Flag{ Name: PreChecks, Usage: "[Default: false] Set to true to run pre transfer checks.` `", }, + lcUrl: cli.StringFlag{ + Name: url, + Usage: "[Optional] JFrog platform URL.` `", + }, + lcAsync: cli.BoolTFlag{ + Name: Async, + Usage: "[Default: true] Set to false to run synchronously and wait for response.` `", + }, + lcProject: cli.StringFlag{ + Name: project, + Usage: "[Optional] Project key associated with the Release Bundle version.` `", + }, + lcBuilds: cli.StringFlag{ + Name: Builds, + Usage: "[Optional] Path to a JSON file containing information of the source builds from which to create a release bundle.` `", + }, + lcReleaseBundles: cli.StringFlag{ + Name: ReleaseBundles, + Usage: "[Optional] Path to a JSON file containing information of the source release bundles from which to create a release bundle.` `", + }, + lcEnvironment: cli.StringFlag{ + Name: Environment, + Usage: "[Mandatory] Name of the target environment for the promotion.` `", + }, + lcOverwrite: cli.BoolFlag{ + Name: Overwrite, + Usage: "[Default: false] Set to true to replace artifacts with the same name but a different checksum if such already exist at the promotion targets. By default, the promotion is stopped in a case of such conflict.` `", + }, } var commandFlags = map[string][]string{ @@ -1766,23 +1811,23 @@ var commandFlags = map[string][]string{ Poetry: { buildName, buildNumber, module, project, }, - ReleaseBundleCreate: { + ReleaseBundleV1Create: { distUrl, user, password, accessToken, serverId, specFlag, specVars, targetProps, rbDryRun, sign, desc, exclusions, releaseNotesPath, releaseNotesSyntax, rbPassphrase, rbRepo, InsecureTls, distTarget, rbDetailedSummary, }, - ReleaseBundleUpdate: { + ReleaseBundleV1Update: { distUrl, user, password, accessToken, serverId, specFlag, specVars, targetProps, rbDryRun, sign, desc, exclusions, releaseNotesPath, releaseNotesSyntax, rbPassphrase, rbRepo, InsecureTls, distTarget, rbDetailedSummary, }, - ReleaseBundleSign: { + ReleaseBundleV1Sign: { distUrl, user, password, accessToken, serverId, rbPassphrase, rbRepo, InsecureTls, rbDetailedSummary, }, - ReleaseBundleDistribute: { + ReleaseBundleV1Distribute: { distUrl, user, password, accessToken, serverId, rbDryRun, distRules, site, city, countryCodes, sync, maxWaitMinutes, InsecureTls, createRepo, }, - ReleaseBundleDelete: { + ReleaseBundleV1Delete: { distUrl, user, password, accessToken, serverId, rbDryRun, distRules, site, city, countryCodes, sync, maxWaitMinutes, InsecureTls, deleteFromDist, deleteQuiet, }, @@ -1834,6 +1879,12 @@ var commandFlags = map[string][]string{ TransferInstall: { installPluginVersion, InstallPluginSrcDir, InstallPluginHomeDir, }, + ReleaseBundleCreate: { + lcUrl, user, password, accessToken, serverId, lcAsync, lcProject, lcBuilds, lcReleaseBundles, + }, + ReleaseBundlePromote: { + lcUrl, user, password, accessToken, serverId, lcAsync, lcProject, lcEnvironment, lcOverwrite, + }, // Xray's commands OfflineUpdate: { licenseId, from, to, Version, target, Stream, Periodic, diff --git a/utils/cliutils/utils.go b/utils/cliutils/utils.go index 887af601b..ffcfe7558 100644 --- a/utils/cliutils/utils.go +++ b/utils/cliutils/utils.go @@ -33,9 +33,10 @@ import ( type CommandDomain string const ( - Rt CommandDomain = "rt" - Ds CommandDomain = "ds" - Xr CommandDomain = "xr" + Rt CommandDomain = "rt" + Ds CommandDomain = "ds" + Xr CommandDomain = "xr" + Platform CommandDomain = "platform" ) // Error modes (how should the application behave when the CheckError function is invoked): @@ -530,6 +531,8 @@ func createServerDetailsFromFlags(c *cli.Context, domain CommandDomain) (details details.XrayUrl = details.Url case Ds: details.DistributionUrl = details.Url + case Platform: + return } details.Url = "" @@ -853,3 +856,12 @@ func doHttpRequest(client *http.Client, req *http.Request) (resp *http.Response, body, err = io.ReadAll(resp.Body) return resp, body, errorutils.CheckError(err) } + +// Get project key from flag or environment variable +func GetProject(c *cli.Context) string { + projectKey := c.String("project") + if projectKey == "" { + projectKey = os.Getenv(coreutils.Project) + } + return projectKey +} diff --git a/utils/tests/consts.go b/utils/tests/consts.go index 23c5202c9..a4c52191c 100644 --- a/utils/tests/consts.go +++ b/utils/tests/consts.go @@ -1,13 +1,11 @@ package tests import ( - "path/filepath" - - "github.com/jfrog/jfrog-client-go/artifactory/services" - "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils" - servicesutils "github.com/jfrog/jfrog-client-go/artifactory/services/utils" - clientutils "github.com/jfrog/jfrog-client-go/utils" + "github.com/jfrog/jfrog-client-go/artifactory/services" + servicesUtils "github.com/jfrog/jfrog-client-go/artifactory/services/utils" + clientUtils "github.com/jfrog/jfrog-client-go/utils" + "path/filepath" ) const ( @@ -93,6 +91,7 @@ const ( SearchAllMaven = "search_all_maven.json" SearchAllNpm = "search_all_npm.json" SearchAllRepo1 = "search_all_repo1.json" + SearchAllProdRepo = "search_all_prod_repo.json" SearchDistRepoByInSuffix = "search_dist_repo_by_in_suffix.json" SearchRepo1ByInSuffix = "search_repo1_by_in_suffix.json" SearchRepo1IncludeDirs = "search_repo1_include_dirs.json" @@ -148,6 +147,11 @@ const ( DockerRemoteRepositoryConfig = "docker_remote_repository_config.json" DockerVirtualRepositoryConfig = "docker_virtual_repository_config.json" XrayEndpoint = "xray/" + DevRepoRepositoryConfig = "dev_repo_repository_config.json" + ProdRepoRepositoryConfig = "prod_repo_repository_config.json" + UploadDevSpecA = "upload_dev_spec_a.json" + UploadDevSpecB = "upload_dev_spec_b.json" + UploadDevSpecC = "upload_dev_spec_c.json" ) var ( @@ -181,6 +185,9 @@ var ( RtRepo1 = "cli-rt1" RtRepo2 = "cli-rt2" RtVirtualRepo = "cli-rt-virtual" + // Repositories that are assigned to an environment. + RtDevRepo = "cli-rt-dev" + RtProdRepo = "cli-rt-prod" // These are not actual repositories. These patterns are meant to be used in both Repo1 and Repo2. RtRepo1And2 = "cli-rt*" RtRepo1And2Placeholder = "cli-rt(*)" @@ -202,6 +209,12 @@ var ( RtBuildName2 = "cli-rt-build2" RtBuildNameWithSpecialChars = "cli-rt-a$+~&^a#-build3" RtPermissionTargetName = "cli-rt-pt" + LcBuildName1 = "cli-lc-build1" + LcBuildName2 = "cli-lc-build2" + LcBuildName3 = "cli-lc-build3" + LcRbName1 = "cli-lc-rb1" + LcRbName2 = "cli-lc-rb2" + LcRbName3 = "cli-lc-rb3" // Users UserName1 = "alice" @@ -249,16 +262,6 @@ func GetExpectedExcludeUploadPart2() []string { RtRepo1 + "/", } } -func GetExpectedExcludeUpload2() []string { - return []string{ - RtRepo1 + "/b3.in", - RtRepo1 + "/a2.in", - RtRepo1 + "/a3.in", - RtRepo1 + "/a1.in", - RtRepo1 + "/c", - RtRepo1 + "/", - } -} func GetExpectedExcludeUploadIncludeDir() []string { return []string{ RtRepo1 + "/a2.in", @@ -269,14 +272,6 @@ func GetExpectedExcludeUploadIncludeDir() []string { } } -func GetUploadLegacyPropsExpected() []string { - return []string{ - RtRepo1 + "/data/a1.in", - RtRepo1 + "/data/a2.in", - RtRepo1 + "/data/a3.in", - } -} - func GetSearchAppendedBuildNoPatternExpected() []string { return []string{ RtRepo1 + "/data/a1.in", @@ -1739,12 +1734,6 @@ func GetSearchResultAfterDeleteByPropsStep3() []utils.SearchResult { } } -func GetDockerSourceManifest() []string { - return []string{ - DockerLocalRepo + "/" + DockerImageName + "/1/manifest.json", - } -} - func GetDockerDeployedManifest() []string { return []string{ DockerLocalPromoteRepo + "/" + DockerImageName + "promotion" + "/2/manifest.json", @@ -1972,17 +1961,17 @@ func GetFileWithDownloadedPlaceHolderSlashSuffix() []string { } } -func GetExpectedUploadSummaryDetails(rtUrl string) []clientutils.FileTransferDetails { +func GetExpectedUploadSummaryDetails(rtUrl string) []clientUtils.FileTransferDetails { path1, path2, path3 := filepath.Join("testdata", "a", "a1.in"), filepath.Join("testdata", "a", "a2.in"), filepath.Join("testdata", "a", "a3.in") - return []clientutils.FileTransferDetails{ + return []clientUtils.FileTransferDetails{ {SourcePath: path1, RtUrl: rtUrl, TargetPath: RtRepo1 + "/testdata/a/a1.in", Sha256: "4eb341b5d2762a853d79cc25e622aa8b978eb6e12c3259e2d99dc9dc60d82c5d"}, {SourcePath: path2, RtUrl: rtUrl, TargetPath: RtRepo1 + "/testdata/a/a2.in", Sha256: "3e3deb6628658a48cf0d280a2210211f9d977ec2e10a4619b95d5fb85cb10450"}, {SourcePath: path3, RtUrl: rtUrl, TargetPath: RtRepo1 + "/testdata/a/a3.in", Sha256: "14e3dc4749bf42df13a67a271065b0f334d0ad36bb34a74cc57c6e137f9af09e"}, } } -func GetReplicationConfig() []servicesutils.ReplicationParams { - return []servicesutils.ReplicationParams{ +func GetReplicationConfig() []servicesUtils.ReplicationParams { + return []servicesUtils.ReplicationParams{ { Url: *JfrogUrl + ArtifactoryEndpoint + "targetRepo", Username: "admin", @@ -2075,3 +2064,17 @@ func GetTransferExpectedRepoSnapshot() []string { RtRepo1 + "/testdata/a/b/b3.in", } } + +func GetExpectedLifecycleArtifacts() []string { + return []string{ + RtProdRepo + "/a1.in", + RtProdRepo + "/a2.in", + RtProdRepo + "/a3.in", + RtProdRepo + "/b1.in", + RtProdRepo + "/b2.in", + RtProdRepo + "/b3.in", + RtProdRepo + "/c1.in", + RtProdRepo + "/c2.in", + RtProdRepo + "/c3.in", + } +} diff --git a/utils/tests/utils.go b/utils/tests/utils.go index 5f245f63e..bfd9d0a6d 100644 --- a/utils/tests/utils.go +++ b/utils/tests/utils.go @@ -7,6 +7,7 @@ import ( "errors" "flag" "fmt" + "github.com/urfave/cli" "io" "math/rand" "os" @@ -68,6 +69,7 @@ var ( TestXray *bool TestAccess *bool TestTransfer *bool + TestLifecycle *bool HideUnitTestLog *bool ciRunId *string InstallDataTransferPlugin *bool @@ -103,6 +105,7 @@ func init() { TestXray = flag.Bool("test.xray", false, "Test Xray") TestAccess = flag.Bool("test.access", false, "Test Access") TestTransfer = flag.Bool("test.transfer", false, "Test files transfer") + TestLifecycle = flag.Bool("test.lc", false, "Test lifecycle") ContainerRegistry = flag.String("test.containerRegistry", "localhost:8082", "Container registry") HideUnitTestLog = flag.Bool("test.hideUnitTestLog", false, "Hide unit tests logs and print it in a file") InstallDataTransferPlugin = flag.Bool("test.installDataTransferPlugin", false, "Install data-transfer plugin on the source Artifactory server") @@ -349,6 +352,8 @@ var reposConfigMap = map[*string]string{ &DockerLocalPromoteRepo: DockerLocalPromoteRepositoryConfig, &DockerRemoteRepo: DockerRemoteRepositoryConfig, &DockerVirtualRepo: DockerVirtualRepositoryConfig, + &RtDevRepo: DevRepoRepositoryConfig, + &RtProdRepo: ProdRepoRepositoryConfig, } var CreatedNonVirtualRepositories map[*string]string @@ -399,6 +404,7 @@ func GetNonVirtualRepositories() map[*string]string { TestXray: {}, TestAccess: {&RtRepo1}, TestTransfer: {&RtRepo1, &RtRepo2, &MvnRepo1, &MvnRemoteRepo, &DockerRemoteRepo}, + TestLifecycle: {&RtDevRepo, &RtProdRepo}, } return getNeededRepositories(nonVirtualReposMap) } @@ -459,6 +465,7 @@ func GetBuildNames() []string { TestXray: {}, TestAccess: {}, TestTransfer: {&MvnBuildName}, + TestLifecycle: {&LcBuildName1, &LcBuildName2, &LcBuildName3}, } return getNeededBuildNames(buildNamesMap) } @@ -512,6 +519,13 @@ func getSubstitutionMap() map[string]string { "{PASSWORD_1}": Password1, "{USER_NAME_2}": UserName2, "{PASSWORD_2}": Password2, + "${LC_BUILD_NAME1}": LcBuildName1, + "${LC_BUILD_NAME2}": LcBuildName2, + "${LC_BUILD_NAME3}": LcBuildName3, + "${RB_NAME1}": LcRbName1, + "${RB_NAME2}": LcRbName2, + "${DEV_REPO}": RtDevRepo, + "${PROD_REPO}": RtProdRepo, } } @@ -561,6 +575,8 @@ func AddTimestampToGlobalVars() { RtRepo1And2Placeholder += uniqueSuffix RtRepo2 += uniqueSuffix RtVirtualRepo += uniqueSuffix + RtDevRepo += uniqueSuffix + RtProdRepo += uniqueSuffix // Builds/bundles/images BundleName += uniqueSuffix @@ -580,6 +596,12 @@ func AddTimestampToGlobalVars() { RtBuildName2 += uniqueSuffix RtBuildNameWithSpecialChars += uniqueSuffix RtPermissionTargetName += uniqueSuffix + LcBuildName1 += uniqueSuffix + LcBuildName2 += uniqueSuffix + LcBuildName3 += uniqueSuffix + LcRbName1 += uniqueSuffix + LcRbName2 += uniqueSuffix + LcRbName3 += uniqueSuffix // Users UserName1 += uniqueSuffix @@ -778,3 +800,24 @@ func SkipKnownFailingTest(t *testing.T) { t.Error("Not skipping test. Please fix the test or delay the skipMonth") } } + +func CreateContext(t *testing.T, testFlags, testArgs []string) (*cli.Context, *bytes.Buffer) { + flagSet := createFlagSet(t, testFlags, testArgs) + app := cli.NewApp() + app.Writer = &bytes.Buffer{} + return cli.NewContext(app, flagSet, nil), &bytes.Buffer{} +} + +// Create flag set with input flags and arguments. +func createFlagSet(t *testing.T, flags []string, args []string) *flag.FlagSet { + flagSet := flag.NewFlagSet("TestFlagSet", flag.ContinueOnError) + flags = append(flags, "url=http://127.0.0.1:8081/artifactory") + var cmdFlags []string + for _, curFlag := range flags { + flagSet.String(strings.Split(curFlag, "=")[0], "", "") + cmdFlags = append(cmdFlags, "--"+curFlag) + } + cmdFlags = append(cmdFlags, args...) + assert.NoError(t, flagSet.Parse(cmdFlags)) + return flagSet +} From d51e66bc37ce94c70124190caaf176d8e7723e54 Mon Sep 17 00:00:00 2001 From: Robi Nino Date: Thu, 29 Jun 2023 12:21:28 +0300 Subject: [PATCH 02/13] update go.mod --- go.mod | 4 ++-- go.sum | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index d6eaf5fd6..0160b66a3 100644 --- a/go.mod +++ b/go.mod @@ -123,8 +123,8 @@ require ( // replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go -replace github.com/jfrog/jfrog-cli-core/v2 => ../jfrog-cli-core +replace github.com/jfrog/jfrog-cli-core/v2 => github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230629090637-500555f9a712 // replace github.com/jfrog/gofrog => github.com/jfrog/gofrog v1.2.6-0.20230418122323-2bf299dd6d27 -replace github.com/jfrog/jfrog-client-go => ../jfrog-client-go +replace github.com/jfrog/jfrog-client-go => github.com/RobiNino/jfrog-client-go v0.0.0-20230629090849-29f071c1f89f diff --git a/go.sum b/go.sum index 00ff0d371..dc904eb0f 100644 --- a/go.sum +++ b/go.sum @@ -49,6 +49,10 @@ github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v github.com/Microsoft/hcsshim v0.9.7 h1:mKNHW/Xvv1aFH87Jb6ERDzXTJTLPlmzfZ28VBFD/bfg= github.com/ProtonMail/go-crypto v0.0.0-20230528122434-6f98819771a1 h1:JMDGhoQvXNTqH6Y3MC0IUw6tcZvaUdujNqzK2HYWZc8= github.com/ProtonMail/go-crypto v0.0.0-20230528122434-6f98819771a1/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230629090637-500555f9a712 h1:3u0tapaG2rRznVTBtnhRMXd12LEilQ59ZGpMCVLFQYI= +github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230629090637-500555f9a712/go.mod h1:iBVB8a8+Khep3ak6bFIvC2D5dVMJ+2MSX59oVf5ip0s= +github.com/RobiNino/jfrog-client-go v0.0.0-20230629090849-29f071c1f89f h1:hPofhdmFJRjWmzdHRnzhLrf5t+vytN9YqW8DtPFARzM= +github.com/RobiNino/jfrog-client-go v0.0.0-20230629090849-29f071c1f89f/go.mod h1:qEJxoe68sUtqHJ1YhXv/7pKYP/9p1D5tJrruzJKYeoI= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= From 729435c01c2570b58d43318c2d20fbb2fec8d8c0 Mon Sep 17 00:00:00 2001 From: Robi Nino Date: Thu, 29 Jun 2023 14:33:19 +0300 Subject: [PATCH 03/13] update args and options --- docs/lifecycle/create/help.go | 7 ++----- docs/lifecycle/promote/help.go | 6 +++--- lifecycle/cli.go | 29 +++++++++++++++++++---------- lifecycle_test.go | 9 +++++++-- utils/cliutils/commandsflags.go | 16 ++++++++-------- 5 files changed, 39 insertions(+), 28 deletions(-) diff --git a/docs/lifecycle/create/help.go b/docs/lifecycle/create/help.go index 66a2e78e2..ee23fe8c5 100644 --- a/docs/lifecycle/create/help.go +++ b/docs/lifecycle/create/help.go @@ -1,6 +1,6 @@ package create -var Usage = []string{"rbc [command options] "} +var Usage = []string{"rbc [command options] "} func GetDescription() string { return "Create a release bundle from builds or release bundles" @@ -11,8 +11,5 @@ func GetArguments() string { Name of the newly created Release Bundle. release bundle version - Version of the newly created Release Bundle. - - signing key name - The GPG/RSA key-pair name given in Artifactory.` + Version of the newly created Release Bundle.` } diff --git a/docs/lifecycle/promote/help.go b/docs/lifecycle/promote/help.go index 762f6e946..b4954a8cf 100644 --- a/docs/lifecycle/promote/help.go +++ b/docs/lifecycle/promote/help.go @@ -1,6 +1,6 @@ package promote -var Usage = []string{"rbp [command options] "} +var Usage = []string{"rbp [command options] "} func GetDescription() string { return "Promote a release bundle" @@ -13,6 +13,6 @@ func GetArguments() string { release bundle version Version of the Release Bundle to promote. - signing key name - The GPG/RSA key-pair name given in Artifactory.` + environment + Name of the target environment for the promotion.` } diff --git a/lifecycle/cli.go b/lifecycle/cli.go index 9681e16ac..f24df6863 100644 --- a/lifecycle/cli.go +++ b/lifecycle/cli.go @@ -55,10 +55,14 @@ func validateCreateReleaseBundleContext(c *cli.Context) error { return err } - if c.NArg() != 3 { + if c.NArg() != 2 { return cliutils.WrongNumberOfArgumentsHandler(c) } + if err := assertSigningKeyProvided(c); err != nil { + return err + } + buildsSourceProvided := c.String(cliutils.Builds) != "" releaseBundlesSourceProvided := c.String(cliutils.ReleaseBundles) != "" if (buildsSourceProvided && releaseBundlesSourceProvided) || @@ -79,13 +83,13 @@ func create(c *cli.Context) (err error) { } createCmd := lifecycle.NewReleaseBundleCreate().SetServerDetails(lcDetails).SetReleaseBundleName(c.Args().Get(0)). - SetReleaseBundleVersion(c.Args().Get(1)).SetSigningKeyName(c.Args().Get(2)).SetAsync(c.BoolT(cliutils.Async)). + SetReleaseBundleVersion(c.Args().Get(1)).SetSigningKeyName(c.String(cliutils.SigningKey)).SetAsync(c.BoolT(cliutils.Async)). SetReleaseBundleProject(cliutils.GetProject(c)).SetBuildsSpecPath(c.String(cliutils.Builds)). SetReleaseBundlesSpecPath(c.String(cliutils.ReleaseBundles)) return commands.Exec(createCmd) } -func promote(c *cli.Context) (err error) { +func promote(c *cli.Context) error { if show, err := cliutils.ShowCmdHelpIfNeeded(c, c.Args()); show || err != nil { return err } @@ -94,23 +98,28 @@ func promote(c *cli.Context) (err error) { return cliutils.WrongNumberOfArgumentsHandler(c) } - env := c.String(cliutils.Environment) - if env == "" { - return errorutils.CheckErrorf("the --%s option is mandatory", cliutils.Environment) + if err := assertSigningKeyProvided(c); err != nil { + return err } lcDetails, err := createLifecycleDetailsByFlags(c) if err != nil { - return + return err } createCmd := lifecycle.NewReleaseBundlePromote().SetServerDetails(lcDetails).SetReleaseBundleName(c.Args().Get(0)). - SetReleaseBundleVersion(c.Args().Get(1)).SetSigningKeyName(c.Args().Get(2)).SetAsync(c.BoolT(cliutils.Async)). - SetReleaseBundleProject(cliutils.GetProject(c)).SetEnvironment(env). - SetOverwrite(c.Bool(cliutils.Overwrite)) + SetReleaseBundleVersion(c.Args().Get(1)).SetEnvironment(c.Args().Get(2)).SetSigningKeyName(c.String(cliutils.SigningKey)). + SetAsync(c.BoolT(cliutils.Async)).SetReleaseBundleProject(cliutils.GetProject(c)).SetOverwrite(c.Bool(cliutils.Overwrite)) return commands.Exec(createCmd) } +func assertSigningKeyProvided(c *cli.Context) error { + if c.String(cliutils.SigningKey) == "" { + return errorutils.CheckErrorf("the --%s option is mandatory", cliutils.SigningKey) + } + return nil +} + func createLifecycleDetailsByFlags(c *cli.Context) (*coreConfig.ServerDetails, error) { lcDetails, err := cliutils.CreateServerDetailsWithConfigOffer(c, false, cliutils.Platform) if err != nil { diff --git a/lifecycle_test.go b/lifecycle_test.go index 49bb4834c..fdd3eb3c6 100644 --- a/lifecycle_test.go +++ b/lifecycle_test.go @@ -30,6 +30,7 @@ const ( releaseBundlesSpec = "release-bundles-spec.json" buildsSpec12 = "builds-spec-1-2.json" buildsSpec3 = "builds-spec-3.json" + prodEnvironment = "PROD" number1, number2, number3 = "111", "222", "333" ) @@ -83,11 +84,15 @@ func uploadBuilds(t *testing.T) func() { func createRb(t *testing.T, specName, sourceOption, buildName, buildNumber string, async bool) { specFile, err := getSpecFile(specName) assert.NoError(t, err) - assert.NoError(t, lcCli.Exec("rbc", buildName, buildNumber, gpgKeyPairName, "--"+sourceOption+"="+specFile, "--async="+strconv.FormatBool(async))) + assert.NoError(t, lcCli.Exec("rbc", buildName, buildNumber, getOption(sourceOption, specFile), getOption(cliutils.SigningKey, gpgKeyPairName), "--async="+strconv.FormatBool(async))) +} + +func getOption(option, value string) string { + return fmt.Sprintf("--%s=%s", option, value) } func promoteRb(t *testing.T, lcManager *lifecycle.LifecycleServicesManager, rbVersion string) { - output := lcCli.RunCliCmdWithOutput(t, "rbp", tests.LcRbName3, rbVersion, gpgKeyPairName, "--env=PROD", "--overwrite=true", "--async=true", "--project=default") + output := lcCli.RunCliCmdWithOutput(t, "rbp", tests.LcRbName3, rbVersion, prodEnvironment, getOption(cliutils.SigningKey, gpgKeyPairName), "--overwrite=true", "--async=true", "--project=default") var promotionResp services.RbPromotionResp if !assert.NoError(t, json.Unmarshal([]byte(output), &promotionResp)) { return diff --git a/utils/cliutils/commandsflags.go b/utils/cliutils/commandsflags.go index fda4bdca1..e5600dd1f 100644 --- a/utils/cliutils/commandsflags.go +++ b/utils/cliutils/commandsflags.go @@ -546,8 +546,8 @@ const ( lcBuilds = lifecyclePrefix + Builds ReleaseBundles = "release-bundles" lcReleaseBundles = lifecyclePrefix + ReleaseBundles - Environment = "env" - lcEnvironment = lifecyclePrefix + Environment + SigningKey = "signing-key" + lcSigningKey = lifecyclePrefix + SigningKey lcOverwrite = lifecyclePrefix + Overwrite ) @@ -1365,7 +1365,7 @@ var flagsMap = map[string]cli.Flag{ }, xrOutput: cli.StringFlag{ Name: xrOutput, - Usage: "[Default: table] Defines the output format of the command. Acceptable values are: table, json, simple-json and sarif. Note: the json format doesn’t include information about scans that are included as part of the Advanced Security package.` `", + Usage: "[Default: table] Defines the output format of the command. Acceptable values are: table, json, simple-json and sarif. Note: the json format doesn't include information about scans that are included as part of the Advanced Security package.` `", }, BypassArchiveLimits: cli.BoolFlag{ Name: BypassArchiveLimits, @@ -1593,9 +1593,9 @@ var flagsMap = map[string]cli.Flag{ Name: ReleaseBundles, Usage: "[Optional] Path to a JSON file containing information of the source release bundles from which to create a release bundle.` `", }, - lcEnvironment: cli.StringFlag{ - Name: Environment, - Usage: "[Mandatory] Name of the target environment for the promotion.` `", + lcSigningKey: cli.StringFlag{ + Name: SigningKey, + Usage: "[Mandatory] The GPG/RSA key-pair name given in Artifactory.` `", }, lcOverwrite: cli.BoolFlag{ Name: Overwrite, @@ -1880,10 +1880,10 @@ var commandFlags = map[string][]string{ installPluginVersion, InstallPluginSrcDir, InstallPluginHomeDir, }, ReleaseBundleCreate: { - lcUrl, user, password, accessToken, serverId, lcAsync, lcProject, lcBuilds, lcReleaseBundles, + lcUrl, user, password, accessToken, serverId, lcSigningKey, lcAsync, lcProject, lcBuilds, lcReleaseBundles, }, ReleaseBundlePromote: { - lcUrl, user, password, accessToken, serverId, lcAsync, lcProject, lcEnvironment, lcOverwrite, + lcUrl, user, password, accessToken, serverId, lcSigningKey, lcAsync, lcProject, lcOverwrite, }, // Xray's commands OfflineUpdate: { From b4215d289a4db9ff80cb4b3bb71403bed1725379 Mon Sep 17 00:00:00 2001 From: Robi Nino Date: Thu, 29 Jun 2023 15:25:25 +0300 Subject: [PATCH 04/13] update go.mod --- go.mod | 4 ++-- go.sum | 8 ++++---- lifecycle_test.go | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 0160b66a3..6e4575d76 100644 --- a/go.mod +++ b/go.mod @@ -123,8 +123,8 @@ require ( // replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go -replace github.com/jfrog/jfrog-cli-core/v2 => github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230629090637-500555f9a712 +replace github.com/jfrog/jfrog-cli-core/v2 => github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230629122351-606dd8420a71 // replace github.com/jfrog/gofrog => github.com/jfrog/gofrog v1.2.6-0.20230418122323-2bf299dd6d27 -replace github.com/jfrog/jfrog-client-go => github.com/RobiNino/jfrog-client-go v0.0.0-20230629090849-29f071c1f89f +replace github.com/jfrog/jfrog-client-go => github.com/RobiNino/jfrog-client-go v0.0.0-20230629122246-1d4d690fb2b2 diff --git a/go.sum b/go.sum index dc904eb0f..5c3023742 100644 --- a/go.sum +++ b/go.sum @@ -49,10 +49,10 @@ github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v github.com/Microsoft/hcsshim v0.9.7 h1:mKNHW/Xvv1aFH87Jb6ERDzXTJTLPlmzfZ28VBFD/bfg= github.com/ProtonMail/go-crypto v0.0.0-20230528122434-6f98819771a1 h1:JMDGhoQvXNTqH6Y3MC0IUw6tcZvaUdujNqzK2HYWZc8= github.com/ProtonMail/go-crypto v0.0.0-20230528122434-6f98819771a1/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= -github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230629090637-500555f9a712 h1:3u0tapaG2rRznVTBtnhRMXd12LEilQ59ZGpMCVLFQYI= -github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230629090637-500555f9a712/go.mod h1:iBVB8a8+Khep3ak6bFIvC2D5dVMJ+2MSX59oVf5ip0s= -github.com/RobiNino/jfrog-client-go v0.0.0-20230629090849-29f071c1f89f h1:hPofhdmFJRjWmzdHRnzhLrf5t+vytN9YqW8DtPFARzM= -github.com/RobiNino/jfrog-client-go v0.0.0-20230629090849-29f071c1f89f/go.mod h1:qEJxoe68sUtqHJ1YhXv/7pKYP/9p1D5tJrruzJKYeoI= +github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230629122351-606dd8420a71 h1:6jrEzc/ObWeZNWeyf/PaDHmm7CRsPKLO7k6gosp/iIo= +github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230629122351-606dd8420a71/go.mod h1:1PIKTMPvponMGaDu2CbsOJepuf+Y1MUh2Oe47pBkjbc= +github.com/RobiNino/jfrog-client-go v0.0.0-20230629122246-1d4d690fb2b2 h1:QU4PzZ06pJiJJ8pXyC8Ga3gqRvFvAIIb5VtefA2rrDM= +github.com/RobiNino/jfrog-client-go v0.0.0-20230629122246-1d4d690fb2b2/go.mod h1:qEJxoe68sUtqHJ1YhXv/7pKYP/9p1D5tJrruzJKYeoI= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= diff --git a/lifecycle_test.go b/lifecycle_test.go index fdd3eb3c6..fe0aa545b 100644 --- a/lifecycle_test.go +++ b/lifecycle_test.go @@ -145,7 +145,7 @@ func getStatus(lcManager *lifecycle.LifecycleServicesManager, rbName, rbVersion, } if createdMillis == "" { - return lcManager.GetReleaseBundleCreateStatus(rbDetails, "", true) + return lcManager.GetReleaseBundleCreationStatus(rbDetails, "", true) } return lcManager.GetReleaseBundlePromotionStatus(rbDetails, "", createdMillis, true) } From 76812952c05cf614476cba283011ae6c5e533014 Mon Sep 17 00:00:00 2001 From: Robi Nino Date: Thu, 29 Jun 2023 16:48:23 +0300 Subject: [PATCH 05/13] change async option to sync --- lifecycle/cli.go | 4 ++-- lifecycle_test.go | 27 ++++++++++++++++++++------- utils/cliutils/commandsflags.go | 14 +++++++------- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/lifecycle/cli.go b/lifecycle/cli.go index f24df6863..2a5b8bb4c 100644 --- a/lifecycle/cli.go +++ b/lifecycle/cli.go @@ -83,7 +83,7 @@ func create(c *cli.Context) (err error) { } createCmd := lifecycle.NewReleaseBundleCreate().SetServerDetails(lcDetails).SetReleaseBundleName(c.Args().Get(0)). - SetReleaseBundleVersion(c.Args().Get(1)).SetSigningKeyName(c.String(cliutils.SigningKey)).SetAsync(c.BoolT(cliutils.Async)). + SetReleaseBundleVersion(c.Args().Get(1)).SetSigningKeyName(c.String(cliutils.SigningKey)).SetAsync(!c.Bool(cliutils.Sync)). SetReleaseBundleProject(cliutils.GetProject(c)).SetBuildsSpecPath(c.String(cliutils.Builds)). SetReleaseBundlesSpecPath(c.String(cliutils.ReleaseBundles)) return commands.Exec(createCmd) @@ -109,7 +109,7 @@ func promote(c *cli.Context) error { createCmd := lifecycle.NewReleaseBundlePromote().SetServerDetails(lcDetails).SetReleaseBundleName(c.Args().Get(0)). SetReleaseBundleVersion(c.Args().Get(1)).SetEnvironment(c.Args().Get(2)).SetSigningKeyName(c.String(cliutils.SigningKey)). - SetAsync(c.BoolT(cliutils.Async)).SetReleaseBundleProject(cliutils.GetProject(c)).SetOverwrite(c.Bool(cliutils.Overwrite)) + SetAsync(!c.Bool(cliutils.Sync)).SetReleaseBundleProject(cliutils.GetProject(c)).SetOverwrite(c.Bool(cliutils.Overwrite)) return commands.Exec(createCmd) } diff --git a/lifecycle_test.go b/lifecycle_test.go index fe0aa545b..24980c734 100644 --- a/lifecycle_test.go +++ b/lifecycle_test.go @@ -19,7 +19,6 @@ import ( "net/http" "os" "path/filepath" - "strconv" "testing" ) @@ -49,16 +48,16 @@ func TestLifecycle(t *testing.T) { defer deleteBuilds() // Create release bundles from builds synchronously. - createRb(t, buildsSpec12, cliutils.Builds, tests.LcRbName1, number1, false) + createRb(t, buildsSpec12, cliutils.Builds, tests.LcRbName1, number1, true) defer deleteReleaseBundle(t, lcManager, tests.LcRbName1, number1) // Create release bundles from builds asynchronously and assert status. - createRb(t, buildsSpec3, cliutils.Builds, tests.LcRbName2, number2, true) + createRb(t, buildsSpec3, cliutils.Builds, tests.LcRbName2, number2, false) defer deleteReleaseBundle(t, lcManager, tests.LcRbName2, number2) assertStatusCompleted(t, lcManager, tests.LcRbName2, number2, "") // Create a combined release bundle from the two previous release bundle. - createRb(t, releaseBundlesSpec, cliutils.ReleaseBundles, tests.LcRbName3, number3, false) + createRb(t, releaseBundlesSpec, cliutils.ReleaseBundles, tests.LcRbName3, number3, true) defer deleteReleaseBundle(t, lcManager, tests.LcRbName3, number3) // Promote the last release bundle. @@ -81,10 +80,21 @@ func uploadBuilds(t *testing.T) func() { } } -func createRb(t *testing.T, specName, sourceOption, buildName, buildNumber string, async bool) { +func createRb(t *testing.T, specName, sourceOption, buildName, buildNumber string, sync bool) { specFile, err := getSpecFile(specName) assert.NoError(t, err) - assert.NoError(t, lcCli.Exec("rbc", buildName, buildNumber, getOption(sourceOption, specFile), getOption(cliutils.SigningKey, gpgKeyPairName), "--async="+strconv.FormatBool(async))) + argsAndOptions := []string{ + "rbc", + buildName, + buildNumber, + getOption(sourceOption, specFile), + getOption(cliutils.SigningKey, gpgKeyPairName), + } + // Add the --sync option only if requested, to test the default value. + if sync { + argsAndOptions = append(argsAndOptions, getOption(cliutils.Sync, "true")) + } + assert.NoError(t, lcCli.Exec(argsAndOptions...)) } func getOption(option, value string) string { @@ -92,7 +102,10 @@ func getOption(option, value string) string { } func promoteRb(t *testing.T, lcManager *lifecycle.LifecycleServicesManager, rbVersion string) { - output := lcCli.RunCliCmdWithOutput(t, "rbp", tests.LcRbName3, rbVersion, prodEnvironment, getOption(cliutils.SigningKey, gpgKeyPairName), "--overwrite=true", "--async=true", "--project=default") + output := lcCli.RunCliCmdWithOutput(t, "rbp", tests.LcRbName3, rbVersion, prodEnvironment, + getOption(cliutils.SigningKey, gpgKeyPairName), + getOption(cliutils.Overwrite, "true"), + "--project=default") var promotionResp services.RbPromotionResp if !assert.NoError(t, json.Unmarshal([]byte(output), &promotionResp)) { return diff --git a/utils/cliutils/commandsflags.go b/utils/cliutils/commandsflags.go index e5600dd1f..080fea61f 100644 --- a/utils/cliutils/commandsflags.go +++ b/utils/cliutils/commandsflags.go @@ -409,7 +409,7 @@ const ( // Base flags distUrl = "dist-url" - // Unique release-bundle-* flags + // Unique release-bundle-* v1 flags releaseBundleV1Prefix = "rbv1-" rbDryRun = releaseBundleV1Prefix + dryRun rbRepo = releaseBundleV1Prefix + repo @@ -540,7 +540,7 @@ const ( // Unique lifecycle flags lifecyclePrefix = "lc-" lcUrl = lifecyclePrefix + url - lcAsync = lifecyclePrefix + Async + lcSync = lifecyclePrefix + Sync lcProject = lifecyclePrefix + project Builds = "builds" lcBuilds = lifecyclePrefix + Builds @@ -1577,9 +1577,9 @@ var flagsMap = map[string]cli.Flag{ Name: url, Usage: "[Optional] JFrog platform URL.` `", }, - lcAsync: cli.BoolTFlag{ - Name: Async, - Usage: "[Default: true] Set to false to run synchronously and wait for response.` `", + lcSync: cli.BoolFlag{ + Name: Sync, + Usage: "[Default: false] Set to true to run synchronously.` `", }, lcProject: cli.StringFlag{ Name: project, @@ -1880,10 +1880,10 @@ var commandFlags = map[string][]string{ installPluginVersion, InstallPluginSrcDir, InstallPluginHomeDir, }, ReleaseBundleCreate: { - lcUrl, user, password, accessToken, serverId, lcSigningKey, lcAsync, lcProject, lcBuilds, lcReleaseBundles, + lcUrl, user, password, accessToken, serverId, lcSigningKey, lcSync, lcProject, lcBuilds, lcReleaseBundles, }, ReleaseBundlePromote: { - lcUrl, user, password, accessToken, serverId, lcSigningKey, lcAsync, lcProject, lcOverwrite, + lcUrl, user, password, accessToken, serverId, lcSigningKey, lcSync, lcProject, lcOverwrite, }, // Xray's commands OfflineUpdate: { From 01aeb6c43bafb26dcc23dc479f08caf34d141eef Mon Sep 17 00:00:00 2001 From: Robi Nino Date: Thu, 29 Jun 2023 17:32:11 +0300 Subject: [PATCH 06/13] static --- lifecycle/cli.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lifecycle/cli.go b/lifecycle/cli.go index 2a5b8bb4c..c0b46ee75 100644 --- a/lifecycle/cli.go +++ b/lifecycle/cli.go @@ -29,9 +29,7 @@ func GetCommands() []cli.Command { ArgsUsage: common.CreateEnvVars(), BashComplete: coreCommon.CreateBashCompletionFunc(), Category: lcCategory, - Action: func(c *cli.Context) error { - return create(c) - }, + Action: create, }, { Name: "release-bundle-promote", @@ -43,9 +41,7 @@ func GetCommands() []cli.Command { ArgsUsage: common.CreateEnvVars(), BashComplete: coreCommon.CreateBashCompletionFunc(), Category: lcCategory, - Action: func(c *cli.Context) error { - return promote(c) - }, + Action: promote, }, }) } From a69ad56214e769872ccea71f7d41fd13ae58551e Mon Sep 17 00:00:00 2001 From: Robi Nino Date: Thu, 29 Jun 2023 18:07:25 +0300 Subject: [PATCH 07/13] fix test --- go.mod | 4 ++-- go.sum | 8 ++++---- lifecycle/cli.go | 4 ++-- lifecycle/cli_test.go | 8 ++++++-- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 6e4575d76..b7df7b09c 100644 --- a/go.mod +++ b/go.mod @@ -123,8 +123,8 @@ require ( // replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go -replace github.com/jfrog/jfrog-cli-core/v2 => github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230629122351-606dd8420a71 +replace github.com/jfrog/jfrog-cli-core/v2 => github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230629150148-ffba89f2aa5e // replace github.com/jfrog/gofrog => github.com/jfrog/gofrog v1.2.6-0.20230418122323-2bf299dd6d27 -replace github.com/jfrog/jfrog-client-go => github.com/RobiNino/jfrog-client-go v0.0.0-20230629122246-1d4d690fb2b2 +replace github.com/jfrog/jfrog-client-go => github.com/RobiNino/jfrog-client-go v0.0.0-20230629135822-1b7215b50da4 diff --git a/go.sum b/go.sum index 5c3023742..b861ad153 100644 --- a/go.sum +++ b/go.sum @@ -49,10 +49,10 @@ github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v github.com/Microsoft/hcsshim v0.9.7 h1:mKNHW/Xvv1aFH87Jb6ERDzXTJTLPlmzfZ28VBFD/bfg= github.com/ProtonMail/go-crypto v0.0.0-20230528122434-6f98819771a1 h1:JMDGhoQvXNTqH6Y3MC0IUw6tcZvaUdujNqzK2HYWZc8= github.com/ProtonMail/go-crypto v0.0.0-20230528122434-6f98819771a1/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= -github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230629122351-606dd8420a71 h1:6jrEzc/ObWeZNWeyf/PaDHmm7CRsPKLO7k6gosp/iIo= -github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230629122351-606dd8420a71/go.mod h1:1PIKTMPvponMGaDu2CbsOJepuf+Y1MUh2Oe47pBkjbc= -github.com/RobiNino/jfrog-client-go v0.0.0-20230629122246-1d4d690fb2b2 h1:QU4PzZ06pJiJJ8pXyC8Ga3gqRvFvAIIb5VtefA2rrDM= -github.com/RobiNino/jfrog-client-go v0.0.0-20230629122246-1d4d690fb2b2/go.mod h1:qEJxoe68sUtqHJ1YhXv/7pKYP/9p1D5tJrruzJKYeoI= +github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230629150148-ffba89f2aa5e h1:a8t7yfaxbmV+MtuJSgCoj/UAGkWl2VV+MLhwdI1t9dc= +github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230629150148-ffba89f2aa5e/go.mod h1:qt/CDw2JqlK0axYGrSypDIrc3s6WjvkZ7bparuLl2Wk= +github.com/RobiNino/jfrog-client-go v0.0.0-20230629135822-1b7215b50da4 h1:sgmArfuVyaBWLsoy11LHuYjdCTZ3XfwXNA4miY6QHqk= +github.com/RobiNino/jfrog-client-go v0.0.0-20230629135822-1b7215b50da4/go.mod h1:qEJxoe68sUtqHJ1YhXv/7pKYP/9p1D5tJrruzJKYeoI= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= diff --git a/lifecycle/cli.go b/lifecycle/cli.go index c0b46ee75..3b9c6aec2 100644 --- a/lifecycle/cli.go +++ b/lifecycle/cli.go @@ -79,7 +79,7 @@ func create(c *cli.Context) (err error) { } createCmd := lifecycle.NewReleaseBundleCreate().SetServerDetails(lcDetails).SetReleaseBundleName(c.Args().Get(0)). - SetReleaseBundleVersion(c.Args().Get(1)).SetSigningKeyName(c.String(cliutils.SigningKey)).SetAsync(!c.Bool(cliutils.Sync)). + SetReleaseBundleVersion(c.Args().Get(1)).SetSigningKeyName(c.String(cliutils.SigningKey)).SetSync(c.Bool(cliutils.Sync)). SetReleaseBundleProject(cliutils.GetProject(c)).SetBuildsSpecPath(c.String(cliutils.Builds)). SetReleaseBundlesSpecPath(c.String(cliutils.ReleaseBundles)) return commands.Exec(createCmd) @@ -105,7 +105,7 @@ func promote(c *cli.Context) error { createCmd := lifecycle.NewReleaseBundlePromote().SetServerDetails(lcDetails).SetReleaseBundleName(c.Args().Get(0)). SetReleaseBundleVersion(c.Args().Get(1)).SetEnvironment(c.Args().Get(2)).SetSigningKeyName(c.String(cliutils.SigningKey)). - SetAsync(!c.Bool(cliutils.Sync)).SetReleaseBundleProject(cliutils.GetProject(c)).SetOverwrite(c.Bool(cliutils.Overwrite)) + SetSync(c.Bool(cliutils.Sync)).SetReleaseBundleProject(cliutils.GetProject(c)).SetOverwrite(c.Bool(cliutils.Overwrite)) return commands.Exec(createCmd) } diff --git a/lifecycle/cli_test.go b/lifecycle/cli_test.go index e8f41ace4..9da70f718 100644 --- a/lifecycle/cli_test.go +++ b/lifecycle/cli_test.go @@ -20,8 +20,12 @@ func TestValidateCreateReleaseBundleContext(t *testing.T) { {"extraArgs", []string{"one", "two", "three", "four"}, []string{}, true}, {"bothSources", []string{"one", "two", "three"}, []string{cliutils.Builds + "=/path/to/file", cliutils.ReleaseBundles + "=/path/to/file"}, true}, {"noSources", []string{"one", "two", "three"}, []string{}, true}, - {"builds", []string{"one", "two", "three"}, []string{cliutils.Builds + "=/path/to/file"}, false}, - {"releaseBundles", []string{"one", "two", "three"}, []string{cliutils.ReleaseBundles + "=/path/to/file"}, false}, + {"builds without signing key", []string{"name", "version"}, []string{cliutils.Builds + "=/path/to/file"}, true}, + {"builds correct", []string{"name", "version"}, []string{ + cliutils.Builds + "=/path/to/file", cliutils.SigningKey + "=key"}, false}, + {"releaseBundles without signing key", []string{"name", "version", "env"}, []string{cliutils.ReleaseBundles + "=/path/to/file"}, true}, + {"releaseBundles", []string{"name", "version"}, []string{ + cliutils.ReleaseBundles + "=/path/to/file", cliutils.SigningKey + "=key"}, false}, } for _, test := range testRuns { From 1a3174f9eeec1a478fed2ad91e6f371c4cb9abc9 Mon Sep 17 00:00:00 2001 From: Robi Nino Date: Thu, 29 Jun 2023 18:14:54 +0300 Subject: [PATCH 08/13] CR --- docs/lifecycle/create/help.go | 2 +- lifecycle/cli.go | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/lifecycle/create/help.go b/docs/lifecycle/create/help.go index ee23fe8c5..c7079e3f2 100644 --- a/docs/lifecycle/create/help.go +++ b/docs/lifecycle/create/help.go @@ -3,7 +3,7 @@ package create var Usage = []string{"rbc [command options] "} func GetDescription() string { - return "Create a release bundle from builds or release bundles" + return "Create a release bundle from builds or from existing release bundles" } func GetArguments() string { diff --git a/lifecycle/cli.go b/lifecycle/cli.go index 3b9c6aec2..8caa332d2 100644 --- a/lifecycle/cli.go +++ b/lifecycle/cli.go @@ -59,10 +59,9 @@ func validateCreateReleaseBundleContext(c *cli.Context) error { return err } - buildsSourceProvided := c.String(cliutils.Builds) != "" - releaseBundlesSourceProvided := c.String(cliutils.ReleaseBundles) != "" - if (buildsSourceProvided && releaseBundlesSourceProvided) || - !(buildsSourceProvided || releaseBundlesSourceProvided) { + bothProvided := c.IsSet(cliutils.Builds) && c.IsSet(cliutils.ReleaseBundles) + noneProvided := !c.IsSet(cliutils.Builds) && !c.IsSet(cliutils.ReleaseBundles) + if bothProvided || noneProvided { return errorutils.CheckErrorf("exactly one of the following options must be supplied: --%s or --%s", cliutils.Builds, cliutils.ReleaseBundles) } return nil From d0b03b32fb4c644788e7cbb061dac1f42608c6f6 Mon Sep 17 00:00:00 2001 From: Robi Nino Date: Thu, 29 Jun 2023 19:11:54 +0300 Subject: [PATCH 09/13] add documentation --- documentation/CLI-for-JFrog-Lifecycle.md | 133 +++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 documentation/CLI-for-JFrog-Lifecycle.md diff --git a/documentation/CLI-for-JFrog-Lifecycle.md b/documentation/CLI-for-JFrog-Lifecycle.md new file mode 100644 index 000000000..f81b7c817 --- /dev/null +++ b/documentation/CLI-for-JFrog-Lifecycle.md @@ -0,0 +1,133 @@ +JFrog CLI : CLI for JFrog Release Lifecycle Management +====================================== + + +Overview +-------- + +This page describes how to use JFrog CLI with [JFrog Release Lifecycle Management](https://jfrog.com/help/r/jfrog-artifactory-documentation/jfrog-release-lifecycle-management-solution). + +Read more about JFrog CLI [here](https://jfrog.com/help/r/jfrog-cli). + +--- +**Note** +> JFrog Release Lifecycle Management is only available since [Artifactory 7.63.2](https://jfrog.com/help/r/jfrog-release-information/artifactory-7.63.2-cloud). +--- + +### Syntax + +When used with JFrog Release Lifecycle Management, JFrog CLI uses the following syntax: + + $ jf command-name global-options command-options arguments + +### Commands + +The following sections describe the commands available in JFrog CLI for use with JFrog Release Lifecycle Management. + +### Creating a release bundle from builds or from existing release bundles + +This commands allows creating a release bundle from one of two sources: +1. Published build infos. To use, provide the `--builds` option, which accepts a path to a JSON file of the following syntax: + ```json + { + "builds": [ + { + "name": "build name", + "number": "build number", + "project": "project" + } + ] + } + ``` + `number` is optional, latest build will be used if empty. + + `project` is optional, default project will be used if empty. + +2. Existing release bundles. To use, provide the `--release-bundles` option, which accepts a path to a JSON file of the following syntax: + ```json + { + "releaseBundles": [ + { + "name": "release bundle name", + "version": "release bundle version", + "project": "project" + } + ] + } + ``` + `project` is optional, default project will be used if empty. + +| | | +|------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Command-name | release-bundle-create | +| Abbreviation | rbc | +| Command options | | +| --builds | \[Optional\]

Path to a JSON file containing information of the source builds from which to create a release bundle. | +| --project | \[Optional\]

Project key associated with the Release Bundle version. | +| --release-bundles | \[Optional\]

Path to a JSON file containing information of the source release bundles from which to create a release bundle. | +| --server-id | \[Optional\]

Platform server ID configured using the config command. | +| --signing-key | \[Mandatory\]

The GPG/RSA key-pair name given in Artifactory. | +| --sync | \[Default: false\]

Set to true to run synchronously. | +| Command arguments | | +| release bundle name | Name of the newly created Release Bundle. | +| release bundle version | Version of the newly created Release Bundle. | + +##### Examples + +##### Example 1 + +Create a release bundle with name "myApp" and version "1.0.0", with signing key pair "myKeyPair". +The release bundle will include artifacts of the builds that were provided in the builds spec. + + jf rbc --builds=/path/to/builds-spec.json --signing-key=myKeyPair myApp 1.0.0 + +##### Example 2 + +Create a release bundle with name "myApp" and version "1.0.0", with signing key pair "myKeyPair". +The release bundle will include artifacts of the release bundles that were provided in the release bundles spec. + + jf ds rbc --spec=/path/to/release-bundles-spec.json --signing-key=myKeyPair myApp 1.0.0 + +##### Example 3 + +Create a release bundle synchronously with name "myApp" and version "1.0.0", in project "project0", with signing key pair "myKeyPair". +The release bundle will include artifacts of the release bundles that were provided in the release bundles spec. + + jf ds rbc --spec=/path/to/release-bundles-spec.json --signing-key=myKeyPair --sync=true --project=project0 myApp 1.0.0 + +### Promoting a release bundle + +This commands allows promoting a release bundle to a target environment. + +| | | +|------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Command-name | release-bundle-promote | +| Abbreviation | rbp | +| Command options | | +| --overwrite | \[Default: false\]

Set to true to replace artifacts with the same name but a different checksum if such already exist at the promotion targets. By default, the promotion is stopped in a case of such conflict | +| --project | \[Optional\]

Project key associated with the Release Bundle version. | +| --server-id | \[Optional\]

Platform server ID configured using the config command. | +| --signing-key | \[Mandatory\]

The GPG/RSA key-pair name given in Artifactory. | +| --sync | \[Default: false\]

Set to true to run synchronously. | +| Command arguments | | +| release bundle name | Name of the Release Bundle to promote. | +| release bundle version | Version of the Release Bundle to promote. | +| environment | Name of the target environment for the promotion. | + + +##### Examples + +##### Example 1 + +Promote a release bundle named "myApp" version "1.0.0" to environment "PROD". +Use signing key pair "myKeyPair". + + jf rbp --signing-key=myKeyPair myApp 1.0.0 PROD + +##### Example 2 + +Promote a release bundle synchronously to environment "PROD". +The release bundle is named "myApp", version "1.0.0", of project "project0". +Use signing key pair "myKeyPair" and overwrite at conflict. + + jf rbp --signing-key=myKeyPair --project=project0 --overwrite=true --sync=true myApp 1.0.0 PROD From 2e60e8486ef2e9793c59295ada6a27e6ab81bd15 Mon Sep 17 00:00:00 2001 From: Robi Nino Date: Thu, 29 Jun 2023 20:00:29 +0300 Subject: [PATCH 10/13] update gomod --- go.mod | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index b7df7b09c..cd6499580 100644 --- a/go.mod +++ b/go.mod @@ -123,8 +123,8 @@ require ( // replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go -replace github.com/jfrog/jfrog-cli-core/v2 => github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230629150148-ffba89f2aa5e +replace github.com/jfrog/jfrog-cli-core/v2 => github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230629165935-eb63401cb7e3 // replace github.com/jfrog/gofrog => github.com/jfrog/gofrog v1.2.6-0.20230418122323-2bf299dd6d27 -replace github.com/jfrog/jfrog-client-go => github.com/RobiNino/jfrog-client-go v0.0.0-20230629135822-1b7215b50da4 +replace github.com/jfrog/jfrog-client-go => github.com/RobiNino/jfrog-client-go v0.0.0-20230629165859-5fbe13350928 From 0175b5a92bc48df3e9ce6f1e4fe57815bfce343e Mon Sep 17 00:00:00 2001 From: Robi Nino Date: Thu, 29 Jun 2023 20:12:05 +0300 Subject: [PATCH 11/13] run on all os --- .github/workflows/lifeCycleTests.yml | 2 +- go.sum | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/lifeCycleTests.yml b/.github/workflows/lifeCycleTests.yml index 74e2a8ff4..b4c97de76 100644 --- a/.github/workflows/lifeCycleTests.yml +++ b/.github/workflows/lifeCycleTests.yml @@ -15,7 +15,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest] + os: [ ubuntu-latest, macos-latest, windows-latest ] runs-on: ${{ matrix.os }} steps: - name: Install Go diff --git a/go.sum b/go.sum index b861ad153..8cbc438ab 100644 --- a/go.sum +++ b/go.sum @@ -49,10 +49,10 @@ github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v github.com/Microsoft/hcsshim v0.9.7 h1:mKNHW/Xvv1aFH87Jb6ERDzXTJTLPlmzfZ28VBFD/bfg= github.com/ProtonMail/go-crypto v0.0.0-20230528122434-6f98819771a1 h1:JMDGhoQvXNTqH6Y3MC0IUw6tcZvaUdujNqzK2HYWZc8= github.com/ProtonMail/go-crypto v0.0.0-20230528122434-6f98819771a1/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= -github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230629150148-ffba89f2aa5e h1:a8t7yfaxbmV+MtuJSgCoj/UAGkWl2VV+MLhwdI1t9dc= -github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230629150148-ffba89f2aa5e/go.mod h1:qt/CDw2JqlK0axYGrSypDIrc3s6WjvkZ7bparuLl2Wk= -github.com/RobiNino/jfrog-client-go v0.0.0-20230629135822-1b7215b50da4 h1:sgmArfuVyaBWLsoy11LHuYjdCTZ3XfwXNA4miY6QHqk= -github.com/RobiNino/jfrog-client-go v0.0.0-20230629135822-1b7215b50da4/go.mod h1:qEJxoe68sUtqHJ1YhXv/7pKYP/9p1D5tJrruzJKYeoI= +github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230629165935-eb63401cb7e3 h1:ogD8KSusqlc1NxV0+85hbMEpLgViil/CkZOWC1oGhaQ= +github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230629165935-eb63401cb7e3/go.mod h1:iYXHCd/GahiUQj2+6R/+PGV8pCLFpZNaovs0F4EQYHk= +github.com/RobiNino/jfrog-client-go v0.0.0-20230629165859-5fbe13350928 h1:gxHb0FtWuq6NXNGoHZ9TwakbH925tByxDGOHDwPcack= +github.com/RobiNino/jfrog-client-go v0.0.0-20230629165859-5fbe13350928/go.mod h1:qEJxoe68sUtqHJ1YhXv/7pKYP/9p1D5tJrruzJKYeoI= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= From 60644e590047480b7670746c43a3716b70e0e3a5 Mon Sep 17 00:00:00 2001 From: Robi Nino Date: Thu, 29 Jun 2023 20:36:21 +0300 Subject: [PATCH 12/13] disable tests --- .github/workflows/lifeCycleTests.yml | 43 ---------------------------- documentation/cli.ftmap | 4 ++- 2 files changed, 3 insertions(+), 44 deletions(-) delete mode 100644 .github/workflows/lifeCycleTests.yml diff --git a/.github/workflows/lifeCycleTests.yml b/.github/workflows/lifeCycleTests.yml deleted file mode 100644 index b4c97de76..000000000 --- a/.github/workflows/lifeCycleTests.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Lifecycle Tests -on: - push: - # Triggers the workflow on labeled PRs only. - pull_request_target: - types: [labeled] -# Ensures that only the latest commit is running for each PR at a time. -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}-${{ github.ref }} - cancel-in-progress: true -jobs: - Life-Cycle-Tests: - if: contains(github.event.pull_request.labels.*.name, 'safe to test') || github.event_name == 'push' - name: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: [ ubuntu-latest, macos-latest, windows-latest ] - runs-on: ${{ matrix.os }} - steps: - - name: Install Go - uses: actions/setup-go@v3 - with: - go-version: 1.20.x - - name: Checkout code - uses: actions/checkout@v3 - with: - ref: ${{ github.event.pull_request.head.sha }} - - name: Go Cache - uses: actions/cache@v3 - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: ${{ runner.os }}-go- - - name: Setup Artifactory - run: | - go install github.com/jfrog/jfrog-testing-infra/local-rt-setup@latest - ~/go/bin/local-rt-setup - env: - RTLIC: ${{secrets.RTLIC}} - GOPROXY: direct - - name: Run Lifecycle tests - run: go test -v github.com/jfrog/jfrog-cli --timeout 0 --test.rb diff --git a/documentation/cli.ftmap b/documentation/cli.ftmap index 8a14c4de4..2a967949b 100644 --- a/documentation/cli.ftmap +++ b/documentation/cli.ftmap @@ -13,6 +13,8 @@ - + + + From 55fd34b89f212db62bec9531bbc6665623bcb636 Mon Sep 17 00:00:00 2001 From: Robi Nino Date: Thu, 29 Jun 2023 21:44:57 +0300 Subject: [PATCH 13/13] update gomod --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index cd6499580..c516a946e 100644 --- a/go.mod +++ b/go.mod @@ -123,8 +123,8 @@ require ( // replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go -replace github.com/jfrog/jfrog-cli-core/v2 => github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230629165935-eb63401cb7e3 +replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20230629184314-ff61ffffba34 // replace github.com/jfrog/gofrog => github.com/jfrog/gofrog v1.2.6-0.20230418122323-2bf299dd6d27 -replace github.com/jfrog/jfrog-client-go => github.com/RobiNino/jfrog-client-go v0.0.0-20230629165859-5fbe13350928 +replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20230629174113-81715f46ec0f diff --git a/go.sum b/go.sum index 8cbc438ab..6e5785091 100644 --- a/go.sum +++ b/go.sum @@ -49,10 +49,6 @@ github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v github.com/Microsoft/hcsshim v0.9.7 h1:mKNHW/Xvv1aFH87Jb6ERDzXTJTLPlmzfZ28VBFD/bfg= github.com/ProtonMail/go-crypto v0.0.0-20230528122434-6f98819771a1 h1:JMDGhoQvXNTqH6Y3MC0IUw6tcZvaUdujNqzK2HYWZc8= github.com/ProtonMail/go-crypto v0.0.0-20230528122434-6f98819771a1/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= -github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230629165935-eb63401cb7e3 h1:ogD8KSusqlc1NxV0+85hbMEpLgViil/CkZOWC1oGhaQ= -github.com/RobiNino/jfrog-cli-core/v2 v2.0.0-20230629165935-eb63401cb7e3/go.mod h1:iYXHCd/GahiUQj2+6R/+PGV8pCLFpZNaovs0F4EQYHk= -github.com/RobiNino/jfrog-client-go v0.0.0-20230629165859-5fbe13350928 h1:gxHb0FtWuq6NXNGoHZ9TwakbH925tByxDGOHDwPcack= -github.com/RobiNino/jfrog-client-go v0.0.0-20230629165859-5fbe13350928/go.mod h1:qEJxoe68sUtqHJ1YhXv/7pKYP/9p1D5tJrruzJKYeoI= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= @@ -242,6 +238,10 @@ github.com/jfrog/build-info-go v1.9.6 h1:lCJ2j5uXAlJsSwDe5J8WD7Co1f/hUlZvMfwfb5A github.com/jfrog/build-info-go v1.9.6/go.mod h1:GbuFS+viHCKZYx9nWHYu7ab1DgQkFdtVN3BJPUNb2D4= github.com/jfrog/gofrog v1.3.0 h1:o4zgsBZE4QyDbz2M7D4K6fXPTBJht+8lE87mS9bw7Gk= github.com/jfrog/gofrog v1.3.0/go.mod h1:IFMc+V/yf7rA5WZ74CSbXe+Lgf0iApEQLxRZVzKRUR0= +github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20230629184314-ff61ffffba34 h1:i1nENbEtG6uv5V92UacU6okTW3zcH1VfzmkvJn9nK7Y= +github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20230629184314-ff61ffffba34/go.mod h1:zc3la+URXmlEvrDhcY2g4AnnAQPOsmJisVfJZM06o+g= +github.com/jfrog/jfrog-client-go v1.28.1-0.20230629174113-81715f46ec0f h1:FzagM0DDnwhcOCR+/grBwrg625a8mwOdytnJTlGCkAI= +github.com/jfrog/jfrog-client-go v1.28.1-0.20230629174113-81715f46ec0f/go.mod h1:qEJxoe68sUtqHJ1YhXv/7pKYP/9p1D5tJrruzJKYeoI= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jszwec/csvutil v1.8.0 h1:G7vS2LGdpZZDH1HmHeNbxOaJ/ZnJlpwGFvOkTkJzzNk=