Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't always add default values #441

Merged
merged 12 commits into from
Oct 18, 2023
4 changes: 2 additions & 2 deletions internal/baking/releases_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package baking
import (
"os"
"path/filepath"
"regexp"
"strings"

"github.com/pivotal-cf/kiln/internal/builder"
)
Expand Down Expand Up @@ -49,7 +49,7 @@ func (s ReleasesService) ReleasesInDirectory(directoryPath string) ([]builder.Pa
return err
}

if match, _ := regexp.MatchString("tgz$|tar.gz$", path); match {
if strings.HasSuffix(path, ".tgz") || strings.HasSuffix(path, ".tar.gz") {
tarballPaths = append(tarballPaths, path)
}

Expand Down
4 changes: 2 additions & 2 deletions internal/commands/bake.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ func getVariablesFilePaths(fs flags.FileSystem) ([]string, error) {
}

func (b *Bake) loadFlags(args []string) error {
_, err := flags.LoadFlagsWithDefaults(&b.Options, args, b.fs.Stat)
_, err := flags.LoadWithDefaultFilePaths(&b.Options, args, b.fs.Stat)
if err != nil {
return err
}
Expand Down Expand Up @@ -355,7 +355,7 @@ func (b Bake) Execute(args []string) error {
b.Options.FetchBakeOptions,
FetchReleaseDir{releaseDir},
}
fetchArgs := flags.ToStrings(fetchOptions)
fetchArgs := flags.Args(fetchOptions)
err = b.fetcher.Execute(fetchArgs)
if err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion internal/commands/cache_compiled_releases.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func (cmd *CacheCompiledReleases) WithLogger(logger *log.Logger) *CacheCompiledR
}

func (cmd *CacheCompiledReleases) Execute(args []string) error {
_, err := flags.LoadFlagsWithDefaults(&cmd.Options, args, cmd.FS.Stat)
_, err := flags.LoadWithDefaultFilePaths(&cmd.Options, args, cmd.FS.Stat)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion internal/commands/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func (f Fetch) Execute(args []string) error {

func (f *Fetch) setup(args []string) (cargo.Kilnfile, cargo.KilnfileLock, []component.Local, error) {
if f.Options.ReleasesDir == "" {
_, err := flags.LoadFlagsWithDefaults(&f.Options, args, nil)
_, err := flags.LoadWithDefaultFilePaths(&f.Options, args, nil)
if err != nil {
return cargo.Kilnfile{}, cargo.KilnfileLock{}, nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion internal/commands/find_release_version.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (cmd *FindReleaseVersion) Execute(args []string) error {
}

func (cmd *FindReleaseVersion) setup(args []string) (cargo.Kilnfile, cargo.KilnfileLock, error) {
argsAfterFlags, err := flags.LoadFlagsWithDefaults(&cmd.Options, args, nil)
argsAfterFlags, err := flags.LoadWithDefaultFilePaths(&cmd.Options, args, nil)
if err != nil {
return cargo.Kilnfile{}, cargo.KilnfileLock{}, err
}
Expand Down
2 changes: 1 addition & 1 deletion internal/commands/find_stemcell_version.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func (cmd *FindStemcellVersion) setup(args []string) (cargo.Kilnfile, error) {
return cargo.Kilnfile{}, err
}

_, err = flags.LoadFlagsWithDefaults(&cmd.Options, args, cmd.FS.Stat)
_, err = flags.LoadWithDefaultFilePaths(&cmd.Options, args, cmd.FS.Stat)
if err != nil {
return cargo.Kilnfile{}, err
}
Expand Down
172 changes: 76 additions & 96 deletions internal/commands/flags/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ package flags

import (
"fmt"
"go/ast"
"io"
"os"
"path/filepath"
"reflect"
"slices"
"strings"

"github.com/go-git/go-billy/v5"
Expand All @@ -27,8 +27,8 @@ type (
billy.Dir
}

KilnfileOptions interface {
KilnfilePathPrefix() string
TileDirectory interface {
TileDirectory() string
}

VariablesService interface {
Expand Down Expand Up @@ -96,7 +96,7 @@ func (options *Standard) LoadKilnfiles(fsOverride billy.Basic, variablesServiceO
return kilnfile, lock, nil
}

func (options *Standard) SaveKilnfileLock(fsOverride billy.Basic, kilnfileLock cargo.KilnfileLock) error {
func (options Standard) SaveKilnfileLock(fsOverride billy.Basic, kilnfileLock cargo.KilnfileLock) error {
fs := fsOverride
if fs == nil {
fs = osfs.New("")
Expand Down Expand Up @@ -132,10 +132,21 @@ func (options Standard) KilnfileLockPath() string {
return options.Kilnfile + ".lock"
}

// LoadFlagsWithDefaults only sets default values if the flag is not set
func (options Standard) TileDirectory() string {
if options.Kilnfile != "" {
if filepath.Base(options.Kilnfile) == "Kilnfile" {
return filepath.Dir(options.Kilnfile)
}
return options.Kilnfile
}
currentWorkingDirectory, _ := os.Getwd()
return currentWorkingDirectory
}

// LoadWithDefaultFilePaths only sets default values if the flag is not set
// this permits explicitly setting "zero values" for in arguments without them being
// overwritten.
func LoadFlagsWithDefaults(options KilnfileOptions, args []string, stat StatFunc) ([]string, error) {
func LoadWithDefaultFilePaths(options TileDirectory, args []string, stat StatFunc) ([]string, error) {
if stat == nil {
stat = os.Stat
}
Expand All @@ -146,120 +157,89 @@ func LoadFlagsWithDefaults(options KilnfileOptions, args []string, stat StatFunc

v := reflect.ValueOf(options).Elem()

pathPrefix := options.KilnfilePathPrefix()
tileDir := options.TileDirectory()

// handle simple case first
configureArrayDefaults(v, pathPrefix, args, stat)
configurePathDefaults(v, pathPrefix, args, stat)
configurePathDefaults(v, tileDir, args, stat)

return argsAfterFlags, nil
}

func configureArrayDefaults(v reflect.Value, pathPrefix string, args []string, stat StatFunc) {
func configurePathDefaults(v reflect.Value, pathPrefix string, args []string, stat StatFunc) {
t := v.Type()

for i := 0; i < t.NumField(); i++ {
field := t.Field(i)

switch field.Type.Kind() {
default:
fieldType := t.Field(i)
if !fieldType.IsExported() {
continue
case reflect.Struct:
embeddedValue := v.Field(i)
if field.Anonymous && ast.IsExported(embeddedValue.Type().Name()) {
configurePathDefaults(embeddedValue, pathPrefix, args, stat)
}
continue
case reflect.Slice:
}

defaultValueStr, ok := field.Tag.Lookup("default")
if !ok {
continue
}
defaultValues := strings.Split(defaultValueStr, ",")
fieldValue := v.Field(i)

flagValues, ok := v.Field(i).Interface().([]string)
if !ok {
// this might occur if we add non string slice params
// notice the field Kind check above was not super specific
switch fieldType.Type.Kind() {
default:
continue
}

if IsSet(field.Tag.Get("short"), field.Tag.Get("long"), args) {
v.Field(i).Set(reflect.ValueOf(flagValues[len(defaultValues):]))
case reflect.Struct:
configurePathDefaults(fieldValue, pathPrefix, args, stat)
continue
}

filteredDefaults := defaultValues[:0]
for _, p := range defaultValues {
if pathPrefix != "" {
p = filepath.Join(pathPrefix, p)
}
_, err := stat(p)
if os.IsNotExist(err) {
case reflect.String:
defaultValue, ok := fieldType.Tag.Lookup("default")
if !ok {
continue
}
filteredDefaults = append(filteredDefaults, p)
}

// if default values were found, use them,
// else filteredDefaults will be an empty slice
// and the Bake command will continue as if they were not set
v.Field(i).Set(reflect.ValueOf(filteredDefaults))
}
}
value := v.Field(i).Interface().(string)

func configurePathDefaults(v reflect.Value, pathPrefix string, args []string, stat StatFunc) {
t := v.Type()

for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
isDefaultValue := defaultValue == value

switch field.Type.Kind() {
default:
continue
case reflect.Struct:
embeddedValue := v.Field(i)
if field.Anonymous && ast.IsExported(embeddedValue.Type().Name()) {
configurePathDefaults(embeddedValue, pathPrefix, args, stat)
if !isDefaultValue {
continue
}
continue
case reflect.String:
}

if IsSet(field.Tag.Get("short"), field.Tag.Get("long"), args) {
continue
}
if pathPrefix != "" {
value = filepath.Join(pathPrefix, value)
}

defaultValue, ok := field.Tag.Lookup("default")
if !ok {
continue
}
_, err := stat(value)
if err != nil {
// set to zero value
v.Field(i).Set(reflect.Zero(v.Field(i).Type()))
continue
}

value, ok := v.Field(i).Interface().(string)
if !ok {
continue // this should not occur
}
fieldValue.Set(reflect.ValueOf(value))
case reflect.Slice:
flagValues := v.Field(i).Interface().([]string)

isDefaultValue := defaultValue == value
defaultValue, ok := fieldType.Tag.Lookup("default")
if !ok {
continue
}
defaultValues := strings.Split(defaultValue, ",")

if !isDefaultValue {
continue
}
if len(flagValues) > len(defaultValues) && slices.Equal(flagValues[:len(defaultValues)], defaultValues) {
fieldValue.Set(reflect.ValueOf(flagValues[len(defaultValues):]))
continue
}

if pathPrefix != "" {
value = filepath.Join(pathPrefix, value)
}
filteredDefaults := defaultValues[:0]
for _, p := range defaultValues {
p = strings.TrimSpace(p)
if pathPrefix != "" {
p = filepath.Join(pathPrefix, p)
}
_, err := stat(p)
if err != nil {
continue
}
filteredDefaults = append(filteredDefaults, p)
}

_, err := stat(value)
if err != nil {
// set to zero value
v.Field(i).Set(reflect.Zero(v.Field(i).Type()))
continue
// if default values were found, use them,
// else filteredDefaults will be an empty slice
// and the Bake command will continue as if they were not set
fieldValue.Set(reflect.ValueOf(filteredDefaults))
}

v.Field(i).Set(reflect.ValueOf(value))
}
}

Expand All @@ -286,17 +266,17 @@ func IsSet(short, long string, args []string) bool {
return false
}

func ToStrings[t any](v t) []string {
return _encode(reflect.ValueOf(v))
func Args(v any) []string {
return encodeFlags(reflect.ValueOf(v))
}

func _encode(v reflect.Value) []string {
func encodeFlags(v reflect.Value) []string {
var result []string
switch v.Kind() {
case reflect.Slice:
for i := 0; i < v.Len(); i++ {
sv := v.Index(i)
encode := _encode(sv)
encode := encodeFlags(sv)
result = append(result, encode...)
}
case reflect.Struct:
Expand All @@ -308,7 +288,7 @@ func _encode(v reflect.Value) []string {
fieldAnnotation = fieldType.Tag.Get("short")
}

encode := _encode(fieldVal)
encode := encodeFlags(fieldVal)
// TODO: make sure that valueless flags are passed correctly
if fieldAnnotation != "" && fieldVal.Kind() == reflect.Bool {
isSet := fieldVal.Bool()
Expand Down
13 changes: 0 additions & 13 deletions internal/commands/flags/parse_suite_test.go

This file was deleted.

Loading