Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b939cc2
Distribute and remotely delete application versions
asafgabai Jul 5, 2026
9edd562
Distribute and remotely delete application versions
asafgabai Jul 5, 2026
2c88c20
Distribute and remotely delete application versions
asafgabai Jul 5, 2026
8d75c98
Distribute and remotely delete application versions
asafgabai Jul 5, 2026
8e265e5
Distribute and remotely delete application versions
asafgabai Jul 5, 2026
7818e7a
Distribute and remotely delete application versions
asafgabai Jul 6, 2026
15331d9
Distribute and remotely delete application versions
asafgabai Jul 6, 2026
0ba6d1e
Distribute and remotely delete application versions
asafgabai Jul 6, 2026
58ffdab
Distribute and remotely delete application versions
asafgabai Jul 6, 2026
91f8516
Distribute and remotely delete application versions
asafgabai Jul 6, 2026
533e3fe
Distribute and remotely delete application versions
asafgabai Jul 6, 2026
4a1d652
Distribute and remotely delete application versions
asafgabai Jul 6, 2026
5750c49
Distribute and remotely delete application versions
asafgabai Jul 6, 2026
c68f42b
Distribute and remotely delete application versions
asafgabai Jul 6, 2026
e8567d0
Distribute and remotely delete application versions
asafgabai Jul 6, 2026
1c48732
Distribute and remotely delete application versions
asafgabai Jul 6, 2026
5a0b541
Distribute and remotely delete application versions
asafgabai Jul 6, 2026
b1353f8
Distribute and remotely delete application versions
asafgabai Jul 6, 2026
86699cb
Distribute and remotely delete application versions
asafgabai Jul 6, 2026
aee8ae9
Distribute and remotely delete application versions
asafgabai Jul 6, 2026
336e742
Distribute and remotely delete application versions
asafgabai Jul 6, 2026
ff07745
Distribute and remotely delete application versions
asafgabai Jul 6, 2026
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
44 changes: 44 additions & 0 deletions apptrust/commands/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ const (
VersionRelease = "version-release"
VersionUpdate = "version-update"
VersionUpdateSources = "version-update-sources"
VersionDistribute = "version-distribute"
VersionRemoteDelete = "version-delete-remote"
PackageBind = "package-bind"
PackageUnbind = "package-unbind"
AppCreate = "app-create"
Expand Down Expand Up @@ -64,6 +66,14 @@ const (
ExcludeFilterFlag = "exclude-filter"
ConflictResolutionFlag = "conflict-resolution"
PathMappingFlag = "path-mapping"
DistRulesFlag = "dist-rules"
SiteFlag = "site"
CityFlag = "city"
CountryCodesFlag = "country-codes"
CreateRepoFlag = "create-repo"
MappingPatternFlag = "mapping-pattern"
MappingTargetFlag = "mapping-target"
QuietFlag = "quiet"
)

// Flag keys mapped to their corresponding components.Flag definition.
Expand Down Expand Up @@ -109,6 +119,14 @@ var flagsMap = map[string]components.Flag{
DeletePropertiesFlag: components.NewStringFlag(DeletePropertiesFlag, "Remove a property key and all its values", func(f *components.StringFlag) { f.Mandatory = false }),
ConflictResolutionFlag: components.NewStringFlag(ConflictResolutionFlag, "How to resolve source conflicts when the same artifact path appears in multiple sources. Supported values: "+coreutils.ListToText(model.ConflictResolutionValues)+".", func(f *components.StringFlag) { f.Mandatory = false }),
PathMappingFlag: components.NewStringFlag(PathMappingFlag, "List of semicolon-separated (;) path mapping rules in the form of 'input=(.*), output=stable-release/$1[, package-type=.*]; input=(.*\\.jar), output=jars/$1, package-type=maven'. Note: quote the value to prevent shell expansion of $1.", func(f *components.StringFlag) { f.Mandatory = false }),
DistRulesFlag: components.NewStringFlag(DistRulesFlag, "Path to distribution rules.", func(f *components.StringFlag) { f.Mandatory = false }),
SiteFlag: components.NewStringFlag(SiteFlag, "Wildcard filter for site name.", func(f *components.StringFlag) { f.Mandatory = false }),
CityFlag: components.NewStringFlag(CityFlag, "Wildcard filter for site city name.", func(f *components.StringFlag) { f.Mandatory = false }),
CountryCodesFlag: components.NewStringFlag(CountryCodesFlag, "List of semicolon-separated (;) wildcard filters for site country codes.", func(f *components.StringFlag) { f.Mandatory = false }),
CreateRepoFlag: components.NewBoolFlag(CreateRepoFlag, "Set to true to create the repository on the edge if it does not exist.", components.WithBoolDefaultValueFalse()),
MappingPatternFlag: components.NewStringFlag(MappingPatternFlag, "Specify along with "+MappingTargetFlag+" to distribute artifacts to a different path on the edge node. You can use wildcards to specify multiple artifacts.", func(f *components.StringFlag) { f.Mandatory = false }),
MappingTargetFlag: components.NewStringFlag(MappingTargetFlag, "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.", func(f *components.StringFlag) { f.Mandatory = false }),
QuietFlag: components.NewBoolFlag(QuietFlag, "Set to true to skip the confirmation message.", components.WithBoolDefaultValueFalse()),
}

var commandFlags = map[string][]string{
Expand Down Expand Up @@ -201,6 +219,32 @@ var commandFlags = map[string][]string{
ExcludeFilterFlag,
},

VersionDistribute: {
url,
user,
accessToken,
serverId,
DistRulesFlag,
SiteFlag,
CityFlag,
CountryCodesFlag,
CreateRepoFlag,
MappingPatternFlag,
MappingTargetFlag,
},
VersionRemoteDelete: {
url,
user,
accessToken,
serverId,
QuietFlag,
DryRunFlag,
DistRulesFlag,
SiteFlag,
CityFlag,
CountryCodesFlag,
},

PackageBind: {
url,
user,
Expand Down
134 changes: 134 additions & 0 deletions apptrust/commands/version/distribute_app_version_cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package version

import (
"github.com/jfrog/jfrog-cli-application/apptrust/app"
"github.com/jfrog/jfrog-cli-application/apptrust/commands"
"github.com/jfrog/jfrog-cli-application/apptrust/commands/utils"
"github.com/jfrog/jfrog-cli-application/apptrust/common"
"github.com/jfrog/jfrog-cli-application/apptrust/model"
"github.com/jfrog/jfrog-cli-application/apptrust/service"
"github.com/jfrog/jfrog-cli-application/apptrust/service/versions"
commonCLiCommands "github.com/jfrog/jfrog-cli-core/v2/common/commands"
pluginsCommon "github.com/jfrog/jfrog-cli-core/v2/plugins/common"
"github.com/jfrog/jfrog-cli-core/v2/plugins/components"
coreConfig "github.com/jfrog/jfrog-cli-core/v2/utils/config"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
)

type distributeAppVersionCommand struct {
versionService versions.VersionService
serverDetails *coreConfig.ServerDetails
applicationKey string
version string
requestPayload *model.DistributeAppVersionRequest
}

func (dv *distributeAppVersionCommand) Run() error {
ctx, err := service.NewContext(*dv.serverDetails)
if err != nil {
return err
}

return dv.versionService.DistributeAppVersion(ctx, dv.applicationKey, dv.version, dv.requestPayload)
}

func (dv *distributeAppVersionCommand) ServerDetails() (*coreConfig.ServerDetails, error) {
return dv.serverDetails, nil
}

func (dv *distributeAppVersionCommand) CommandName() string {
return commands.VersionDistribute
}

func (dv *distributeAppVersionCommand) prepareAndRunCommand(ctx *components.Context) error {
if len(ctx.Arguments) != 2 {
return pluginsCommon.WrongNumberOfArgumentsHandler(ctx)
}

dv.applicationKey = ctx.Arguments[0]
dv.version = ctx.Arguments[1]

if err := ValidateDistributionFlags(ctx); err != nil {
return err
}

serverDetails, err := utils.ServerDetailsByFlags(ctx)
if err != nil {
return err
}
dv.serverDetails = serverDetails

dv.requestPayload, err = dv.buildRequestPayload(ctx)
if errorutils.CheckError(err) != nil {
return err
}

return commonCLiCommands.Exec(dv)
}

func (dv *distributeAppVersionCommand) buildRequestPayload(ctx *components.Context) (*model.DistributeAppVersionRequest, error) {
distributionRules, err := ParseDistributionRules(ctx)
if err != nil {
return nil, err
}

modifications, err := ParseDistributionModifications(ctx)
if err != nil {
return nil, err
}

return &model.DistributeAppVersionRequest{
DistributionRules: distributionRules,
AutoCreateRepo: ctx.GetBoolFlagValue(commands.CreateRepoFlag),
Modifications: modifications,
}, nil
}

func GetDistributeAppVersionCommand(appContext app.Context) components.Command {
cmd := &distributeAppVersionCommand{
versionService: appContext.GetVersionService(),
}
return components.Command{
Name: commands.VersionDistribute,
Description: "Distribute application version to distribution targets.",
AIDescription: `Distribute an application version's artifacts to one or more distribution targets according to distribution rules.

When to use:
- Make an application version's artifacts available on distribution targets close to consumers.
- Replicate a released version to remote sites for faster, local access.

Prerequisites:
- The application version must already exist.
- Configured server and distribution permission on the application's project.
- Reachable distribution target(s) matching the provided distribution rules.

Common patterns:
$ jf apptrust version-distribute my-app 1.0.0
$ jf apptrust version-distribute my-app 1.0.0 --site="us-*" --country-codes="US;CA"
$ jf apptrust version-distribute my-app 1.0.0 --dist-rules=/path/to/dist-rules.json
$ jf apptrust version-distribute my-app 1.0.0 --mapping-pattern="repo/(*)" --mapping-target="target/{1}"
$ jf apptrust version-distribute my-app 1.0.0 --create-repo

Gotchas:
- Distribution is asynchronous: a successful result means it was triggered, not that it has completed on the distribution targets.
- --dist-rules can't be combined with --site, --city or --country-codes.
- --mapping-pattern and --mapping-target must be provided together.

Related: jf apptrust version-delete-remote, jf apptrust version-release`,
Category: common.CategoryVersion,
Arguments: []components.Argument{
{
Name: "application-key",
Description: "The application key.",
Optional: false,
},
{
Name: "version",
Description: "The version to distribute.",
Optional: false,
},
},
Flags: commands.GetCommandFlags(commands.VersionDistribute),
Action: cmd.prepareAndRunCommand,
}
}
Loading
Loading