From 897e4ce3c1383003f0a482d1b296e0dfce27a28c Mon Sep 17 00:00:00 2001 From: Norwin Date: Fri, 6 Mar 2020 03:43:28 +0000 Subject: [PATCH] add `tea times` command (#54) Merge branch 'master' into 50-cmd-times labels: fix refactor bug fixup! use version check implemented in SDK instead add subcmds: `tea times (delete|reset)` fixes #87 fixes #88 times: reword help use version check implemented in SDK instead make fmt Check gitea server version for times endpoint refactor times.go dont print TrackedTime ID print username & issue index instead of IDs switch to urface/cli/v2 vendor araddon/dateparse use araddon/dateparse for arbitrary date inputs add --from, --until flags allow filtering by issue index make app name lower case to make the help texts consistent with the binary name add --total flag implement `tea times add` add `tea times` subcommand Co-authored-by: Lunny Xiao Co-authored-by: Norwin Roosen Reviewed-on: https://gitea.com/gitea/tea/pulls/54 Reviewed-by: 6543 <6543@noreply.gitea.io> Reviewed-by: Lunny Xiao --- cmd/flags.go | 12 +- cmd/issues.go | 10 +- cmd/labels.go | 5 +- cmd/login.go | 16 +- cmd/logout.go | 5 +- cmd/releases.go | 25 +- cmd/times.go | 274 +++ go.mod | 1 + go.sum | 2 + main.go | 3 +- .../github.com/araddon/dateparse/.travis.yml | 14 + vendor/github.com/araddon/dateparse/LICENSE | 21 + vendor/github.com/araddon/dateparse/README.md | 282 +++ .../github.com/araddon/dateparse/parseany.go | 1864 +++++++++++++++++ vendor/modules.txt | 2 + 15 files changed, 2507 insertions(+), 29 deletions(-) create mode 100644 cmd/times.go create mode 100644 vendor/github.com/araddon/dateparse/.travis.yml create mode 100644 vendor/github.com/araddon/dateparse/LICENSE create mode 100644 vendor/github.com/araddon/dateparse/README.md create mode 100644 vendor/github.com/araddon/dateparse/parseany.go diff --git a/cmd/flags.go b/cmd/flags.go index 21a684c..998d992 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -21,28 +21,32 @@ var ( // LoginFlag provides flag to specify tea login profile var LoginFlag = cli.StringFlag{ - Name: "login, l", + Name: "login", + Aliases: []string{"l"}, Usage: "Use a different Gitea login. Optional", Destination: &loginValue, } // RepoFlag provides flag to specify repository var RepoFlag = cli.StringFlag{ - Name: "repo, r", + Name: "repo", + Aliases: []string{"r"}, Usage: "Repository to interact with. Optional", Destination: &repoValue, } // RemoteFlag provides flag to specify remote repository var RemoteFlag = cli.StringFlag{ - Name: "remote, R", + Name: "remote", + Aliases: []string{"R"}, Usage: "Discover Gitea login from remote. Optional", Destination: &remoteValue, } // OutputFlag provides flag to specify output type var OutputFlag = cli.StringFlag{ - Name: "output, o", + Name: "output", + Aliases: []string{"o"}, Usage: "Output format. (csv, simple, table, tsv, yaml)", Destination: &outputValue, } diff --git a/cmd/issues.go b/cmd/issues.go index a3ccecf..5fba5a9 100644 --- a/cmd/issues.go +++ b/cmd/issues.go @@ -125,12 +125,14 @@ var CmdIssuesCreate = cli.Command{ Action: runIssuesCreate, Flags: append([]cli.Flag{ &cli.StringFlag{ - Name: "title, t", - Usage: "issue title to create", + Name: "title", + Aliases: []string{"t"}, + Usage: "issue title to create", }, &cli.StringFlag{ - Name: "body, b", - Usage: "issue body to create", + Name: "body", + Aliases: []string{"b"}, + Usage: "issue body to create", }, }, LoginRepoFlags...), } diff --git a/cmd/labels.go b/cmd/labels.go index 2419a93..72e8580 100644 --- a/cmd/labels.go +++ b/cmd/labels.go @@ -30,8 +30,9 @@ var CmdLabels = cli.Command{ }, Flags: append([]cli.Flag{ &cli.StringFlag{ - Name: "save, s", - Usage: "Save all the labels as a file", + Name: "save", + Aliases: []string{"s"}, + Usage: "Save all the labels as a file", }, }, AllDefaultFlags...), } diff --git a/cmd/login.go b/cmd/login.go index 771490e..1a5cb83 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -34,24 +34,28 @@ var cmdLoginAdd = cli.Command{ Description: `Add a Gitea login`, Flags: []cli.Flag{ &cli.StringFlag{ - Name: "name, n", - Usage: "Login name", + Name: "name", + Aliases: []string{"n"}, + Usage: "Login name", }, &cli.StringFlag{ - Name: "url, u", + Name: "url", + Aliases: []string{"u"}, Value: "https://try.gitea.io", EnvVars: []string{"GITEA_SERVER_URL"}, Usage: "Server URL", }, &cli.StringFlag{ - Name: "token, t", + Name: "token", + Aliases: []string{"t"}, Value: "", EnvVars: []string{"GITEA_SERVER_TOKEN"}, Usage: "Access token. Can be obtained from Settings > Applications", }, &cli.BoolFlag{ - Name: "insecure, i", - Usage: "Disable TLS verification", + Name: "insecure", + Aliases: []string{"i"}, + Usage: "Disable TLS verification", }, }, Action: runLoginAdd, diff --git a/cmd/logout.go b/cmd/logout.go index 0335bed..192d519 100644 --- a/cmd/logout.go +++ b/cmd/logout.go @@ -20,8 +20,9 @@ var CmdLogout = cli.Command{ Action: runLogout, Flags: []cli.Flag{ &cli.StringFlag{ - Name: "name, n", - Usage: "Login name to remove", + Name: "name", + Aliases: []string{"n"}, + Usage: "Login name to remove", }, }, } diff --git a/cmd/releases.go b/cmd/releases.go index 09b874f..788f831 100644 --- a/cmd/releases.go +++ b/cmd/releases.go @@ -80,24 +80,29 @@ var CmdReleaseCreate = cli.Command{ Usage: "Target refs, branch name or commit id", }, &cli.StringFlag{ - Name: "title, t", - Usage: "Release title", + Name: "title", + Aliases: []string{"t"}, + Usage: "Release title", }, &cli.StringFlag{ - Name: "note, n", - Usage: "Release notes", + Name: "note", + Aliases: []string{"n"}, + Usage: "Release notes", }, &cli.BoolFlag{ - Name: "draft, d", - Usage: "Is a draft", + Name: "draft", + Aliases: []string{"d"}, + Usage: "Is a draft", }, &cli.BoolFlag{ - Name: "prerelease, p", - Usage: "Is a pre-release", + Name: "prerelease", + Aliases: []string{"p"}, + Usage: "Is a pre-release", }, &cli.StringSliceFlag{ - Name: "asset, a", - Usage: "List of files to attach", + Name: "asset", + Aliases: []string{"a"}, + Usage: "List of files to attach", }, }, LoginRepoFlags...), } diff --git a/cmd/times.go b/cmd/times.go new file mode 100644 index 0000000..9d8d32e --- /dev/null +++ b/cmd/times.go @@ -0,0 +1,274 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "fmt" + "log" + "strconv" + "strings" + "time" + + "code.gitea.io/sdk/gitea" + + "github.com/araddon/dateparse" + "github.com/urfave/cli/v2" +) + +// CmdTrackedTimes represents the command to operate repositories' times. +var CmdTrackedTimes = cli.Command{ + Name: "times", + Aliases: []string{"time"}, + Usage: "Operate on tracked times of a repository's issues & pulls", + Description: `Operate on tracked times of a repository's issues & pulls. + Depending on your permissions on the repository, only your own tracked + times might be listed.`, + ArgsUsage: "[username | #issue]", + Action: runTrackedTimes, + Subcommands: []*cli.Command{ + &CmdTrackedTimesAdd, + &CmdTrackedTimesDelete, + &CmdTrackedTimesReset, + }, + Flags: append([]cli.Flag{ + &cli.StringFlag{ + Name: "from", + Aliases: []string{"f"}, + Usage: "Show only times tracked after this date", + }, + &cli.StringFlag{ + Name: "until", + Aliases: []string{"u"}, + Usage: "Show only times tracked before this date", + }, + &cli.BoolFlag{ + Name: "total", + Aliases: []string{"t"}, + Usage: "Print the total duration at the end", + }, + }, AllDefaultFlags...), +} + +func runTrackedTimes(ctx *cli.Context) error { + login, owner, repo := initCommand() + client := login.Client() + + if err := client.CheckServerVersionConstraint(">= 1.11"); err != nil { + return err + } + + var times []*gitea.TrackedTime + var err error + + user := ctx.Args().First() + fmt.Println(ctx.Command.ArgsUsage) + if user == "" { + // get all tracked times on the repo + times, err = client.GetRepoTrackedTimes(owner, repo) + } else if strings.HasPrefix(user, "#") { + // get all tracked times on the specified issue + issue, err2 := strconv.ParseInt(user[1:], 10, 64) + if err2 != nil { + return err2 + } + times, err = client.ListTrackedTimes(owner, repo, issue) + } else { + // get all tracked times by the specified user + times, err = client.GetUserTrackedTimes(owner, repo, user) + } + + if err != nil { + return err + } + + var from, until time.Time + if ctx.String("from") != "" { + from, err = dateparse.ParseLocal(ctx.String("from")) + if err != nil { + return err + } + } + if ctx.String("until") != "" { + until, err = dateparse.ParseLocal(ctx.String("until")) + if err != nil { + return err + } + } + + printTrackedTimes(times, outputValue, from, until, ctx.Bool("total")) + return nil +} + +func printTrackedTimes(times []*gitea.TrackedTime, outputType string, from, until time.Time, printTotal bool) { + var outputValues [][]string + var totalDuration int64 + + localLoc, err := time.LoadLocation("Local") // local timezone for time formatting + if err != nil { + log.Fatal(err) + } + + for _, t := range times { + if !from.IsZero() && from.After(t.Created) { + continue + } + if !until.IsZero() && until.Before(t.Created) { + continue + } + + totalDuration += t.Time + + outputValues = append( + outputValues, + []string{ + t.Created.In(localLoc).Format("2006-01-02 15:04:05"), + "#" + strconv.FormatInt(t.Issue.Index, 10), + t.UserName, + time.Duration(1e9 * t.Time).String(), + }, + ) + } + + if printTotal { + outputValues = append(outputValues, []string{ + "TOTAL", "", "", time.Duration(1e9 * totalDuration).String(), + }) + } + + headers := []string{ + "Created", + "Issue", + "User", + "Duration", + } + Output(outputType, headers, outputValues) +} + +// CmdTrackedTimesAdd represents a sub command of times to add time to an issue +var CmdTrackedTimesAdd = cli.Command{ + Name: "add", + Usage: "Track spent time on an issue", + UsageText: "tea times add ", + Description: `Track spent time on an issue + Example: + tea times add 1 1h25m + `, + Action: runTrackedTimesAdd, + Flags: LoginRepoFlags, +} + +func runTrackedTimesAdd(ctx *cli.Context) error { + login, owner, repo := initCommand() + + if ctx.Args().Len() < 2 { + return fmt.Errorf("No issue or duration specified.\nUsage:\t%s", ctx.Command.UsageText) + } + + issueStr := ctx.Args().First() + if strings.HasPrefix(issueStr, "#") { + issueStr = issueStr[1:] + } + issue, err := strconv.ParseInt(issueStr, 10, 64) + if err != nil { + log.Fatal(err) + } + + duration, err := time.ParseDuration(strings.Join(ctx.Args().Tail(), "")) + if err != nil { + log.Fatal(err) + } + + _, err = login.Client().AddTime(owner, repo, issue, gitea.AddTimeOption{ + Time: int64(duration.Seconds()), + }) + if err != nil { + log.Fatal(err) + } + + return nil +} + +// CmdTrackedTimesDelete is a sub command of CmdTrackedTimes, and removes time from an issue +var CmdTrackedTimesDelete = cli.Command{ + Name: "delete", + Aliases: []string{"rm"}, + Usage: "Delete a single tracked time on an issue", + UsageText: "tea times delete