Merge branch 'master' into lynncyrin-patch-6
This commit is contained in:
commit
a12e06e3fd
1
.github/workflows/cli.yml
vendored
1
.github/workflows/cli.yml
vendored
@ -4,6 +4,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
- v1
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
18
README.md
18
README.md
@ -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).
|
||||||
|
4
docs.go
4
docs.go
@ -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()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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"},
|
||||||
|
@ -22,7 +22,7 @@ type Float64Flag struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IsSet returns whether or not the flag has been set through env or file
|
// IsSet returns whether or not the flag has been set through env or file
|
||||||
func (f *Float64Flag)IsSet() bool {
|
func (f *Float64Flag) IsSet() bool {
|
||||||
return f.HasBeenSet
|
return f.HasBeenSet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
55
flag_test.go
55
flag_test.go
@ -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
152
flag_timestamp.go
Normal 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: ×tamp}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 = ×tamp
|
||||||
|
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
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user