Skip to content

Commit aaff415

Browse files
authored
Enhancements to Release Bundle Create Command. (#2789)
1 parent aff64e3 commit aaff415

File tree

4 files changed

+207
-45
lines changed

4 files changed

+207
-45
lines changed

lifecycle/cli.go

+71-9
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ package lifecycle
22

33
import (
44
"errors"
5+
"fmt"
56
commonCliUtils "github.com/jfrog/jfrog-cli-core/v2/common/cliutils"
67
"github.com/jfrog/jfrog-cli-core/v2/common/commands"
78
"github.com/jfrog/jfrog-cli-core/v2/common/spec"
9+
speccore "github.com/jfrog/jfrog-cli-core/v2/common/spec"
810
coreCommon "github.com/jfrog/jfrog-cli-core/v2/docs/common"
911
"github.com/jfrog/jfrog-cli-core/v2/lifecycle"
1012
coreConfig "github.com/jfrog/jfrog-cli-core/v2/utils/config"
@@ -24,6 +26,7 @@ import (
2426
"github.com/jfrog/jfrog-client-go/utils"
2527
"github.com/jfrog/jfrog-client-go/utils/errorutils"
2628
"github.com/urfave/cli"
29+
"os"
2730
"strings"
2831
)
2932

@@ -131,21 +134,56 @@ func validateCreateReleaseBundleContext(c *cli.Context) error {
131134
}
132135

133136
func assertValidCreationMethod(c *cli.Context) error {
137+
// Determine the methods provided
134138
methods := []bool{
135-
c.IsSet("spec"), c.IsSet(cliutils.Builds), c.IsSet(cliutils.ReleaseBundles)}
136-
if coreutils.SumTrueValues(methods) > 1 {
137-
return errorutils.CheckErrorf("exactly one creation source must be supplied: --%s, --%s or --%s.\n"+
138-
"Opt to use the --%s option as the --%s and --%s are deprecated",
139+
c.IsSet("spec"),
140+
c.IsSet(cliutils.Builds),
141+
c.IsSet(cliutils.ReleaseBundles),
142+
}
143+
methodCount := coreutils.SumTrueValues(methods)
144+
145+
// Validate that only one creation method is provided
146+
if err := validateSingleCreationMethod(methodCount); err != nil {
147+
return err
148+
}
149+
150+
if err := validateCreationValuesPresence(c, methodCount); err != nil {
151+
return err
152+
}
153+
return nil
154+
}
155+
156+
func validateSingleCreationMethod(methodCount int) error {
157+
if methodCount > 1 {
158+
return errorutils.CheckErrorf(
159+
"exactly one creation source must be supplied: --%s, --%s, or --%s.\n"+
160+
"Opt to use the --%s option as the --%s and --%s are deprecated",
161+
"spec", cliutils.Builds, cliutils.ReleaseBundles,
139162
"spec", cliutils.Builds, cliutils.ReleaseBundles,
140-
"spec", cliutils.Builds, cliutils.ReleaseBundles)
163+
)
141164
}
142-
// If the user did not provide a source, we suggest only the recommended spec approach.
143-
if coreutils.SumTrueValues(methods) == 0 {
144-
return errorutils.CheckErrorf("the --spec option is mandatory")
165+
return nil
166+
}
167+
168+
func validateCreationValuesPresence(c *cli.Context, methodCount int) error {
169+
if methodCount == 0 {
170+
if !areBuildFlagsSet(c) && !areBuildEnvVarsSet() {
171+
return errorutils.CheckErrorf("Either --build-name or JFROG_CLI_BUILD_NAME, and --build-number or JFROG_CLI_BUILD_NUMBER must be defined")
172+
}
145173
}
146174
return nil
147175
}
148176

177+
// areBuildFlagsSet checks if build-name or build-number flags are set.
178+
func areBuildFlagsSet(c *cli.Context) bool {
179+
return c.IsSet(cliutils.BuildName) || c.IsSet(cliutils.BuildNumber)
180+
}
181+
182+
// areBuildEnvVarsSet checks if build environment variables are set.
183+
func areBuildEnvVarsSet() bool {
184+
return os.Getenv("JFROG_CLI_BUILD_NUMBER") != "" && os.Getenv("JFROG_CLI_BUILD_NAME") != ""
185+
}
186+
149187
func create(c *cli.Context) (err error) {
150188
if err = validateCreateReleaseBundleContext(c); err != nil {
151189
return err
@@ -169,10 +207,34 @@ func create(c *cli.Context) (err error) {
169207
}
170208

171209
func getReleaseBundleCreationSpec(c *cli.Context) (*spec.SpecFiles, error) {
210+
// Checking if the "builds" or "release-bundles" flags are set - if so, the spec flag should be ignored
211+
if c.IsSet(cliutils.Builds) || c.IsSet(cliutils.ReleaseBundles) {
212+
return nil, nil
213+
}
214+
215+
// Check if the "spec" flag is set - if so, return the spec
172216
if c.IsSet("spec") {
173217
return cliutils.GetSpec(c, true, false)
174218
}
175-
return nil, nil
219+
220+
// Else - create a spec from the buildName and buildnumber flags or env vars
221+
buildName := getStringFlagOrEnv(c, cliutils.BuildName, coreutils.BuildName)
222+
buildNumber := getStringFlagOrEnv(c, cliutils.BuildNumber, coreutils.BuildNumber)
223+
224+
if buildName != "" && buildNumber != "" {
225+
return speccore.CreateSpecFromBuildNameAndNumber(buildName, buildNumber)
226+
}
227+
228+
return nil, fmt.Errorf("either the --spec flag must be provided, " +
229+
"or both --build-name and --build-number flags (or their corresponding environment variables " +
230+
"JFROG_CLI_BUILD_NAME and JFROG_CLI_BUILD_NUMBER) must be set")
231+
}
232+
233+
func getStringFlagOrEnv(c *cli.Context, flag string, envVar string) string {
234+
if c.IsSet(flag) {
235+
return c.String(flag)
236+
}
237+
return os.Getenv(envVar)
176238
}
177239

178240
func promote(c *cli.Context) error {

lifecycle/cli_test.go

+73
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package lifecycle
22

33
import (
4+
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
45
"github.com/jfrog/jfrog-cli/utils/cliutils"
56
"github.com/jfrog/jfrog-cli/utils/tests"
7+
clientTestUtils "github.com/jfrog/jfrog-client-go/utils/tests"
68
"github.com/stretchr/testify/assert"
79
"path/filepath"
810
"testing"
@@ -56,3 +58,74 @@ func TestCreateReleaseBundleSpecWithProject(t *testing.T) {
5658
creationSpec.Get(0).Project = ""
5759
assert.Equal(t, projectKey, cliutils.GetProject(context))
5860
}
61+
62+
func TestGetReleaseBundleCreationSpec(t *testing.T) {
63+
64+
t.Run("Spec Flag Set", func(t *testing.T) {
65+
specFile := filepath.Join("testdata", "specfile.json")
66+
ctx, _ := tests.CreateContext(t, []string{"spec=" + specFile}, []string{})
67+
68+
spec, err := getReleaseBundleCreationSpec(ctx)
69+
70+
assert.NoError(t, err)
71+
assert.NotNil(t, spec)
72+
})
73+
74+
t.Run("Build Name and Number Set via Flags", func(t *testing.T) {
75+
ctx, _ := tests.CreateContext(t, []string{"build-name=Common-builds", "build-number=1.0.0"}, []string{})
76+
77+
spec, err := getReleaseBundleCreationSpec(ctx)
78+
79+
assert.NoError(t, err)
80+
assert.NotNil(t, spec)
81+
assert.Equal(t, "Common-builds/1.0.0", spec.Files[0].Build)
82+
})
83+
84+
t.Run("Build Name and Number Set via Env Variables", func(t *testing.T) {
85+
86+
setEnvBuildNameCallBack := clientTestUtils.SetEnvWithCallbackAndAssert(t, coreutils.BuildName, "Common-builds")
87+
defer setEnvBuildNameCallBack()
88+
setEnvBuildNumberCallBack := clientTestUtils.SetEnvWithCallbackAndAssert(t, coreutils.BuildNumber, "2.0.0")
89+
defer setEnvBuildNumberCallBack()
90+
91+
ctx, _ := tests.CreateContext(t, []string{}, []string{})
92+
93+
spec, err := getReleaseBundleCreationSpec(ctx)
94+
95+
assert.NoError(t, err)
96+
assert.NotNil(t, spec)
97+
assert.Equal(t, "Common-builds/2.0.0", spec.Files[0].Build)
98+
})
99+
100+
t.Run("Missing Build Name and Number", func(t *testing.T) {
101+
ctx, _ := tests.CreateContext(t, []string{}, []string{})
102+
103+
spec, err := getReleaseBundleCreationSpec(ctx)
104+
105+
assert.Error(t, err)
106+
assert.Nil(t, spec)
107+
assert.EqualError(t, err, "either the --spec flag must be provided, or both --build-name and --build-number flags (or their corresponding environment variables JFROG_CLI_BUILD_NAME and JFROG_CLI_BUILD_NUMBER) must be set")
108+
})
109+
110+
t.Run("Only One Build Variable Set", func(t *testing.T) {
111+
ctx, _ := tests.CreateContext(t, []string{"build-name=Common-builds"}, []string{})
112+
113+
spec, err := getReleaseBundleCreationSpec(ctx)
114+
115+
assert.Error(t, err)
116+
assert.Nil(t, spec)
117+
assert.EqualError(t, err, "either the --spec flag must be provided, or both --build-name and --build-number flags (or their corresponding environment variables JFROG_CLI_BUILD_NAME and JFROG_CLI_BUILD_NUMBER) must be set")
118+
})
119+
120+
t.Run("One Env Variable One Flag", func(t *testing.T) {
121+
ctx, _ := tests.CreateContext(t, []string{"build-name=Common-builds"}, []string{})
122+
setEnvBuildNumberCallBack := clientTestUtils.SetEnvWithCallbackAndAssert(t, coreutils.BuildNumber, "2.0.0")
123+
defer setEnvBuildNumberCallBack()
124+
125+
spec, err := getReleaseBundleCreationSpec(ctx)
126+
127+
assert.NoError(t, err)
128+
assert.NotNil(t, spec)
129+
assert.Equal(t, "Common-builds/2.0.0", spec.Files[0].Build)
130+
})
131+
}

lifecycle_test.go

+33-6
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,6 @@ func TestLifecycleFullFlow(t *testing.T) {
167167
// Verify the artifacts were distributed correctly by the provided path mappings.
168168
assertExpectedArtifacts(t, tests.SearchAllDevRepo, tests.GetExpectedLifecycleDistributedArtifacts())
169169
*/
170-
171170
}
172171

173172
// Import bundles only work on onPerm platforms
@@ -206,30 +205,58 @@ func uploadBuilds(t *testing.T) func() {
206205
func createRbBackwardCompatible(t *testing.T, specName, sourceOption, rbName, rbVersion string, sync bool) {
207206
specFile, err := getSpecFile(specName)
208207
assert.NoError(t, err)
209-
createRb(t, specFile, sourceOption, rbName, rbVersion, sync, false)
208+
createRbWithFlags(t, specFile, sourceOption, "", "", rbName, rbVersion, sync, false)
210209
}
211210

212211
func createRbFromSpec(t *testing.T, specName, rbName, rbVersion string, sync bool, withoutSigningKey bool) {
213212
specFile, err := tests.CreateSpec(specName)
214213
assert.NoError(t, err)
215-
createRb(t, specFile, "spec", rbName, rbVersion, sync, withoutSigningKey)
214+
createRbWithFlags(t, specFile, "spec", "", "", rbName, rbVersion, sync, withoutSigningKey)
216215
}
217216

218-
func createRb(t *testing.T, specFilePath, sourceOption, rbName, rbVersion string, sync bool, withoutSigningKey bool) {
217+
func TestCreateBundleWithoutSpec(t *testing.T) {
218+
cleanCallback := initLifecycleTest(t, signingKeyOptionalArtifactoryMinVersion)
219+
defer cleanCallback()
220+
221+
lcManager := getLcServiceManager(t)
222+
223+
deleteBuilds := uploadBuilds(t)
224+
defer deleteBuilds()
225+
226+
createRbWithFlags(t, "", "", tests.LcBuildName1, number1, tests.LcRbName1, number1, false, false)
227+
assertStatusCompleted(t, lcManager, tests.LcRbName1, number1, "")
228+
defer deleteReleaseBundle(t, lcManager, tests.LcRbName1, number1)
229+
230+
createRbWithFlags(t, "", "", tests.LcBuildName2, number2, tests.LcRbName2, number2, false, true)
231+
assertStatusCompleted(t, lcManager, tests.LcRbName2, number2, "")
232+
defer deleteReleaseBundle(t, lcManager, tests.LcRbName2, number2)
233+
}
234+
235+
func createRbWithFlags(t *testing.T, specFilePath, sourceOption, buildName, buildNumber, rbName, rbVersion string,
236+
sync, withoutSigningKey bool) {
219237
argsAndOptions := []string{
220238
"rbc",
221239
rbName,
222240
rbVersion,
223-
getOption(sourceOption, specFilePath),
241+
}
242+
243+
if specFilePath != "" {
244+
argsAndOptions = append(argsAndOptions, getOption(sourceOption, specFilePath))
245+
}
246+
247+
if buildName != "" && buildNumber != "" {
248+
argsAndOptions = append(argsAndOptions, getOption(cliutils.BuildName, buildName))
249+
argsAndOptions = append(argsAndOptions, getOption(cliutils.BuildNumber, buildNumber))
224250
}
225251

226252
if !withoutSigningKey {
227253
argsAndOptions = append(argsAndOptions, getOption(cliutils.SigningKey, gpgKeyPairName))
228254
}
229-
// Add the --sync option only if requested, to test the default value.
255+
230256
if sync {
231257
argsAndOptions = append(argsAndOptions, getOption(cliutils.Sync, "true"))
232258
}
259+
233260
assert.NoError(t, lcCli.Exec(argsAndOptions...))
234261
}
235262

0 commit comments

Comments
 (0)