Merge branch 'master' into lynncyrin-patch-6

This commit is contained in:
lynn [they] 2019-12-24 06:05:23 -08:00 committed by GitHub
commit a12e06e3fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 248 additions and 14 deletions

View File

@ -4,6 +4,7 @@ on:
push: push:
branches: branches:
- master - master
- v1
pull_request: pull_request:
branches: branches:
- master - master

View File

@ -1,7 +1,6 @@
cli cli
=== ===
[![Build Status](https://travis-ci.org/urfave/cli.svg?branch=master)](https://travis-ci.org/urfave/cli)
[![Windows Build Status](https://ci.appveyor.com/api/projects/status/rtgk5xufi932pb2v?svg=true)](https://ci.appveyor.com/project/urfave/cli) [![Windows Build Status](https://ci.appveyor.com/api/projects/status/rtgk5xufi932pb2v?svg=true)](https://ci.appveyor.com/project/urfave/cli)
[![GoDoc](https://godoc.org/github.com/urfave/cli?status.svg)](https://godoc.org/github.com/urfave/cli) [![GoDoc](https://godoc.org/github.com/urfave/cli?status.svg)](https://godoc.org/github.com/urfave/cli)
@ -20,6 +19,12 @@ Usage documentation exists for each major version. Don't know what version you'r
- `v2` - [./docs/v2/manual.md](./docs/v2/manual.md) - `v2` - [./docs/v2/manual.md](./docs/v2/manual.md)
- `v1` - [./docs/v1/manual.md](./docs/v1/manual.md) - `v1` - [./docs/v1/manual.md](./docs/v1/manual.md)
## Installation
Make sure you have a working Go environment. Go version 1.11+ is supported. [See the install instructions for Go](http://golang.org/doc/install.html).
Go Modules are strongly recommended when using this package. [See the go blog guide on using Go Modules](https://blog.golang.org/using-go-modules).
### Using `v2` releases ### Using `v2` releases
``` ```
@ -37,7 +42,7 @@ import (
### Using `v1` releases ### Using `v1` releases
``` ```
$ go get github.com/urfave/cli $ GO111MODULE=on go get github.com/urfave/cli
``` ```
```go ```go
@ -48,11 +53,6 @@ import (
... ...
``` ```
## Installation
Make sure you have a working Go environment. Go version 1.10+ is supported. [See
the install instructions for Go](http://golang.org/doc/install.html).
### GOPATH ### GOPATH
Make sure your `PATH` includes the `$GOPATH/bin` directory so your commands can Make sure your `PATH` includes the `$GOPATH/bin` directory so your commands can
@ -64,5 +64,5 @@ export PATH=$PATH:$GOPATH/bin
### Supported platforms ### Supported platforms
cli is tested against multiple versions of Go on Linux, and against the latest cli is tested against multiple versions of Go on Linux, and against the latest
released version of Go on OS X and Windows. For full details, see released version of Go on OS X and Windows. This project uses Github Actions for
[`./.travis.yml`](./.travis.yml) and [`./appveyor.yml`](./appveyor.yml). builds. For more build info, please look at the [./.github/workflows/cli.yml](https://github.com/urfave/cli/blob/master/.github/workflows/cli.yml).

View File

@ -48,8 +48,8 @@ func (a *App) writeDocTemplate(w io.Writer) error {
return t.ExecuteTemplate(w, name, &cliTemplate{ return t.ExecuteTemplate(w, name, &cliTemplate{
App: a, App: a,
Commands: prepareCommands(a.Commands, 0), Commands: prepareCommands(a.Commands, 0),
GlobalArgs: prepareArgsWithValues(a.Flags), GlobalArgs: prepareArgsWithValues(a.VisibleFlags()),
SynopsisArgs: prepareArgsSynopsis(a.Flags), SynopsisArgs: prepareArgsSynopsis(a.VisibleFlags()),
}) })
} }

View File

@ -6,6 +6,27 @@
View [unreleased 2.X] series changes. View [unreleased 2.X] series changes.
## [2.1.0] - 2019-12-24
These release notes were written for the git hash [ae84df4cef4a2a6f1a0cb1d41ea0f3af8755e5a8](https://github.com/urfave/cli/tree/ae84df4cef4a2a6f1a0cb1d41ea0f3af8755e5a8)
### Fixed
* Fixed some golint errors in [urfave/cli/pull/988](https://github.com/urfave/cli/pull/988) via [@liamchampton](https://github.com/liamchampton)
* Fixed a panic with flag completion [urfave/cli/pull/946](https://github.com/urfave/cli/pull/946) via [@unRob](https://github.com/unRob)
### Changed
* Changed docs generation to use visible flags in [urfave/cli/pull/999](https://github.com/urfave/cli/pull/999) via [@subpop](https://github.com/subpop)
* Changed `App.Run` to use an optional context for timeouts and cancellation in [urfave/cli/pull/975](https://github.com/urfave/cli/pull/975) via [@marwan-at-work](https://github.com/marwan-at-work)
* Changed version info to be hidden if the user has not defined a version in [urfave/cli/pull/955](https://github.com/urfave/cli/pull/955) via [@asahasrabuddhe](https://github.com/asahasrabuddhe)
* Changed docs generation to take into account multiple authors in [urfave/cli/pull/900](https://github.com/urfave/cli/pull/900) via [@saschagrunert](https://github.com/saschagrunert)
* Changed context to expose a `Value` accessor in [urfave/cli/pull/741](https://github.com/urfave/cli/pull/741) via [@corruptmemory](https://github.com/corruptmemory)
### Added
* Added timestamp flag in [urfave/cli/pull/987](https://github.com/urfave/cli/pull/987) via [@drov0](https://github.com/drov0)
## [2.0.0] - 2019-11-17 ## [2.0.0] - 2019-11-17
The V2 changes were all shipped in [urfave/cli/pull/892](https://github.com/urfave/cli/pull/892), which was created with the effort of over a dozen participants! They are: The V2 changes were all shipped in [urfave/cli/pull/892](https://github.com/urfave/cli/pull/892), which was created with the effort of over a dozen participants! They are:
@ -520,7 +541,8 @@ signature of `func(*cli.Context) error`, as defined by `cli.ActionFunc`.
### Added ### Added
- Initial implementation. - Initial implementation.
[unreleased 2.X]: https://github.com/urfave/cli/compare/v2.0.0...HEAD [unreleased 2.X]: https://github.com/urfave/cli/compare/v2.1.0...HEAD
[2.1.0]: https://github.com/urfave/cli/compare/v2.0.0...v2.1.0
[2.0.0]: https://github.com/urfave/cli/compare/v1.22.2...v2.0.0 [2.0.0]: https://github.com/urfave/cli/compare/v1.22.2...v2.0.0
[unreleased 1.22.X]: https://github.com/urfave/cli/compare/v1.22.2...v1 [unreleased 1.22.X]: https://github.com/urfave/cli/compare/v1.22.2...v1

View File

@ -22,6 +22,10 @@ func testApp() *App {
Aliases: []string{"b"}, Aliases: []string{"b"},
Usage: "another usage text", Usage: "another usage text",
}, },
&BoolFlag{
Name: "hidden-flag",
Hidden: true,
},
} }
app.Commands = []*Command{{ app.Commands = []*Command{{
Aliases: []string{"c"}, Aliases: []string{"c"},

View File

@ -1678,3 +1678,58 @@ func TestInt64Slice_Serialized_Set(t *testing.T) {
t.Fatalf("pre and post serialization do not match: %v != %v", sl0, sl1) t.Fatalf("pre and post serialization do not match: %v != %v", sl0, sl1)
} }
} }
func TestTimestamp_set(t *testing.T) {
ts := Timestamp{
timestamp: nil,
hasBeenSet: false,
layout: "Jan 2, 2006 at 3:04pm (MST)",
}
time1 := "Feb 3, 2013 at 7:54pm (PST)"
if err := ts.Set(time1); err != nil {
t.Fatalf("Failed to parse time %s with layout %s", time1, ts.layout)
}
if ts.hasBeenSet == false {
t.Fatalf("hasBeenSet is not true after setting a time")
}
ts.hasBeenSet = false
ts.SetLayout(time.RFC3339)
time2 := "2006-01-02T15:04:05Z"
if err := ts.Set(time2); err != nil {
t.Fatalf("Failed to parse time %s with layout %s", time2, ts.layout)
}
if ts.hasBeenSet == false {
t.Fatalf("hasBeenSet is not true after setting a time")
}
}
func TestTimestampFlagApply(t *testing.T) {
expectedResult, _ := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
fl := TimestampFlag{Name: "time", Aliases: []string{"t"}, Layout: time.RFC3339}
set := flag.NewFlagSet("test", 0)
_ = fl.Apply(set)
err := set.Parse([]string{"--time", "2006-01-02T15:04:05Z"})
expect(t, err, nil)
expect(t, *fl.Value.timestamp, expectedResult)
}
func TestTimestampFlagApply_Fail_Parse_Wrong_Layout(t *testing.T) {
fl := TimestampFlag{Name: "time", Aliases: []string{"t"}, Layout: "randomlayout"}
set := flag.NewFlagSet("test", 0)
_ = fl.Apply(set)
err := set.Parse([]string{"--time", "2006-01-02T15:04:05Z"})
expect(t, err, fmt.Errorf("invalid value \"2006-01-02T15:04:05Z\" for flag -time: parsing time \"2006-01-02T15:04:05Z\" as \"randomlayout\": cannot parse \"2006-01-02T15:04:05Z\" as \"randomlayout\""))
}
func TestTimestampFlagApply_Fail_Parse_Wrong_Time(t *testing.T) {
fl := TimestampFlag{Name: "time", Aliases: []string{"t"}, Layout: "Jan 2, 2006 at 3:04pm (MST)"}
set := flag.NewFlagSet("test", 0)
_ = fl.Apply(set)
err := set.Parse([]string{"--time", "2006-01-02T15:04:05Z"})
expect(t, err, fmt.Errorf("invalid value \"2006-01-02T15:04:05Z\" for flag -time: parsing time \"2006-01-02T15:04:05Z\" as \"Jan 2, 2006 at 3:04pm (MST)\": cannot parse \"2006-01-02T15:04:05Z\" as \"Jan\""))
}

152
flag_timestamp.go Normal file
View File

@ -0,0 +1,152 @@
package cli
import (
"flag"
"fmt"
"time"
)
// Timestamp wrap to satisfy golang's flag interface.
type Timestamp struct {
timestamp *time.Time
hasBeenSet bool
layout string
}
// Timestamp constructor
func NewTimestamp(timestamp time.Time) *Timestamp {
return &Timestamp{timestamp: &timestamp}
}
// Set the timestamp value directly
func (t *Timestamp) SetTimestamp(value time.Time) {
if !t.hasBeenSet {
t.timestamp = &value
t.hasBeenSet = true
}
}
// Set the timestamp string layout for future parsing
func (t *Timestamp) SetLayout(layout string) {
t.layout = layout
}
// Parses the string value to timestamp
func (t *Timestamp) Set(value string) error {
timestamp, err := time.Parse(t.layout, value)
if err != nil {
return err
}
t.timestamp = &timestamp
t.hasBeenSet = true
return nil
}
// String returns a readable representation of this value (for usage defaults)
func (t *Timestamp) String() string {
return fmt.Sprintf("%#v", t.timestamp)
}
// Value returns the timestamp value stored in the flag
func (t *Timestamp) Value() *time.Time {
return t.timestamp
}
// Get returns the flag structure
func (t *Timestamp) Get() interface{} {
return *t
}
// TimestampFlag is a flag with type time
type TimestampFlag struct {
Name string
Aliases []string
Usage string
EnvVars []string
FilePath string
Required bool
Hidden bool
Layout string
Value *Timestamp
DefaultText string
HasBeenSet bool
}
// IsSet returns whether or not the flag has been set through env or file
func (f *TimestampFlag) IsSet() bool {
return f.HasBeenSet
}
// String returns a readable representation of this value
// (for usage defaults)
func (f *TimestampFlag) String() string {
return FlagStringer(f)
}
// Names returns the names of the flag
func (f *TimestampFlag) Names() []string {
return flagNames(f)
}
// IsRequired returns whether or not the flag is required
func (f *TimestampFlag) IsRequired() bool {
return f.Required
}
// TakesValue returns true of the flag takes a value, otherwise false
func (f *TimestampFlag) TakesValue() bool {
return true
}
// GetUsage returns the usage string for the flag
func (f *TimestampFlag) GetUsage() string {
return f.Usage
}
// GetValue returns the flags value as string representation and an empty
// string if the flag takes no value at all.
func (f *TimestampFlag) GetValue() string {
if f.Value != nil {
return f.Value.timestamp.String()
}
return ""
}
// Apply populates the flag given the flag set and environment
func (f *TimestampFlag) Apply(set *flag.FlagSet) error {
if f.Layout == "" {
return fmt.Errorf("timestamp Layout is required")
}
f.Value = &Timestamp{}
f.Value.SetLayout(f.Layout)
if val, ok := flagFromEnvOrFile(f.EnvVars, f.FilePath); ok {
if err := f.Value.Set(val); err != nil {
return fmt.Errorf("could not parse %q as timestamp value for flag %s: %s", val, f.Name, err)
}
f.HasBeenSet = true
}
for _, name := range f.Names() {
set.Var(f.Value, name, f.Usage)
}
return nil
}
// Timestamp gets the timestamp from a flag name
func (c *Context) Timestamp(name string) *time.Time {
if fs := lookupFlagSet(name, c); fs != nil {
return lookupTimestamp(name, fs)
}
return nil
}
// Fetches the timestamp value from the local timestampWrap
func lookupTimestamp(name string, set *flag.FlagSet) *time.Time {
f := set.Lookup(name)
if f != nil {
return (f.Value.(*Timestamp)).Value()
}
return nil
}