Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
10 changes: 10 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: gomod
directory: /
schedule:
interval: weekly
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: weekly
36 changes: 36 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Test
on:
push:
branches:
- master
pull_request:
workflow_dispatch:

jobs:
go-test:
name: Go Test
runs-on: ubuntu-latest
steps:
- name: Check out source
uses: actions/checkout@v6
- name: Read go version
id: go_version
run: |
# Read the variable from the file
GO_VERSION=$(grep '^go ' go.mod | awk '{print $2}')
# Set the variable as an output
echo "GO_VERSION=$GO_VERSION" >> $GITHUB_OUTPUT

- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: ${{ steps.go_version.outputs.GO_VERSION }}

- name: Build
run: |
go get -t ./...
go build .

- name: Test
run: |
go test .
38 changes: 0 additions & 38 deletions .travis.yml

This file was deleted.

3 changes: 2 additions & 1 deletion examples/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ package main
import (
"errors"
"fmt"
"github.com/jessevdk/go-flags"
"os"
"strconv"
"strings"

flags "github.com/zmap/zflags"
)

type EditorOptions struct {
Expand Down
102 changes: 53 additions & 49 deletions fallback_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package flags

import (
"fmt"
"os"
"reflect"
"testing"
Expand All @@ -11,9 +12,9 @@ import (
// when `env` is absent, it attempts to fall back to `long` (and to `json`)
func TestFallback(t *testing.T) {
type Options struct {
Int int `long:"int" json:"json-int" default:"1"`
Time time.Duration `json:"time" default:"1m"`
Map map[string]int `json:"map,omitempty" default:"a:1" env-delim:";"`
Int int `long:"int" json:"json-int" default:"1" env:"Int"`
Time time.Duration `json:"time" default:"1m" env:"Time"`
Map map[string]int `json:"map,omitempty" default:"a:1" env:"Map" env-delim:";"`
Slice []int `long:"slice" default:"1" default:"2" env:"OVERRIDE_SLICE" env-delim:","`
}

Expand All @@ -27,19 +28,19 @@ func TestFallback(t *testing.T) {
msg: "JSON override",
args: []string{},
expected: Options{
Int: 23,
Time: time.Minute * 3,
Map: map[string]int{"key1": 1},
Slice: []int{3,4,5},
Int: 23,
Time: time.Minute * 3,
Map: map[string]int{"key1": 1},
Slice: []int{3, 4, 5},
},
env: map[string]string{
// since both `json` and `long` are present, `long` ("int") wins
"json-int": "4",
"int": "23",
"time": "3m",
"map": "key1:1",
"Int": "23",
"Time": "3m",
"Map": "key1:1",
// since both `env` and `long` are present, `env` ("OVERRIDE_SLICE") wins
"slice": "3,2,1",
"Slice": "3,2,1",
"OVERRIDE_SLICE": "3,4,5",
},
},
Expand All @@ -63,9 +64,9 @@ func TestFallback(t *testing.T) {
Slice: []int{4, 5, 6},
},
env: map[string]string{
"Int": "2",
"Time": "2m",
"Map": "a:2;b:3",
"Int": "2",
"Time": "2m",
"Map": "a:2;b:3",
"OVERRIDE_SLICE": "4,5,6",
},
},
Expand All @@ -75,13 +76,13 @@ func TestFallback(t *testing.T) {
expected: Options{
Int: 3,
Time: 3 * time.Millisecond,
Map: map[string]int{"c": 3,"d":4},
Slice: []int{3,1},
Map: map[string]int{"c": 3, "d": 4},
Slice: []int{3, 1},
},
env: map[string]string{
"Int": "2",
"Time": "2m",
"Map": "a:2;b:3",
"Int": "2",
"Time": "2m",
"Map": "a:2;b:3",
"OVERRIDE_SLICE": "4,5,6",
},
},
Expand All @@ -95,9 +96,9 @@ func TestFallback(t *testing.T) {
Slice: []int{0},
},
env: map[string]string{
"Int": "2",
"Time": "2m",
"Map": "a:2;b:3",
"Int": "2",
"Time": "2m",
"Map": "a:2;b:3",
"OVERRIDE_SLICE": "4,5,6",
},
},
Expand All @@ -111,9 +112,9 @@ func TestFallback(t *testing.T) {
Slice: []int{0},
},
env: map[string]string{
"Int": "2",
"Time": "2m",
"Map": "a:2;b:3",
"Int": "2",
"Time": "2m",
"Map": "a:2;b:3",
"Slice": "4,5,6",
},
},
Expand All @@ -126,9 +127,12 @@ func TestFallback(t *testing.T) {
var opts Options
oldEnv.Restore()
for envKey, envValue := range test.env {
os.Setenv(envKey, envValue)
err := os.Setenv(envKey, envValue)
if err != nil {
t.Fatal(fmt.Errorf("error setting env var (%s): %s", envKey, err))
}
}
_, _, _, err := NewParser(&opts, Default | EnvironmentFallback).ParseCommandLine(test.args)
_, _, _, err := NewParser(&opts, Default|EnvironmentFallback).ParseCommandLine(test.args)
if err != nil {
t.Fatalf("%s:\nUnexpected error: %v", test.msg, err)
}
Expand Down Expand Up @@ -163,17 +167,17 @@ func TestNoFallback(t *testing.T) {
msg: "JSON override",
args: []string{},
expected: Options{
Int: 1,
Time: time.Minute * 1,
Map: map[string]int{"a": 1},
Slice: []int{3,4,5},
Int: 1,
Time: time.Minute * 1,
Map: map[string]int{"a": 1},
Slice: []int{3, 4, 5},
},
env: map[string]string{
"json-int": "4",
"int": "23",
"time": "3m",
"map": "key1:1",
"slice": "3,2,1",
"json-int": "4",
"int": "23",
"time": "3m",
"map": "key1:1",
"slice": "3,2,1",
"OVERRIDE_SLICE": "3,4,5",
},
},
Expand All @@ -197,9 +201,9 @@ func TestNoFallback(t *testing.T) {
Slice: []int{4, 5, 6},
},
env: map[string]string{
"Int": "2",
"Time": "2m",
"Map": "a:2;b:3",
"Int": "2",
"Time": "2m",
"Map": "a:2;b:3",
"OVERRIDE_SLICE": "4,5,6",
},
},
Expand All @@ -209,13 +213,13 @@ func TestNoFallback(t *testing.T) {
expected: Options{
Int: 3,
Time: 3 * time.Millisecond,
Map: map[string]int{"c": 3,"d":4},
Slice: []int{3,1},
Map: map[string]int{"c": 3, "d": 4},
Slice: []int{3, 1},
},
env: map[string]string{
"Int": "2",
"Time": "2m",
"Map": "a:2;b:3",
"Int": "2",
"Time": "2m",
"Map": "a:2;b:3",
"OVERRIDE_SLICE": "4,5,6",
},
},
Expand All @@ -229,9 +233,9 @@ func TestNoFallback(t *testing.T) {
Slice: []int{0},
},
env: map[string]string{
"Int": "2",
"Time": "2m",
"Map": "a:2;b:3",
"Int": "2",
"Time": "2m",
"Map": "a:2;b:3",
"OVERRIDE_SLICE": "4,5,6",
},
},
Expand All @@ -246,7 +250,7 @@ func TestNoFallback(t *testing.T) {
for envKey, envValue := range test.env {
os.Setenv(envKey, envValue)
}
_, _, _, err := NewParser(&opts, Default & (^EnvironmentFallback)).ParseCommandLine(test.args)
_, _, _, err := NewParser(&opts, Default&(^EnvironmentFallback)).ParseCommandLine(test.args)
if err != nil {
t.Fatalf("%s:\nUnexpected error: %v", test.msg, err)
}
Expand All @@ -259,4 +263,4 @@ func TestNoFallback(t *testing.T) {
t.Errorf("%s:\nUnexpected options with arguments %+v\nexpected\n%+v\nbut got\n%+v\n", test.msg, test.args, test.expected, opts)
}
}
}
}
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/zmap/zflags

go 1.24.0
Empty file added go.sum
Empty file.
30 changes: 16 additions & 14 deletions ini.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,18 +115,18 @@ func (i *IniParser) ParseFile(filename string) ([]string, []interface{}, error)
//
// The format of the ini file is as follows:
//
// [Option group name]
// option = value
// [Option group name]
// option = value
//
// Each section in the ini file represents an option group or command in the
// flags parser. The default flags parser option group (i.e. when using
// flags.Parse) is named 'Application Options'. The ini option name is matched
// in the following order:
//
// 1. Compared to the ini-name tag on the option struct field (if present)
// 2. Compared to the struct field name
// 3. Compared to the option long name (if present)
// 4. Compared to the option short name (if present)
// 1. Compared to the ini-name tag on the option struct field (if present)
// 2. Compared to the struct field name
// 3. Compared to the option long name (if present)
// 4. Compared to the option short name (if present)
//
// Sections for nested groups and commands can be addressed using a dot `.'
// namespacing notation (i.e [subcommand.Options]). Group section names are
Expand Down Expand Up @@ -604,15 +604,17 @@ func (i *IniParser) parse(ini *ini) ([]string, []interface{}, error) {

if name != "" && name != "Application Options" {
c := i.parser.Find(name)
if cmd, ok := c.data.(ZCommander); ok {
if err := cmd.Validate([]string{}); err != nil { //validate
log.Fatal(err)
if c != nil {
if cmd, ok := c.data.(ZCommander); ok {
if err := cmd.Validate([]string{}); err != nil { //validate
log.Fatal(err)
}
modTypes = append(modTypes, name)
returnFlags = append(returnFlags, c.data)
par, _ := c.parent.(*Command)
c.Name = "-" //remove previous command
par.AddCommand(name, c.ShortDescription, c.LongDescription, c.module) //recreate new group with duplicate module
}
modTypes = append(modTypes, name)
returnFlags = append(returnFlags, c.data)
par, _ := c.parent.(*Command)
c.Name = "-" //remove previous command
par.AddCommand(name, c.ShortDescription, c.LongDescription, c.module) //recreate new group with duplicate module
}
}
}
Expand Down
Loading