Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions apptrust/commands/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const (
OverwriteStrategyFlag = "overwrite-strategy"
TagFlag = "tag"
DraftFlag = "draft"
SkipUnassignedFlag = "skip-unassigned"
SourceTypeBuildsFlag = "source-type-builds"
SourceTypeReleaseBundlesFlag = "source-type-release-bundles"
SourceTypeApplicationVersionsFlag = "source-type-application-versions"
Expand Down Expand Up @@ -94,6 +95,7 @@ var flagsMap = map[string]components.Flag{
OverwriteStrategyFlag: components.NewStringFlag(OverwriteStrategyFlag, "Strategy for handling target artifacts with the same path but different checksum. Supported values: "+coreutils.ListToText(model.OverwriteStrategyValues)+".", func(f *components.StringFlag) { f.Mandatory = false }),
TagFlag: components.NewStringFlag(TagFlag, "A tag to associate with the version. Must contain only alphanumeric characters, hyphens (-), underscores (_), and dots (.).", func(f *components.StringFlag) { f.Mandatory = false }),
DraftFlag: components.NewBoolFlag(DraftFlag, "Create the application version as a draft.", components.WithBoolDefaultValueFalse()),
SkipUnassignedFlag: components.NewBoolFlag(SkipUnassignedFlag, "Automatically promote the new version to the first lifecycle stage when all of its source artifacts reside in repositories mapped to that stage. Otherwise the version is left unassigned and a message explaining why is returned.", components.WithBoolDefaultValueFalse()),
SourceTypeBuildsFlag: components.NewStringFlag(SourceTypeBuildsFlag, "List of semicolon-separated (;) builds in the form of 'name=buildName1, id=runID1[, include-deps=true][, repo-key=repo1][, started=2023-01-01T12:34:56.789+0100]; name=buildName2, id=runID2[, include-deps=true][, repo-key=repo2][, started=2023-01-01T12:34:56.789+0100]' to be included in the new version.", func(f *components.StringFlag) { f.Mandatory = false }),
SourceTypeReleaseBundlesFlag: components.NewStringFlag(SourceTypeReleaseBundlesFlag, "List of semicolon-separated (;) release bundles in the form of 'name=releaseBundleName1, version=version1[, project-key=project1][, repo-key=repo1]; name=releaseBundleName2, version=version2[, project-key=project2][, repo-key=repo2]' to be included in the new version.", func(f *components.StringFlag) { f.Mandatory = false }),
SourceTypeApplicationVersionsFlag: components.NewStringFlag(SourceTypeApplicationVersionsFlag, "List of semicolon-separated (;) application versions in the form of 'application-key=app1, version=version1; application-key=app2, version=version2' to be included in the new version.", func(f *components.StringFlag) { f.Mandatory = false }),
Expand All @@ -114,6 +116,7 @@ var commandFlags = map[string][]string{
SyncFlag,
TagFlag,
DraftFlag,
SkipUnassignedFlag,
SourceTypeBuildsFlag,
SourceTypeReleaseBundlesFlag,
SourceTypeApplicationVersionsFlag,
Expand Down
1 change: 1 addition & 0 deletions apptrust/commands/version/create_app_version_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ func (cv *createAppVersionCommand) buildRequestPayload(ctx *components.Context)
Sources: sources,
Tag: ctx.GetStringFlagValue(commands.TagFlag),
Draft: ctx.GetBoolFlagValue(commands.DraftFlag),
SkipUnassigned: ctx.GetBoolFlagValue(commands.SkipUnassignedFlag),
Filters: filters,
}, nil
}
Expand Down
18 changes: 18 additions & 0 deletions apptrust/commands/version/create_app_version_cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,24 @@ func TestCreateAppVersionCommand_FlagsSuite(t *testing.T) {
},
},
},
{
name: "skip-unassigned flag",
ctxSetup: func(ctx *components.Context) {
ctx.Arguments = []string{"app-key", "1.0.0"}
ctx.AddBoolFlag(commands.SkipUnassignedFlag, true)
ctx.AddStringFlag(commands.SourceTypePackagesFlag, "type=npm,name=pkg1,version=1.0.0,repo-key=repo1")
},
expectsPayload: &model.CreateAppVersionRequest{
ApplicationKey: "app-key",
Version: "1.0.0",
SkipUnassigned: true,
Sources: &model.CreateVersionSources{
Packages: []model.CreateVersionPackage{
{Type: "npm", Name: "pkg1", Version: "1.0.0", Repository: "repo1"},
},
},
},
},
{
name: "spec only",
ctxSetup: func(ctx *components.Context) {
Expand Down
1 change: 1 addition & 0 deletions apptrust/common/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ var OrderedAppVersionKeys = []string{
"status",
"current_stage",
"tag",
"message",
}

// OrderedAppKeys defines the display order for application table output
Expand Down
1 change: 1 addition & 0 deletions apptrust/model/create_app_version_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ type CreateAppVersionRequest struct {
Sources *CreateVersionSources `json:"sources,omitempty"`
Tag string `json:"tag,omitempty"`
Draft bool `json:"draft,omitempty"`
SkipUnassigned bool `json:"skip_unassigned,omitempty"`
Filters *CreateVersionFilters `json:"filters,omitempty"`
}

Expand Down
19 changes: 19 additions & 0 deletions e2e/utils/artifactory_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,23 @@ func uploadPackageToArtifactory(t *testing.T, repoKey, buildName, buildNumber st
return artifactDetails.Checksums.Sha256
}

func CreateGenericRepoWithEnv(t *testing.T, suffix string, environments []string) string {
servicesManager := getArtifactoryServicesManager(t)
repoKey := GetTestProjectKey(t) + "-" + suffix
localRepoConfig := services.NewGenericLocalRepositoryParams()
localRepoConfig.ProjectKey = GetTestProjectKey(t)
localRepoConfig.Key = repoKey
localRepoConfig.Environments = environments
err := servicesManager.CreateLocalRepository().Generic(localRepoConfig)
require.NoError(t, err)
t.Cleanup(func() { _ = servicesManager.DeleteRepository(repoKey) })
return repoKey
}

func UploadTestArtifact(t *testing.T, repoKey, fileName string) string {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why creating this function instead of just making uploadSimpleFileToArtifactory public?

return uploadSimpleFileToArtifactory(t, repoKey, fileName)
}

func uploadSimpleFileToArtifactory(t *testing.T, repoKey, targetFileName string) string {
tmpFile, err := os.CreateTemp("", "e2e-artifact-*.txt")
require.NoError(t, err)
Expand All @@ -147,6 +164,8 @@ func uploadSimpleFileToArtifactory(t *testing.T, repoKey, targetFileName string)
err = summary.Close()
require.NoError(t, err)

reindexRepo(t, repoKey)

return targetPath
}

Expand Down
60 changes: 60 additions & 0 deletions e2e/version_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,66 @@ func TestCreateVersion_Draft(t *testing.T) {
assert.Equal(t, utils.StatusDraft, versionContent.Status)
}

type skipUnassignedResponse struct {
ApplicationKey string `json:"application_key"`
Version string `json:"version"`
Status string `json:"status"`
CurrentStage string `json:"current_stage"`
Message string `json:"message"`
}

func TestCreateVersion_SkipUnassigned(t *testing.T) {
appKey := utils.GenerateUniqueKey("app-version-skip-unassigned")
utils.CreateBasicApplication(t, appKey)
defer utils.DeleteApplication(t, appKey)

t.Run("auto-promotes when source repo is mapped to first stage", func(t *testing.T) {
version := utils.GenerateUniqueKey("skip-ua-ok")

testPackage := utils.GetTestPackage(t)
packageFlag := fmt.Sprintf("--source-type-packages=type=%s, name=%s, version=%s, repo-key=%s",
testPackage.PackageType, testPackage.PackageName, testPackage.PackageVersion, testPackage.RepoKey)
output := utils.AppTrustCli.RunCliCmdWithOutput(t, "version-create", appKey, version, packageFlag, "--skip-unassigned", "--sync")
defer utils.DeleteApplicationVersion(t, appKey, version)

require.NotEmpty(t, output)

var response skipUnassignedResponse
err := json.Unmarshal([]byte(output), &response)
require.NoError(t, err, "failed to parse CLI output as JSON: %s", output)
assert.Equal(t, appKey, response.ApplicationKey)
assert.Equal(t, version, response.Version)
assert.Empty(t, response.Message, "No message means auto-promote succeeded")

versionContent, statusCode, err := utils.GetApplicationVersion(appKey, version)
require.NoError(t, err)
assert.Equal(t, http.StatusOK, statusCode)
require.NotNil(t, versionContent)
assert.Equal(t, "DEV", versionContent.CurrentStage, "Version should be auto-promoted to DEV stage")
})

t.Run("stays unassigned with message when artifact not in first stage", func(t *testing.T) {
version := utils.GenerateUniqueKey("skip-ua-fail")

repoKey := utils.CreateGenericRepoWithEnv(t, "prod-only-local", []string{"PROD"})
artifactPath := utils.UploadTestArtifact(t, repoKey, "mismatch-artifact.txt")

artifactFlag := fmt.Sprintf("--source-type-artifacts=path=%s", artifactPath)
output := utils.AppTrustCli.RunCliCmdWithOutput(t, "version-create", appKey, version, artifactFlag, "--skip-unassigned", "--sync")
defer utils.DeleteApplicationVersion(t, appKey, version)

require.NotEmpty(t, output)

var response skipUnassignedResponse
err := json.Unmarshal([]byte(output), &response)
require.NoError(t, err, "failed to parse CLI output as JSON: %s", output)
assert.Equal(t, appKey, response.ApplicationKey)
assert.Equal(t, version, response.Version)
require.NotEmpty(t, response.Message, "A message should explain why auto-promotion did not occur")
assert.Contains(t, response.Message, "not all source artifacts are in repositories mapped to the first stage")
})
}

func TestCreateVersion_Async(t *testing.T) {
appKey := utils.GenerateUniqueKey("app-version-create-async")
utils.CreateBasicApplication(t, appKey)
Expand Down
Loading