Skip to content

Commit b31a04f

Browse files
authored
Distribute and remotely delete application versions (#91)
* Distribute and remotely delete application versions
1 parent ba763ef commit b31a04f

14 files changed

Lines changed: 1553 additions & 0 deletions

apptrust/commands/flags.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ const (
1616
VersionRelease = "version-release"
1717
VersionUpdate = "version-update"
1818
VersionUpdateSources = "version-update-sources"
19+
VersionDistribute = "version-distribute"
20+
VersionRemoteDelete = "version-delete-remote"
1921
PackageBind = "package-bind"
2022
PackageUnbind = "package-unbind"
2123
AppCreate = "app-create"
@@ -64,6 +66,14 @@ const (
6466
ExcludeFilterFlag = "exclude-filter"
6567
ConflictResolutionFlag = "conflict-resolution"
6668
PathMappingFlag = "path-mapping"
69+
DistRulesFlag = "dist-rules"
70+
SiteFlag = "site"
71+
CityFlag = "city"
72+
CountryCodesFlag = "country-codes"
73+
CreateRepoFlag = "create-repo"
74+
MappingPatternFlag = "mapping-pattern"
75+
MappingTargetFlag = "mapping-target"
76+
QuietFlag = "quiet"
6777
)
6878

6979
// Flag keys mapped to their corresponding components.Flag definition.
@@ -109,6 +119,14 @@ var flagsMap = map[string]components.Flag{
109119
DeletePropertiesFlag: components.NewStringFlag(DeletePropertiesFlag, "Remove a property key and all its values", func(f *components.StringFlag) { f.Mandatory = false }),
110120
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 }),
111121
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 }),
122+
DistRulesFlag: components.NewStringFlag(DistRulesFlag, "Path to distribution rules.", func(f *components.StringFlag) { f.Mandatory = false }),
123+
SiteFlag: components.NewStringFlag(SiteFlag, "Wildcard filter for site name.", func(f *components.StringFlag) { f.Mandatory = false }),
124+
CityFlag: components.NewStringFlag(CityFlag, "Wildcard filter for site city name.", func(f *components.StringFlag) { f.Mandatory = false }),
125+
CountryCodesFlag: components.NewStringFlag(CountryCodesFlag, "List of semicolon-separated (;) wildcard filters for site country codes.", func(f *components.StringFlag) { f.Mandatory = false }),
126+
CreateRepoFlag: components.NewBoolFlag(CreateRepoFlag, "Set to true to create the repository on the edge if it does not exist.", components.WithBoolDefaultValueFalse()),
127+
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 }),
128+
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 }),
129+
QuietFlag: components.NewBoolFlag(QuietFlag, "Set to true to skip the confirmation message.", components.WithBoolDefaultValueFalse()),
112130
}
113131

114132
var commandFlags = map[string][]string{
@@ -201,6 +219,32 @@ var commandFlags = map[string][]string{
201219
ExcludeFilterFlag,
202220
},
203221

222+
VersionDistribute: {
223+
url,
224+
user,
225+
accessToken,
226+
serverId,
227+
DistRulesFlag,
228+
SiteFlag,
229+
CityFlag,
230+
CountryCodesFlag,
231+
CreateRepoFlag,
232+
MappingPatternFlag,
233+
MappingTargetFlag,
234+
},
235+
VersionRemoteDelete: {
236+
url,
237+
user,
238+
accessToken,
239+
serverId,
240+
QuietFlag,
241+
DryRunFlag,
242+
DistRulesFlag,
243+
SiteFlag,
244+
CityFlag,
245+
CountryCodesFlag,
246+
},
247+
204248
PackageBind: {
205249
url,
206250
user,
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
package version
2+
3+
import (
4+
"github.com/jfrog/jfrog-cli-application/apptrust/app"
5+
"github.com/jfrog/jfrog-cli-application/apptrust/commands"
6+
"github.com/jfrog/jfrog-cli-application/apptrust/commands/utils"
7+
"github.com/jfrog/jfrog-cli-application/apptrust/common"
8+
"github.com/jfrog/jfrog-cli-application/apptrust/model"
9+
"github.com/jfrog/jfrog-cli-application/apptrust/service"
10+
"github.com/jfrog/jfrog-cli-application/apptrust/service/versions"
11+
commonCLiCommands "github.com/jfrog/jfrog-cli-core/v2/common/commands"
12+
pluginsCommon "github.com/jfrog/jfrog-cli-core/v2/plugins/common"
13+
"github.com/jfrog/jfrog-cli-core/v2/plugins/components"
14+
coreConfig "github.com/jfrog/jfrog-cli-core/v2/utils/config"
15+
"github.com/jfrog/jfrog-client-go/utils/errorutils"
16+
)
17+
18+
type distributeAppVersionCommand struct {
19+
versionService versions.VersionService
20+
serverDetails *coreConfig.ServerDetails
21+
applicationKey string
22+
version string
23+
requestPayload *model.DistributeAppVersionRequest
24+
}
25+
26+
func (dv *distributeAppVersionCommand) Run() error {
27+
ctx, err := service.NewContext(*dv.serverDetails)
28+
if err != nil {
29+
return err
30+
}
31+
32+
return dv.versionService.DistributeAppVersion(ctx, dv.applicationKey, dv.version, dv.requestPayload)
33+
}
34+
35+
func (dv *distributeAppVersionCommand) ServerDetails() (*coreConfig.ServerDetails, error) {
36+
return dv.serverDetails, nil
37+
}
38+
39+
func (dv *distributeAppVersionCommand) CommandName() string {
40+
return commands.VersionDistribute
41+
}
42+
43+
func (dv *distributeAppVersionCommand) prepareAndRunCommand(ctx *components.Context) error {
44+
if len(ctx.Arguments) != 2 {
45+
return pluginsCommon.WrongNumberOfArgumentsHandler(ctx)
46+
}
47+
48+
dv.applicationKey = ctx.Arguments[0]
49+
dv.version = ctx.Arguments[1]
50+
51+
if err := ValidateDistributionFlags(ctx); err != nil {
52+
return err
53+
}
54+
55+
serverDetails, err := utils.ServerDetailsByFlags(ctx)
56+
if err != nil {
57+
return err
58+
}
59+
dv.serverDetails = serverDetails
60+
61+
dv.requestPayload, err = dv.buildRequestPayload(ctx)
62+
if errorutils.CheckError(err) != nil {
63+
return err
64+
}
65+
66+
return commonCLiCommands.Exec(dv)
67+
}
68+
69+
func (dv *distributeAppVersionCommand) buildRequestPayload(ctx *components.Context) (*model.DistributeAppVersionRequest, error) {
70+
distributionRules, err := ParseDistributionRules(ctx)
71+
if err != nil {
72+
return nil, err
73+
}
74+
75+
modifications, err := ParseDistributionModifications(ctx)
76+
if err != nil {
77+
return nil, err
78+
}
79+
80+
return &model.DistributeAppVersionRequest{
81+
DistributionRules: distributionRules,
82+
AutoCreateRepo: ctx.GetBoolFlagValue(commands.CreateRepoFlag),
83+
Modifications: modifications,
84+
}, nil
85+
}
86+
87+
func GetDistributeAppVersionCommand(appContext app.Context) components.Command {
88+
cmd := &distributeAppVersionCommand{
89+
versionService: appContext.GetVersionService(),
90+
}
91+
return components.Command{
92+
Name: commands.VersionDistribute,
93+
Description: "Distribute application version to distribution targets.",
94+
AIDescription: `Distribute an application version's artifacts to one or more distribution targets according to distribution rules.
95+
96+
When to use:
97+
- Make an application version's artifacts available on distribution targets close to consumers.
98+
- Replicate a released version to remote sites for faster, local access.
99+
100+
Prerequisites:
101+
- The application version must already exist.
102+
- Configured server and distribution permission on the application's project.
103+
- Reachable distribution target(s) matching the provided distribution rules.
104+
105+
Common patterns:
106+
$ jf apptrust version-distribute my-app 1.0.0
107+
$ jf apptrust version-distribute my-app 1.0.0 --site="us-*" --country-codes="US;CA"
108+
$ jf apptrust version-distribute my-app 1.0.0 --dist-rules=/path/to/dist-rules.json
109+
$ jf apptrust version-distribute my-app 1.0.0 --mapping-pattern="repo/(*)" --mapping-target="target/{1}"
110+
$ jf apptrust version-distribute my-app 1.0.0 --create-repo
111+
112+
Gotchas:
113+
- Distribution is asynchronous: a successful result means it was triggered, not that it has completed on the distribution targets.
114+
- --dist-rules can't be combined with --site, --city or --country-codes.
115+
- --mapping-pattern and --mapping-target must be provided together.
116+
117+
Related: jf apptrust version-delete-remote, jf apptrust version-release`,
118+
Category: common.CategoryVersion,
119+
Arguments: []components.Argument{
120+
{
121+
Name: "application-key",
122+
Description: "The application key.",
123+
Optional: false,
124+
},
125+
{
126+
Name: "version",
127+
Description: "The version to distribute.",
128+
Optional: false,
129+
},
130+
},
131+
Flags: commands.GetCommandFlags(commands.VersionDistribute),
132+
Action: cmd.prepareAndRunCommand,
133+
}
134+
}

0 commit comments

Comments
 (0)