Skip to content
Draft
4 changes: 4 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*.go text eol=lf
*.md text eol=lf
*.sh text eol=lf
*.yaml text eol=lf
47 changes: 23 additions & 24 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -1,26 +1,38 @@
name: CI

on:
push:
branches: [master]
pull_request:
on: [push, pull_request]

# Default to 'contents: read', which grants actions to read commits. Any
# permission not included is implicitly set to "none".
#
# see https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
permissions:
contents: read

jobs:
test:
name: Test
runs-on: ubuntu-latest
runs-on: ${{ matrix.platform }}
timeout-minutes: 20 # guardrails timeout

strategy:
fail-fast: false
matrix:
go: ["1.12", "1.21", "1.22", "1.23"]

# Oldest supported + currently supported Go versions
go: ["1.12.x", "oldstable", "stable"]
platform: [ubuntu-latest, windows-latest, macos-latest]
exclude:
# go1.12 is not available for windows and macos on GitHub actions.
- go: "1.12.x"
platform: windows-latest
- go: "1.12.x"
platform: macos-latest
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1

- name: Set up Go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
go-version: ${{ matrix.go }}

Expand All @@ -29,20 +41,7 @@ jobs:
# run: go test -race -v -shuffle=on ./...
run: go test -race -v ./...

lint:
name: Lint
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Set up Go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
with:
go-version: "1.23"

- name: Lint
uses: golangci/golangci-lint-action@971e284b6050e8a5849b72094c50ab08da042db8 # v6.1.1
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
with:
version: v1.63.4
version: v2.7
19 changes: 18 additions & 1 deletion .golangci.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
version: "2"

linters:
disable-all: true
enable:
- errorlint
- nolintlint
- revive
- unconvert
- unparam
settings:
staticcheck:
# Enable all options, with some exceptions.
# For defaults, see https://golangci-lint.run/usage/linters/#staticcheck
checks:
- all
- -QF1008 # Omit embedded fields from selector expression; https://staticcheck.dev/docs/checks/#QF1008
- -ST1003 # Poorly chosen identifier; https://staticcheck.dev/docs/checks/#ST1003

formatters:
enable:
- gofmt
4 changes: 2 additions & 2 deletions bool_func_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func TestBoolFuncUsage(t *testing.T) {
// regular boolfunc flag:
// expect to see '--flag1' followed by the usageMessage, and no mention of a default value
fset := NewFlagSet("unittest", ContinueOnError)
fset.BoolFunc("flag1", "usage message", func(s string) error { return nil })
fset.BoolFunc("flag1", "usage message", func(string) error { return nil })
usage := fset.FlagUsagesWrapped(80)

usage = strings.TrimSpace(usage)
Expand All @@ -64,7 +64,7 @@ func TestBoolFuncUsage(t *testing.T) {
// func flag, with a placeholder name:
// if usageMesage contains a placeholder, expect '--flag2 {placeholder}'; still expect no mention of a default value
fset := NewFlagSet("unittest", ContinueOnError)
fset.BoolFunc("flag2", "usage message with `name` placeholder", func(s string) error { return nil })
fset.BoolFunc("flag2", "usage message with `name` placeholder", func(string) error { return nil })
usage := fset.FlagUsagesWrapped(80)

usage = strings.TrimSpace(usage)
Expand Down
4 changes: 1 addition & 3 deletions bool_slice.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,12 @@ func newBoolSliceValue(val []bool, p *[]bool) *boolSliceValue {
// Set converts, and assigns, the comma-separated boolean argument string representation as the []bool value of this flag.
// If Set is called on a flag that already has a []bool assigned, the newly converted values will be appended.
func (s *boolSliceValue) Set(val string) error {

// remove all quote characters
rmQuote := strings.NewReplacer(`"`, "", `'`, "", "`", "")

// read flag arguments with CSV parser
boolStrSlice, err := readAsCSV(rmQuote.Replace(val))
if err != nil && err != io.EOF {
if err != nil && err != io.EOF { //nolint:errorlint // not using errors.Is for compatibility with go1.12
return err
}

Expand Down Expand Up @@ -60,7 +59,6 @@ func (s *boolSliceValue) Type() string {

// String defines a "native" format for this boolean slice flag value.
func (s *boolSliceValue) String() string {

boolStrSlice := make([]string, len(*s.value))
for i, b := range *s.value {
boolStrSlice[i] = strconv.FormatBool(b)
Expand Down
1 change: 0 additions & 1 deletion bool_slice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,6 @@ func TestBSAsSliceValue(t *testing.T) {
}

func TestBSBadQuoting(t *testing.T) {

tests := []struct {
Want []bool
FlagArg []string
Expand Down
2 changes: 1 addition & 1 deletion bool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func (v *triStateValue) IsBoolFlag() bool {
}

func (v *triStateValue) Get() interface{} {
return triStateValue(*v)
return *v
}

func (v *triStateValue) Set(s string) error {
Expand Down
10 changes: 2 additions & 8 deletions bytes.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ func (bytesHex bytesHexValue) String() string {
// Set implements pflag.Value.Set.
func (bytesHex *bytesHexValue) Set(value string) error {
bin, err := hex.DecodeString(strings.TrimSpace(value))

if err != nil {
return err
}
Expand All @@ -39,20 +38,18 @@ func newBytesHexValue(val []byte, p *[]byte) *bytesHexValue {
}

func bytesHexConv(sval string) (interface{}, error) {

bin, err := hex.DecodeString(sval)

if err == nil {
return bin, nil
}

return nil, fmt.Errorf("invalid string being converted to Bytes: %s %s", sval, err)
return nil, fmt.Errorf("invalid string being converted to Bytes: %s %s", sval, err.Error())
}

// GetBytesHex return the []byte value of a flag with the given name
func (f *FlagSet) GetBytesHex(name string) ([]byte, error) {
val, err := f.getFlagType(name, "bytesHex", bytesHexConv)

if err != nil {
return []byte{}, err
}
Expand Down Expand Up @@ -119,7 +116,6 @@ func (bytesBase64 bytesBase64Value) String() string {
// Set implements pflag.Value.Set.
func (bytesBase64 *bytesBase64Value) Set(value string) error {
bin, err := base64.StdEncoding.DecodeString(strings.TrimSpace(value))

if err != nil {
return err
}
Expand All @@ -140,19 +136,17 @@ func newBytesBase64Value(val []byte, p *[]byte) *bytesBase64Value {
}

func bytesBase64ValueConv(sval string) (interface{}, error) {

bin, err := base64.StdEncoding.DecodeString(sval)
if err == nil {
return bin, nil
}

return nil, fmt.Errorf("invalid string being converted to Bytes: %s %s", sval, err)
return nil, fmt.Errorf("invalid string being converted to Bytes: %s %s", sval, err.Error())
}

// GetBytesBase64 return the []byte value of a flag with the given name
func (f *FlagSet) GetBytesBase64(name string) ([]byte, error) {
val, err := f.getFlagType(name, "bytesBase64", bytesBase64ValueConv)

if err != nil {
return []byte{}, err
}
Expand Down
2 changes: 1 addition & 1 deletion count.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func newCountValue(val int, p *int) *countValue {
func (i *countValue) Set(s string) error {
// "+1" means that no specific value was passed, so increment
if s == "+1" {
*i = countValue(*i + 1)
*i = *i + 1
return nil
}
v, err := strconv.ParseInt(s, 0, 0)
Expand Down
5 changes: 2 additions & 3 deletions duration_slice.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package pflag

import (
"fmt"
"strings"
"time"
)
Expand Down Expand Up @@ -46,7 +45,7 @@ func (s *durationSliceValue) Type() string {
func (s *durationSliceValue) String() string {
out := make([]string, len(*s.value))
for i, d := range *s.value {
out[i] = fmt.Sprintf("%s", d)
out[i] = d.String()
}
return "[" + strings.Join(out, ",") + "]"
}
Expand All @@ -56,7 +55,7 @@ func (s *durationSliceValue) fromString(val string) (time.Duration, error) {
}

func (s *durationSliceValue) toString(val time.Duration) string {
return fmt.Sprintf("%s", val)
return val.String()
}

func (s *durationSliceValue) Append(val string) error {
Expand Down
2 changes: 1 addition & 1 deletion errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ func (e *InvalidSyntaxError) Error() string {
return fmt.Sprintf("bad flag syntax: %s", e.specifiedFlag)
}

// GetSpecifiedName returns the exact flag (with dashes) as it
// GetSpecifiedFlag returns the exact flag (with dashes) as it
// appeared in the parsed arguments.
func (e *InvalidSyntaxError) GetSpecifiedFlag() string {
return e.specifiedFlag
Expand Down
4 changes: 2 additions & 2 deletions errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ func TestInvalidValueError(t *testing.T) {
if err.GetValue() != "foo" {
t.Errorf("Expected GetValue to return %q, got %q", "foo", err.GetValue())
}
if err.Unwrap() != expectedCause {
t.Errorf("Expected Unwrwap to return %q, got %q", expectedCause, err.Unwrap())
if actual := err.Unwrap(); actual != expectedCause { //nolint:errorlint // not using errors.Is for compatibility with go1.12
t.Errorf("Expected Unwrwap to return %q, got %q", expectedCause, actual)
}
}

Expand Down
Loading