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

Move genflags tool to cmd/ and pin to previous release #1451

Merged
merged 3 commits into from Aug 16, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/cli.yml
Expand Up @@ -43,6 +43,9 @@ jobs:
- name: test
run: go run internal/build/build.go test

- name: test urfave-cli-genflags
run: make -C cmd/urfave-cli-genflags

- name: check-binary-size
run: go run internal/build/build.go check-binary-size

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -6,5 +6,6 @@ internal/*/built-example
coverage.txt
/.local/
/site/
/cmd/urfave-cli-genflags/urfave-cli-genflags

*.exe
2 changes: 1 addition & 1 deletion cli.go
Expand Up @@ -20,4 +20,4 @@
// }
package cli

//go:generate go run internal/genflags/cmd/genflags/main.go
//go:generate go run cmd/urfave-cli-genflags/main.go
21 changes: 21 additions & 0 deletions cmd/urfave-cli-genflags/Makefile
@@ -0,0 +1,21 @@
GOTEST_FLAGS ?= -v --coverprofile main.coverprofile --covermode count --cover github.com/urfave/cli/v2/cmd/urfave-cli-genflags
GOBUILD_FLAGS ?= -x

.PHONY: all
all: test build smoke-test

.PHONY: test
test:
go test $(GOTEST_FLAGS) ./...

.PHONY: build
build:
go build $(GOBUILD_FLAGS) ./...

.PHONY: smoke-test
smoke-test: build
./urfave-cli-genflags --help

.PHONY: show-cover
show-cover:
go tool cover -func main.coverprofile
15 changes: 15 additions & 0 deletions cmd/urfave-cli-genflags/README.md
@@ -0,0 +1,15 @@
# urfave-cli-genflags

This is a tool that is used internally by [urfave/cli] to generate
flag types and methods from a YAML input. It intentionally pins
usage of `github.com/urfave/cli/v2` to a *release* rather than
using the adjacent code so that changes don't result in *this* tool
refusing to compile. It's almost like dogfooding?

## support warning

This tool is maintained as a sub-project and is not covered by the
API and backward compatibility guaranteed by releases of
[urfave/cli].

[urfave/cli]: https://github.com/urfave/cli
15 changes: 15 additions & 0 deletions cmd/urfave-cli-genflags/go.mod
@@ -0,0 +1,15 @@
module github.com/urfave/cli/v2/cmd/urfave-cli-genflags

go 1.18

require (
github.com/urfave/cli/v2 v2.11.2
golang.org/x/text v0.3.7
gopkg.in/yaml.v3 v3.0.1
)

require (
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
)
17 changes: 17 additions & 0 deletions cmd/urfave-cli-genflags/go.sum
@@ -0,0 +1,17 @@
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/urfave/cli/v2 v2.11.2 h1:FVfNg4m3vbjbBpLYxW//WjxUoHvJ9TlppXcqY9Q9ZfA=
github.com/urfave/cli/v2 v2.11.2/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Expand Up @@ -9,19 +9,31 @@ import (
"os/exec"
"os/signal"
"path/filepath"
"sort"
"strings"
"syscall"
"text/template"

"github.com/urfave/cli/v2"
"github.com/urfave/cli/v2/internal/genflags"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"gopkg.in/yaml.v3"
)

const (
defaultPackageName = "cli"
)

var (
//go:embed generated.gotmpl
TemplateString string

//go:embed generated_test.gotmpl
TestTemplateString string

titler = cases.Title(language.Und, cases.NoLower)
)

func sh(ctx context.Context, exe string, args ...string) (string, error) {
cmd := exec.CommandContext(ctx, exe, args...)
cmd.Stderr = os.Stderr
Expand Down Expand Up @@ -92,7 +104,7 @@ func runGenFlags(cCtx *cli.Context) error {
return err
}

spec := &genflags.Spec{}
spec := &Spec{}
if err := yaml.Unmarshal(specBytes, spec); err != nil {
return err
}
Expand Down Expand Up @@ -123,12 +135,12 @@ func runGenFlags(cCtx *cli.Context) error {
spec.UrfaveCLITestNamespace = "cli."
}

genTmpl, err := template.New("gen").Parse(genflags.TemplateString)
genTmpl, err := template.New("gen").Parse(TemplateString)
if err != nil {
return err
}

genTestTmpl, err := template.New("gen_test").Parse(genflags.TestTemplateString)
genTestTmpl, err := template.New("gen_test").Parse(TestTemplateString)
if err != nil {
return err
}
Expand Down Expand Up @@ -161,3 +173,120 @@ func runGenFlags(cCtx *cli.Context) error {

return nil
}

func TypeName(goType string, fc *FlagTypeConfig) string {
if fc != nil && strings.TrimSpace(fc.TypeName) != "" {
return strings.TrimSpace(fc.TypeName)
}

dotSplit := strings.Split(goType, ".")
goType = dotSplit[len(dotSplit)-1]

if strings.HasPrefix(goType, "[]") {
return titler.String(strings.TrimPrefix(goType, "[]")) + "SliceFlag"
}

return titler.String(goType) + "Flag"
}

type Spec struct {
FlagTypes map[string]*FlagTypeConfig `yaml:"flag_types"`
PackageName string `yaml:"package_name"`
TestPackageName string `yaml:"test_package_name"`
UrfaveCLINamespace string `yaml:"urfave_cli_namespace"`
UrfaveCLITestNamespace string `yaml:"urfave_cli_test_namespace"`
}

func (gfs *Spec) SortedFlagTypes() []*FlagType {
typeNames := []string{}

for name := range gfs.FlagTypes {
if strings.HasPrefix(name, "[]") {
name = strings.TrimPrefix(name, "[]") + "Slice"
}

typeNames = append(typeNames, name)
}

sort.Strings(typeNames)

ret := make([]*FlagType, len(typeNames))

for i, typeName := range typeNames {
ret[i] = &FlagType{
GoType: typeName,
Config: gfs.FlagTypes[typeName],
}
}

return ret
}

type FlagTypeConfig struct {
SkipInterfaces []string `yaml:"skip_interfaces"`
StructFields []*FlagStructField `yaml:"struct_fields"`
TypeName string `yaml:"type_name"`
ValuePointer bool `yaml:"value_pointer"`
}

type FlagStructField struct {
Name string
Type string
}

type FlagType struct {
GoType string
Config *FlagTypeConfig
}

func (ft *FlagType) StructFields() []*FlagStructField {
if ft.Config == nil || ft.Config.StructFields == nil {
return []*FlagStructField{}
}

return ft.Config.StructFields
}

func (ft *FlagType) ValuePointer() bool {
if ft.Config == nil {
return false
}

return ft.Config.ValuePointer
}

func (ft *FlagType) TypeName() string {
return TypeName(ft.GoType, ft.Config)
}

func (ft *FlagType) GenerateFmtStringerInterface() bool {
return ft.skipInterfaceNamed("fmt.Stringer")
}

func (ft *FlagType) GenerateFlagInterface() bool {
return ft.skipInterfaceNamed("Flag")
}

func (ft *FlagType) GenerateRequiredFlagInterface() bool {
return ft.skipInterfaceNamed("RequiredFlag")
}

func (ft *FlagType) GenerateVisibleFlagInterface() bool {
return ft.skipInterfaceNamed("VisibleFlag")
}

func (ft *FlagType) skipInterfaceNamed(name string) bool {
if ft.Config == nil {
return true
}

lowName := strings.ToLower(name)

for _, interfaceName := range ft.Config.SkipInterfaces {
if strings.ToLower(interfaceName) == lowName {
return false
}
}

return true
}
@@ -1,42 +1,76 @@
package genflags_test
package main_test

import (
"fmt"
"reflect"
"testing"

"github.com/urfave/cli/v2/internal/genflags"
main "github.com/urfave/cli/v2/cmd/urfave-cli-genflags"
)

func TestTypeName(t *testing.T) {
for _, tc := range []struct {
gt string
fc *main.FlagTypeConfig
expected string
}{
{gt: "int", fc: nil, expected: "IntFlag"},
{gt: "int", fc: &main.FlagTypeConfig{}, expected: "IntFlag"},
{gt: "int", fc: &main.FlagTypeConfig{TypeName: "VeryIntyFlag"}, expected: "VeryIntyFlag"},
{gt: "[]bool", fc: nil, expected: "BoolSliceFlag"},
{gt: "[]bool", fc: &main.FlagTypeConfig{}, expected: "BoolSliceFlag"},
{gt: "[]bool", fc: &main.FlagTypeConfig{TypeName: "ManyTruthsFlag"}, expected: "ManyTruthsFlag"},
{gt: "time.Rumination", fc: nil, expected: "RuminationFlag"},
{gt: "time.Rumination", fc: &main.FlagTypeConfig{}, expected: "RuminationFlag"},
{gt: "time.Rumination", fc: &main.FlagTypeConfig{TypeName: "PonderFlag"}, expected: "PonderFlag"},
} {
t.Run(
fmt.Sprintf("type=%s,cfg=%v", tc.gt, func() string {
if tc.fc != nil {
return tc.fc.TypeName
}
return "nil"
}()),
func(ct *testing.T) {
actual := main.TypeName(tc.gt, tc.fc)
if tc.expected != actual {
ct.Errorf("expected %q, got %q", tc.expected, actual)
}
},
)
}
}

func TestSpec_SortedFlagTypes(t *testing.T) {
spec := &genflags.Spec{
FlagTypes: map[string]*genflags.FlagTypeConfig{
"nerf": &genflags.FlagTypeConfig{},
spec := &main.Spec{
FlagTypes: map[string]*main.FlagTypeConfig{
"nerf": &main.FlagTypeConfig{},
"gerf": nil,
},
}

actual := spec.SortedFlagTypes()
expected := []*genflags.FlagType{
expected := []*main.FlagType{
{
GoType: "gerf",
Config: nil,
},
{
GoType: "nerf",
Config: &genflags.FlagTypeConfig{},
Config: &main.FlagTypeConfig{},
},
}
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected %#v, got %#v", expected, actual)
}
}

func genFlagType() *genflags.FlagType {
return &genflags.FlagType{
func genFlagType() *main.FlagType {
return &main.FlagType{
GoType: "blerf",
Config: &genflags.FlagTypeConfig{
Config: &main.FlagTypeConfig{
SkipInterfaces: []string{"fmt.Stringer"},
StructFields: []*genflags.FlagStructField{
StructFields: []*main.FlagStructField{
{
Name: "Foibles",
Type: "int",
Expand Down
2 changes: 1 addition & 1 deletion docs/CONTRIBUTING.md
Expand Up @@ -95,7 +95,7 @@ The built-in `go generate` command is used to run the commands specified in
line help system which may be consulted for further information, e.g.:

```sh
go run internal/genflags/cmd/genflags/main.go --help
go run cmd/urfave-cli-genflags/main.go --help
```

#### docs output
Expand Down
4 changes: 2 additions & 2 deletions flag-spec.yaml
@@ -1,6 +1,6 @@
# NOTE: this file is used by the tool defined in
# ./internal/genflags/cmd/genflags/main.go which uses the
# `genflags.Spec` type that maps to this file structure.
# ./cmd/urfave-cli-genflags/main.go which uses the
# `Spec` type that maps to this file structure.

flag_types:
bool: {}
Expand Down
1 change: 0 additions & 1 deletion go.mod
Expand Up @@ -6,7 +6,6 @@ require (
github.com/BurntSushi/toml v1.1.0
github.com/cpuguy83/go-md2man/v2 v2.0.2
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673
golang.org/x/text v0.3.7
gopkg.in/yaml.v3 v3.0.1
)

Expand Down