Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
2 changes: 2 additions & 0 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cli
import (
distributionCLI "github.com/jfrog/jfrog-cli-artifactory/distribution/cli"
evidenceCLI "github.com/jfrog/jfrog-cli-artifactory/evidence/cli"
"github.com/jfrog/jfrog-cli-artifactory/lifecycle"
"github.com/jfrog/jfrog-cli-core/v2/common/cliutils"
"github.com/jfrog/jfrog-cli-core/v2/plugins/components"
)
Expand All @@ -24,5 +25,6 @@ func GetJfrogCliArtifactoryApp() components.App {
Commands: evidenceCLI.GetCommands(),
Category: "Command Namespaces",
})
app.Commands = append(app.Commands, lifecycle.GetCommands()...)
return app
}
19 changes: 19 additions & 0 deletions cliutils/cmddefs/commands.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package cmddefs

const (
// Distribution V1 Commands
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"

// Lifecycle Commands
ReleaseBundleCreate = "release-bundle-create"
ReleaseBundlePromote = "release-bundle-promote"
ReleaseBundleDistribute = "release-bundle-distribute"
ReleaseBundleDeleteLocal = "release-bundle-delete-local"
ReleaseBundleDeleteRemote = "release-bundle-delete-remote"
ReleaseBundleExport = "release-bundle-export"
ReleaseBundleImport = "release-bundle-import"
)
52 changes: 52 additions & 0 deletions cliutils/distribution/distribute.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package distribution

import (
"github.com/jfrog/jfrog-cli-core/v2/common/spec"
pluginsCommon "github.com/jfrog/jfrog-cli-core/v2/plugins/common"
"github.com/jfrog/jfrog-cli-core/v2/plugins/components"
distributionUtils "github.com/jfrog/jfrog-client-go/utils/distribution"
)

func CreateDefaultDistributionRules(c *components.Context) *spec.DistributionRules {
return &spec.DistributionRules{
DistributionRules: []spec.DistributionRule{{
SiteName: c.GetStringFlagValue("site"),
CityName: c.GetStringFlagValue("city"),
CountryCodes: pluginsCommon.GetStringsArrFlagValue(c, "country-codes"),
}},
}
}

func ValidateReleaseBundleDistributeCmd(c *components.Context) error {
if len(c.Arguments) != 2 {
return pluginsCommon.WrongNumberOfArgumentsHandler(c)
}
if c.IsFlagSet("max-wait-minutes") && !c.IsFlagSet("sync") {
return pluginsCommon.PrintHelpAndReturnError("The --max-wait-minutes option can't be used without --sync", c)
}

if c.IsFlagSet("dist-rules") && (c.IsFlagSet("site") || c.IsFlagSet("city") || c.IsFlagSet("country-code")) {
return pluginsCommon.PrintHelpAndReturnError("The --dist-rules option can't be used with --site, --city or --country-code", c)
}

return nil
}

func InitReleaseBundleDistributeCmd(c *components.Context) (distributionRules *spec.DistributionRules, maxWaitMinutes int, params distributionUtils.DistributionParams, err error) {
if c.IsFlagSet("dist-rules") {
distributionRules, err = spec.CreateDistributionRulesFromFile(c.GetStringFlagValue("dist-rules"))
if err != nil {
return
}
} else {
distributionRules = CreateDefaultDistributionRules(c)
}

maxWaitMinutes, err = c.GetDefaultIntFlagValueIfNotSet("max-wait-minutes", 60)
if err != nil {
return
}

params = distributionUtils.NewDistributeReleaseBundleParams(c.Arguments[0], c.Arguments[1])
return
}
201 changes: 201 additions & 0 deletions cliutils/flagkit/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
package flagkit
Copy link
Collaborator

Choose a reason for hiding this comment

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

Since this is only used by distribution / lifecycle commands, I suggest placing it under a non common package.

Copy link
Collaborator

Choose a reason for hiding this comment

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

In addition, please remove all unused flags from jfrog-cli

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

it is only used distribution, lifecycle since we have moved only those 2 categories for now, all command flags including artifactory or any other types if moved in future can be placed here hence the common package.

Removed unused flags from jfrg-cli


import (
"github.com/jfrog/jfrog-cli-artifactory/cliutils/cmddefs"
pluginsCommon "github.com/jfrog/jfrog-cli-core/v2/plugins/common"
"github.com/jfrog/jfrog-cli-core/v2/plugins/components"
"strconv"
)

const (
distUrl = "dist-url"
DownloadMinSplitKb = 5120
DownloadSplitCount = 3

// Unique release-bundle-* v1 flags
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"
deleteFromDist = "delete-from-dist"

// Common release-bundle-* v1&v2 flags
DistRules = "dist-rules"
site = "site"
city = "city"
countryCodes = "country-codes"
sync = "sync"
maxWaitMinutes = "max-wait-minutes"
CreateRepo = "create-repo"

// Base flags
platformUrl = "platform-url"
user = "user"
password = "password"
accessToken = "access-token"
serverId = "server-id"
url = "url"

// Client certification flags
InsecureTls = "insecure-tls"

// Spec flags
specFlag = "spec"
specVars = "spec-vars"

// Generic commands flags
exclusions = "exclusions"
dryRun = "dry-run"
targetProps = "target-props"
quiet = "quiet"
detailedSummary = "detailed-summary"
deletePrefix = "delete-"
deleteQuiet = deletePrefix + quiet
repo = "repo"
target = "target"
name = "name"
passphrase = "passphrase"
Project = "project"
IncludeRepos = "include-repos"
ExcludeRepos = "exclude-repos"

// Unique lifecycle flags
Sync = "sync"
lifecyclePrefix = "lc-"
lcSync = lifecyclePrefix + Sync
lcProject = lifecyclePrefix + Project
Builds = "builds"
lcBuilds = lifecyclePrefix + Builds
ReleaseBundles = "release-bundles"
lcReleaseBundles = lifecyclePrefix + ReleaseBundles
SigningKey = "signing-key"
lcSigningKey = lifecyclePrefix + SigningKey
PathMappingPattern = "mapping-pattern"
lcPathMappingPattern = lifecyclePrefix + PathMappingPattern
PathMappingTarget = "mapping-target"
lcPathMappingTarget = lifecyclePrefix + PathMappingTarget
lcDryRun = lifecyclePrefix + dryRun
lcIncludeRepos = lifecyclePrefix + IncludeRepos
lcExcludeRepos = lifecyclePrefix + ExcludeRepos
setupRepo = repo

// Build Info flags
BuildName = "build-name"
BuildNumber = "build-number"

// Unique Download Flags
downloadPrefix = "download-"
downloadMinSplit = downloadPrefix + MinSplit
downloadSplitCount = downloadPrefix + SplitCount

// Generic Command Flags
MinSplit = "min-split"
SplitCount = "split-count"
)

var commandFlags = map[string][]string{
cmddefs.ReleaseBundleV1Create: {
distUrl, user, password, accessToken, serverId, specFlag, specVars, targetProps,
rbDryRun, sign, desc, exclusions, releaseNotesPath, releaseNotesSyntax, rbPassphrase, rbRepo, InsecureTls, distTarget, rbDetailedSummary,
},
cmddefs.ReleaseBundleV1Update: {
distUrl, user, password, accessToken, serverId, specFlag, specVars, targetProps,
rbDryRun, sign, desc, exclusions, releaseNotesPath, releaseNotesSyntax, rbPassphrase, rbRepo, InsecureTls, distTarget, rbDetailedSummary,
},
cmddefs.ReleaseBundleV1Sign: {
distUrl, user, password, accessToken, serverId, rbPassphrase, rbRepo,
InsecureTls, rbDetailedSummary,
},
cmddefs.ReleaseBundleV1Distribute: {
distUrl, user, password, accessToken, serverId, rbDryRun, DistRules,
site, city, countryCodes, sync, maxWaitMinutes, InsecureTls, CreateRepo,
},
cmddefs.ReleaseBundleV1Delete: {
distUrl, user, password, accessToken, serverId, rbDryRun, DistRules,
site, city, countryCodes, sync, maxWaitMinutes, InsecureTls, deleteFromDist, deleteQuiet,
},
cmddefs.ReleaseBundleCreate: {
platformUrl, user, password, accessToken, serverId, lcSigningKey, lcSync, lcProject, lcBuilds, lcReleaseBundles,
specFlag, specVars, BuildName, BuildNumber,
},
cmddefs.ReleaseBundlePromote: {
platformUrl, user, password, accessToken, serverId, lcSigningKey, lcSync, lcProject, lcIncludeRepos, lcExcludeRepos,
},
cmddefs.ReleaseBundleDistribute: {
platformUrl, user, password, accessToken, serverId, lcProject, DistRules, site, city, countryCodes,
lcDryRun, CreateRepo, lcPathMappingPattern, lcPathMappingTarget, lcSync, maxWaitMinutes,
},
cmddefs.ReleaseBundleDeleteLocal: {
platformUrl, user, password, accessToken, serverId, deleteQuiet, lcSync, lcProject,
},
cmddefs.ReleaseBundleDeleteRemote: {
platformUrl, user, password, accessToken, serverId, deleteQuiet, lcDryRun, DistRules, site, city, countryCodes,
lcSync, maxWaitMinutes, lcProject,
},
cmddefs.ReleaseBundleExport: {
platformUrl, user, password, accessToken, serverId, lcPathMappingTarget, lcPathMappingPattern, Project,
downloadMinSplit, downloadSplitCount,
},
cmddefs.ReleaseBundleImport: {
user, password, accessToken, serverId, platformUrl,
},
}

var flagsMap = map[string]components.Flag{
distUrl: components.NewStringFlag(url, "JFrog Distribution URL. (example: https://acme.jfrog.io/distribution)", components.SetMandatoryFalse()),
user: components.NewStringFlag(user, "JFrog username.", components.SetMandatoryFalse()),
password: components.NewStringFlag(password, "JFrog password.", components.SetMandatoryFalse()),
accessToken: components.NewStringFlag(accessToken, "JFrog access token.", components.SetMandatoryFalse()),
serverId: components.NewStringFlag(serverId, "Server ID configured using the 'jf config' command.", components.SetMandatoryFalse()),
specFlag: components.NewStringFlag(specFlag, "Path to a File Spec.", components.SetMandatoryFalse()),
specVars: components.NewStringFlag(specVars, "List of semicolon-separated(;) variables in the form of \"key1=value1;key2=value2;...\" to be replaced in the File Spec.", components.SetMandatoryFalse()),
targetProps: components.NewStringFlag(targetProps, "List of semicolon-separated(;) properties, in the form of \"key1=value1;key2=value2;...\" to be added to the artifacts after distribution of the release bundle.", components.SetMandatoryFalse()),
rbDryRun: components.NewBoolFlag(dryRun, "Set to true to disable communication with JFrog Distribution.", components.WithBoolDefaultValueFalse()),
sign: components.NewBoolFlag(sign, "If set to true, automatically signs the release bundle version.", components.WithBoolDefaultValueFalse()),
desc: components.NewStringFlag(desc, "Description of the release bundle.", components.SetMandatoryFalse()),
exclusions: components.NewStringFlag(exclusions, "List of semicolon-separated(;) exclusions. Exclusions can include the * and the ? wildcards.", components.SetMandatoryFalse()),
releaseNotesPath: components.NewStringFlag(releaseNotesPath, "Path to a file describes the release notes for the release bundle version.", components.SetMandatoryFalse()),
releaseNotesSyntax: components.NewStringFlag(releaseNotesSyntax, "The syntax for the release notes. Can be one of 'markdown', 'asciidoc', or 'plain_text.", components.SetMandatoryFalse()),
rbPassphrase: components.NewStringFlag(passphrase, "The passphrase for the signing key.", components.SetMandatoryFalse()),
rbRepo: components.NewStringFlag(repo, "A repository name at source Artifactory to store release bundle artifacts in. If not provided, Artifactory will use the default one.", components.SetMandatoryFalse()),
InsecureTls: components.NewBoolFlag(InsecureTls, "Set to true to skip TLS certificates verification.", components.WithBoolDefaultValueFalse()),
distTarget: components.NewStringFlag(target, "The target path for distributed artifacts on the edge node.", components.SetMandatoryFalse()),
rbDetailedSummary: components.NewBoolFlag(detailedSummary, "Set to true to get a command summary with details about the release bundle artifact.", components.WithBoolDefaultValueFalse()),
DistRules: components.NewStringFlag(DistRules, "Path to distribution rules.", components.SetMandatoryFalse()),
site: components.NewStringFlag(site, "Wildcard filter for site name.", components.SetMandatoryFalse()),
city: components.NewStringFlag(city, "Wildcard filter for site city name.", components.SetMandatoryFalse()),
countryCodes: components.NewStringFlag(countryCodes, "List of semicolon-separated(;) wildcard filters for site country codes.", components.SetMandatoryFalse()),
sync: components.NewBoolFlag(sync, "Set to true to enable sync distribution (the command execution will end when the distribution process ends).", components.WithBoolDefaultValueFalse()),
maxWaitMinutes: components.NewStringFlag(maxWaitMinutes, "Max minutes to wait for sync distribution.", components.WithStrDefaultValue("60")),
deleteFromDist: components.NewBoolFlag(deleteFromDist, "Set to true to delete release bundle version in JFrog Distribution itself after deletion is complete.", components.WithBoolDefaultValueFalse()),
deleteQuiet: components.NewBoolFlag(quiet, "Set to true to skip the delete confirmation message.", components.WithBoolDefaultValueFalse()),
CreateRepo: components.NewBoolFlag(CreateRepo, "Set to true to create the repository on the edge if it does not exist.", components.WithBoolDefaultValueFalse()),
lcSync: components.NewBoolFlag(Sync, "Set to false to run asynchronously.", components.WithBoolDefaultValueTrue()),
lcProject: components.NewStringFlag(Project, "Project key associated with the Release Bundle version.", components.SetMandatoryFalse()),
lcBuilds: components.NewStringFlag(Builds, "Path to a JSON file containing information of the source builds from which to create a release bundle.", components.WithHiddenTrue(), components.SetMandatoryFalse()),
lcReleaseBundles: components.NewStringFlag(ReleaseBundles, "Path to a JSON file containing information of the source release bundles from which to create a release bundle.", components.WithHiddenTrue(), components.SetMandatoryFalse()),
lcSigningKey: components.NewStringFlag(SigningKey, "The GPG/RSA key-pair name given in Artifactory. If the key isn't provided, the command creates or uses the default key.", components.SetMandatoryFalse()),
lcPathMappingPattern: components.NewStringFlag(PathMappingPattern, "Specify along with "+PathMappingTarget+" to distribute artifacts to a different path on the edge node. You can use wildcards to specify multiple artifacts.", components.SetMandatoryFalse()),
lcPathMappingTarget: components.NewStringFlag(PathMappingTarget, "The target path for distributed artifacts on the edge node. If not specified, the artifacts will have the same path and name on the edge node, as on the source Artifactory server. "+
"For flexibility in specifying the distribution path, you can include placeholders in the form of {1}, {2} which are replaced by corresponding tokens in the pattern path that are enclosed in parenthesis.` `", components.SetMandatoryFalse()),
lcDryRun: components.NewBoolFlag(dryRun, "Set to true to only simulate the distribution of the release bundle.", components.WithBoolDefaultValueFalse()),
lcIncludeRepos: components.NewStringFlag(IncludeRepos, "List of semicolon-separated(;) repositories to include in the promotion. If this property is left undefined, all repositories (except those specifically excluded) are included in the promotion. "+
"If one or more repositories are specifically included, all other repositories are excluded.` `", components.SetMandatoryFalse()),
lcExcludeRepos: components.NewStringFlag(ExcludeRepos, "List of semicolon-separated(;) repositories to exclude from the promotion.` `", components.SetMandatoryFalse()),
platformUrl: components.NewStringFlag(url, "JFrog platform URL. (example: https://acme.jfrog.io)` `", components.SetMandatoryFalse()),
downloadMinSplit: components.NewStringFlag(MinSplit, "[Default: "+strconv.Itoa(DownloadMinSplitKb)+"] Minimum file size in KB to split into ranges when downloading. Set to -1 for no splits.` `"),
downloadSplitCount: components.NewStringFlag(SplitCount, "[Default: "+strconv.Itoa(DownloadSplitCount)+"] Number of parts to split a file when downloading. Set to 0 for no splits.` `"),
Project: components.NewStringFlag(Project, "JFrog Artifactory project key.` `", components.SetMandatoryFalse()),
BuildName: components.NewStringFlag(BuildName, "Providing this option will collect and record build info for this build name. Build number option is mandatory when this option is provided.` `", components.SetMandatoryFalse()),
BuildNumber: components.NewStringFlag(BuildNumber, "Providing this option will collect and record build info for this build number. Build name option is mandatory when this option is provided.` `", components.SetMandatoryFalse()),
}

func GetCommandFlags(cmdKey string) []components.Flag {
return pluginsCommon.GetCommandFlags(cmdKey, commandFlags, flagsMap)
}
23 changes: 23 additions & 0 deletions cliutils/flagkit/flagskit_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package flagkit

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestCommandFlags(t *testing.T) {
for cmdKey, flags := range commandFlags {
t.Run(cmdKey, func(t *testing.T) {
assert.Equal(t, len(flags), len(commandFlags[cmdKey]), "Number of flags mismatch for command: %s", cmdKey)

for _, flag := range flags {
_, exists := flagsMap[flag]
if !exists {
t.Logf("Flag %s not found in flagsMap for command: %s", flag, cmdKey)
}
assert.True(t, exists, "Flag %s not found in flagsMap for command: %s", flag, cmdKey)
}
})
}
}
12 changes: 7 additions & 5 deletions distribution/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package cli

import (
"errors"
"github.com/jfrog/jfrog-cli-artifactory/cliutils/cmddefs"
"github.com/jfrog/jfrog-cli-artifactory/cliutils/flagkit"
distributionCommands "github.com/jfrog/jfrog-cli-artifactory/distribution/commands"
"github.com/jfrog/jfrog-cli-artifactory/distribution/docs/releasebundlecreate"
"github.com/jfrog/jfrog-cli-artifactory/distribution/docs/releasebundledelete"
Expand Down Expand Up @@ -29,7 +31,7 @@ func GetCommands() []components.Command {
return []components.Command{
{
Name: "release-bundle-create",
Flags: GetCommandFlags(ReleaseBundleV1Create),
Flags: flagkit.GetCommandFlags(cmddefs.ReleaseBundleV1Create),
Aliases: []string{"rbc"},
Description: releasebundlecreate.GetDescription(),
Arguments: releasebundlecreate.GetArguments(),
Expand All @@ -38,7 +40,7 @@ func GetCommands() []components.Command {
},
{
Name: "release-bundle-update",
Flags: GetCommandFlags(ReleaseBundleV1Update),
Flags: flagkit.GetCommandFlags(cmddefs.ReleaseBundleV1Update),
Aliases: []string{"rbu"},
Description: releasebundleupdate.GetDescription(),
Arguments: releasebundleupdate.GetArguments(),
Expand All @@ -47,7 +49,7 @@ func GetCommands() []components.Command {
},
{
Name: "release-bundle-sign",
Flags: GetCommandFlags(ReleaseBundleV1Sign),
Flags: flagkit.GetCommandFlags(cmddefs.ReleaseBundleV1Sign),
Aliases: []string{"rbs"},
Description: releasebundlesign.GetDescription(),
Arguments: releasebundlesign.GetArguments(),
Expand All @@ -56,7 +58,7 @@ func GetCommands() []components.Command {
},
{
Name: "release-bundle-distribute",
Flags: GetCommandFlags(ReleaseBundleV1Distribute),
Flags: flagkit.GetCommandFlags(cmddefs.ReleaseBundleV1Distribute),
Aliases: []string{"rbd"},
Description: releasebundledistribute.GetDescription(),
Arguments: releasebundledistribute.GetArguments(),
Expand All @@ -65,7 +67,7 @@ func GetCommands() []components.Command {
},
{
Name: "release-bundle-delete",
Flags: GetCommandFlags(ReleaseBundleV1Delete),
Flags: flagkit.GetCommandFlags(cmddefs.ReleaseBundleV1Delete),
Aliases: []string{"rbdel"},
Description: releasebundledelete.GetDescription(),
Arguments: releasebundledelete.GetArguments(),
Expand Down
Loading
Loading