Skip to content

Commit 05e5fa0

Browse files
committed
Add include/exclude filters to application version creation
1 parent 1c39a37 commit 05e5fa0

File tree

5 files changed

+707
-7
lines changed

5 files changed

+707
-7
lines changed

apptrust/commands/flags.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ const (
5353
SourceTypeArtifactsFlag = "source-type-artifacts"
5454
PropertiesFlag = "properties"
5555
DeletePropertiesFlag = "delete-properties"
56+
IncludeFilterFlag = "include-filter"
57+
ExcludeFilterFlag = "exclude-filter"
5658
)
5759

5860
// Flag keys mapped to their corresponding components.Flag definition.
@@ -85,6 +87,8 @@ var flagsMap = map[string]components.Flag{
8587
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 }),
8688
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 }),
8789
SourceTypePackagesFlag: components.NewStringFlag(SourceTypePackagesFlag, "List of semicolon-separated (;) packages in the form of 'type=packageType1, name=packageName1, version=version1, repo-key=repo1; type=packageType2, name=packageName2, version=version2, repo-key=repo2' to be included in the new version.", func(f *components.StringFlag) { f.Mandatory = false }),
90+
IncludeFilterFlag: components.NewStringFlag(IncludeFilterFlag, "List of semicolon-separated (;) inclusion filter in the form of 'filter_type=package/artifact ...' for packages, at least one of 'type', 'name', or 'version' must be specified. For artifacts, at least one of 'path' or 'sha256' must be specified.", func(f *components.StringFlag) { f.Mandatory = false }),
91+
ExcludeFilterFlag: components.NewStringFlag(ExcludeFilterFlag, "List of semicolon-separated (;) inclusion filter in the form of 'filter_type=package/artifact ...' for packages, at least one of 'type', 'name', or 'version' must be specified. For artifacts, at least one of 'path' or 'sha256' must be specified.", func(f *components.StringFlag) { f.Mandatory = false }),
8892
SourceTypeArtifactsFlag: components.NewStringFlag(SourceTypeArtifactsFlag, "List of semicolon-separated (;) artifacts in the form of 'path=repo/path/to/artifact1[, sha256=hash1]; path=repo/path/to/artifact2[, sha256=hash2]' to be included in the new version.", func(f *components.StringFlag) { f.Mandatory = false }),
8993
PropertiesFlag: components.NewStringFlag(PropertiesFlag, "Sets or updates custom properties for the application version in format 'key1=value1[,value2,...];key2=value3[,value4,...]'", func(f *components.StringFlag) { f.Mandatory = false }),
9094
DeletePropertiesFlag: components.NewStringFlag(DeletePropertiesFlag, "Remove a property key and all its values", func(f *components.StringFlag) { f.Mandatory = false }),
@@ -103,6 +107,8 @@ var commandFlags = map[string][]string{
103107
SourceTypePackagesFlag,
104108
SourceTypeArtifactsFlag,
105109
SpecFlag,
110+
IncludeFilterFlag,
111+
ExcludeFilterFlag,
106112
SpecVarsFlag,
107113
},
108114
VersionPromote: {

apptrust/commands/version/create_app_version_cmd.go

Lines changed: 106 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type createVersionSpec struct {
3434
Builds []model.CreateVersionBuild `json:"builds,omitempty"`
3535
ReleaseBundles []model.CreateVersionReleaseBundle `json:"release_bundles,omitempty"`
3636
Versions []model.CreateVersionReference `json:"versions,omitempty"`
37+
Filters *model.CreateVersionFilters `json:"filters,omitempty"`
3738
}
3839

3940
func (cv *createAppVersionCommand) Run() error {
@@ -72,13 +73,18 @@ func (cv *createAppVersionCommand) prepareAndRunCommand(ctx *components.Context)
7273
func (cv *createAppVersionCommand) buildRequestPayload(ctx *components.Context) (*model.CreateAppVersionRequest, error) {
7374
var (
7475
sources *model.CreateVersionSources
76+
filters *model.CreateVersionFilters
7577
err error
7678
)
7779

7880
if ctx.IsFlagSet(commands.SpecFlag) {
79-
sources, err = cv.loadFromSpec(ctx)
81+
sources, filters, err = cv.loadFromSpec(ctx)
8082
} else {
8183
sources, err = cv.buildSourcesFromFlags(ctx)
84+
if err != nil {
85+
return nil, err
86+
}
87+
filters, err = cv.buildFiltersFromFlags(ctx)
8288
}
8389

8490
if err != nil {
@@ -90,6 +96,7 @@ func (cv *createAppVersionCommand) buildRequestPayload(ctx *components.Context)
9096
Version: ctx.Arguments[1],
9197
Sources: sources,
9298
Tag: ctx.GetStringFlagValue(commands.TagFlag),
99+
Filters: filters,
93100
}, nil
94101
}
95102

@@ -133,13 +140,13 @@ func (cv *createAppVersionCommand) buildSourcesFromFlags(ctx *components.Context
133140
return sources, nil
134141
}
135142

136-
func (cv *createAppVersionCommand) loadFromSpec(ctx *components.Context) (*model.CreateVersionSources, error) {
143+
func (cv *createAppVersionCommand) loadFromSpec(ctx *components.Context) (*model.CreateVersionSources, *model.CreateVersionFilters, error) {
137144
specFilePath := ctx.GetStringFlagValue(commands.SpecFlag)
138145
spec := new(createVersionSpec)
139146
specVars := coreutils.SpecVarsStringToMap(ctx.GetStringFlagValue(commands.SpecVarsFlag))
140147
content, err := fileutils.ReadFile(specFilePath)
141148
if errorutils.CheckError(err) != nil {
142-
return nil, err
149+
return nil, nil, err
143150
}
144151

145152
if len(specVars) > 0 {
@@ -148,12 +155,12 @@ func (cv *createAppVersionCommand) loadFromSpec(ctx *components.Context) (*model
148155

149156
err = json.Unmarshal(content, spec)
150157
if errorutils.CheckError(err) != nil {
151-
return nil, err
158+
return nil, nil, err
152159
}
153160

154161
// Validation: if all sources are empty, return error
155162
if (len(spec.Packages) == 0) && (len(spec.Builds) == 0) && (len(spec.ReleaseBundles) == 0) && (len(spec.Versions) == 0) && (len(spec.Artifacts) == 0) {
156-
return nil, errorutils.CheckErrorf("Spec file is empty: must provide at least one source (artifacts, packages, builds, release_bundles, or versions)")
163+
return nil, nil, errorutils.CheckErrorf("Spec file is empty: must provide at least one source (artifacts, packages, builds, release_bundles, or versions)")
157164
}
158165

159166
sources := &model.CreateVersionSources{
@@ -164,7 +171,7 @@ func (cv *createAppVersionCommand) loadFromSpec(ctx *components.Context) (*model
164171
Versions: spec.Versions,
165172
}
166173

167-
return sources, nil
174+
return sources, spec.Filters, nil
168175
}
169176

170177
func (cv *createAppVersionCommand) parseBuilds(buildsStr string) ([]model.CreateVersionBuild, error) {
@@ -310,6 +317,92 @@ func (cv *createAppVersionCommand) parseArtifacts(artifactsStr string) ([]model.
310317
return artifacts, nil
311318
}
312319

320+
func (cv *createAppVersionCommand) buildFiltersFromFlags(ctx *components.Context) (*model.CreateVersionFilters, error) {
321+
includeFilterValues := ctx.GetStringsArrFlagValue(commands.IncludeFilterFlag)
322+
excludeFilterValues := ctx.GetStringsArrFlagValue(commands.ExcludeFilterFlag)
323+
324+
if len(includeFilterValues) == 0 && len(excludeFilterValues) == 0 {
325+
return nil, nil
326+
}
327+
filters := &model.CreateVersionFilters{}
328+
if includedFilters, err := cv.parseFilterValues(includeFilterValues); err != nil {
329+
return nil, err
330+
} else if len(includedFilters) > 0 {
331+
filters.Included = includedFilters
332+
}
333+
if excludedFilters, err := cv.parseFilterValues(excludeFilterValues); err != nil {
334+
return nil, err
335+
} else if len(excludedFilters) > 0 {
336+
filters.Excluded = excludedFilters
337+
}
338+
339+
return filters, nil
340+
}
341+
342+
func (cv *createAppVersionCommand) parseFilterValues(filterValues []string) ([]*model.CreateVersionSourceFilter, error) {
343+
if len(filterValues) == 0 {
344+
return nil, nil
345+
}
346+
return cv.parseFilters(filterValues)
347+
}
348+
349+
func (cv *createAppVersionCommand) parseFilters(filterStrings []string) ([]*model.CreateVersionSourceFilter, error) {
350+
const (
351+
filterTypeField = "filter_type"
352+
packageTypeField = "type"
353+
packageNameField = "name"
354+
packageVersionField = "version"
355+
artifactPathField = "path"
356+
artifactShaField = "sha256"
357+
)
358+
359+
var filters []*model.CreateVersionSourceFilter
360+
361+
for i, filterStr := range filterStrings {
362+
filterMap, err := utils.ParseKeyValueString(filterStr, ",")
363+
if err != nil {
364+
return nil, errorutils.CheckErrorf("invalid filter format at index %d: %v", i, err)
365+
}
366+
filterType, ok := filterMap[filterTypeField]
367+
if !ok {
368+
return nil, errorutils.CheckErrorf("invalid filter format at index %d: missing 'filter_type' field", i)
369+
}
370+
filter := &model.CreateVersionSourceFilter{}
371+
372+
switch filterType {
373+
case "package":
374+
if val, ok := filterMap[packageTypeField]; ok {
375+
filter.PackageType = val
376+
}
377+
if val, ok := filterMap[packageNameField]; ok {
378+
filter.PackageName = val
379+
}
380+
if val, ok := filterMap[packageVersionField]; ok {
381+
filter.PackageVersion = val
382+
}
383+
if filter.PackageType == "" && filter.PackageName == "" && filter.PackageVersion == "" {
384+
return nil, errorutils.CheckErrorf("invalid package filter at index %d: at least one of 'type', 'name', or 'version' must be specified", i)
385+
}
386+
case "artifact":
387+
if val, ok := filterMap[artifactPathField]; ok {
388+
filter.Path = val
389+
}
390+
if val, ok := filterMap[artifactShaField]; ok {
391+
filter.SHA256 = val
392+
}
393+
if filter.Path == "" && filter.SHA256 == "" {
394+
return nil, errorutils.CheckErrorf("invalid artifact filter at index %d: at least one of 'path' or 'sha256' must be specified", i)
395+
}
396+
default:
397+
return nil, errorutils.CheckErrorf("invalid filter_type '%s' at index %d: must be 'package' or 'artifact'", filterType, i)
398+
}
399+
400+
filters = append(filters, filter)
401+
}
402+
403+
return filters, nil
404+
}
405+
313406
func validateCreateAppVersionContext(ctx *components.Context) error {
314407
if err := validateNoSpecAndFlagsTogether(ctx); err != nil {
315408
return err
@@ -358,7 +451,7 @@ func GetCreateAppVersionCommand(appContext app.Context) components.Command {
358451
}
359452
}
360453

361-
// Returns error if both --spec and any other source flag are set
454+
// Returns error if both --spec and any other source flag or filter flag are set
362455
func validateNoSpecAndFlagsTogether(ctx *components.Context) error {
363456
if ctx.IsFlagSet(commands.SpecFlag) {
364457
otherSourceFlags := []string{
@@ -373,6 +466,12 @@ func validateNoSpecAndFlagsTogether(ctx *components.Context) error {
373466
return errorutils.CheckErrorf("--spec provided: all other source flags (e.g., --%s) are not allowed.", flag)
374467
}
375468
}
469+
if ctx.IsFlagSet(commands.IncludeFilterFlag) {
470+
return errorutils.CheckErrorf("--spec provided: filter flags (e.g., --%s) are not allowed.", commands.IncludeFilterFlag)
471+
}
472+
if ctx.IsFlagSet(commands.ExcludeFilterFlag) {
473+
return errorutils.CheckErrorf("--spec provided: filter flags (e.g., --%s) are not allowed.", commands.ExcludeFilterFlag)
474+
}
376475
}
377476
return nil
378477
}

0 commit comments

Comments
 (0)