1
0
mirror of https://gitea.com/jolheiser/sip synced 2024-11-22 11:41:59 +01:00

Refactor (#29)

Clean up docs

Signed-off-by: jolheiser <john.olheiser@gmail.com>

Add generated docs

Signed-off-by: jolheiser <john.olheiser@gmail.com>

Update go.sum

Signed-off-by: jolheiser <john.olheiser@gmail.com>

Fix remote parsing

Signed-off-by: jolheiser <john.olheiser@gmail.com>

Refactor and clean up

Signed-off-by: jolheiser <john.olheiser@gmail.com>

Co-authored-by: jolheiser <john.olheiser@gmail.com>
Reviewed-on: https://gitea.com/jolheiser/sip/pulls/29
This commit is contained in:
John Olheiser 2020-09-17 16:25:09 +00:00
parent a51305f816
commit 894a641ef8
36 changed files with 444 additions and 207 deletions

7
LICENSE Normal file

@ -0,0 +1,7 @@
Copyright 2020 John Olheiser
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

@ -32,6 +32,10 @@ fmt:
test:
$(GO) test -race ./...
.PHONY: docs
docs:
$(GO) run docs.go
.PHONY: release
release: release-dirs check-xgo release-windows release-linux release-darwin release-copy release-compress release-check

@ -1,12 +1,14 @@
# Sip (alternative)
# Sip
CLI for interacting with Gitea
[![Build Status](https://drone.gitea.com/api/badges/jolheiser/sip/status.svg)](https://drone.gitea.com/jolheiser/sip)
[Docs](docs.md)
### Features
Understands the concepts of an origin vs remote repository.
By default uses remotes `origin` and `upstream`.
By default, uses remotes `origin` and `upstream`.
If no `upstream` repository is found, `upstream` becomes synonymous with `origin` for the sake of defaults.
* Configuration `sip config`
@ -28,6 +30,12 @@ If no `upstream` repository is found, `upstream` becomes synonymous with `origin
* Create a new pull request `sip pulls create`
* Check pull request status (default based on current branch) `sip pulls status`
* Checkout a pull request to test locally `sip pulls checkout`
* Release search `sip release`
* Create a new release `sip release create`
* Attach files to an existing release `sip release attach`
* Open item in a web browser
* Repository `sip open` or `sip open owner/repo`
* Issue or PR `sip open 1234` or `sip open owner/repo/1234`
### Search filters
Sip supports certain search filters for issues/PRs.
@ -39,3 +47,7 @@ Anything in the query that doesn't match one of the below filters will be sent a
* Milestone `mileston:v1.0.0` - only the last milestone in the query will be applied
e.g. `test is:open query author:jolheiser milestone:0.2.0` will search for issues/PRs with keywords `test query` that are `open`, authored by `jolheiser`, and in the `0.2.0` milestone.
## License
[MIT](LICENSE)

@ -3,10 +3,9 @@ package cmd
import (
"errors"
"fmt"
"sync"
"gitea.com/jolheiser/sip/modules/config"
"gitea.com/jolheiser/sip/modules/git"
"gitea.com/jolheiser/sip/config"
"gitea.com/jolheiser/sip/flag"
"code.gitea.io/sdk/gitea"
"github.com/AlecAivazis/survey/v2"
@ -14,47 +13,25 @@ import (
"go.jolheiser.com/beaver/color"
)
var (
Flags = []cli.Flag{
&cli.StringFlag{
Name: "origin",
Usage: "The origin remote",
Value: config.Origin,
},
&cli.StringFlag{
Name: "upstream",
Usage: "The upstream remote",
Value: config.Upstream,
},
&cli.StringFlag{
Name: "url",
Aliases: []string{"u"},
Usage: "The base URL to the Gitea instance",
Value: getUpstreamRepo()[0],
},
&cli.StringFlag{
Name: "owner",
Aliases: []string{"o"},
Usage: "The owner to target",
Value: getUpstreamRepo()[1],
},
&cli.StringFlag{
Name: "repo",
Aliases: []string{"r"},
Usage: "The repo to target",
Value: getUpstreamRepo()[2],
},
&cli.StringFlag{
Name: "token",
Aliases: []string{"t"},
Usage: "The access token to use (by name)",
},
func NewApp(version string) *cli.App {
app := cli.NewApp()
app.Name = "Sip"
app.Usage = "Command line tool to interact with Gitea"
app.Version = version
app.Commands = []*cli.Command{
&Config,
&Tokens,
&Repo,
&Issues,
&Pulls,
&Release,
&Open,
}
originOnce sync.Once
originRepo []string
upstreamOnce sync.Once
upstreamRepo []string
)
app.Before = flag.Before
app.Flags = flag.Flags
app.EnableBashCompletion = true
return app
}
func getToken(name string) string {
for _, token := range config.Tokens {
@ -65,9 +42,9 @@ func getToken(name string) string {
return ""
}
func getClient(ctx *cli.Context, requireToken bool) (*gitea.Client, error) {
if ctx.IsSet("token") {
return gitea.NewClient(ctx.String("url"), gitea.SetToken(getToken(ctx.String("token"))))
func getClient(requireToken bool) (*gitea.Client, error) {
if flag.Token != "" {
return gitea.NewClient(flag.URL, gitea.SetToken(getToken(flag.Token)))
}
var token string
@ -93,42 +70,5 @@ func getClient(ctx *cli.Context, requireToken bool) (*gitea.Client, error) {
token = tokenMap[answer].Token
}
return gitea.NewClient(ctx.String("url"), gitea.SetToken(token))
}
func defRemote(remote, def string) string {
if remote == "" {
return def
}
return remote
}
func getUpstreamRepo() []string {
upstreamOnce.Do(func() {
var err error
upstreamRepo, err = git.GetRepo(defRemote(config.Upstream, "upstream"))
if err != nil {
upstreamRepo = getOriginRepo()
}
})
return upstreamRepo
}
func getOriginRepo() []string {
originOnce.Do(func() {
var err error
originRepo, err = git.GetRepo(defRemote(config.Origin, "origin"))
if err != nil {
originRepo = []string{"https://gitea.com", "jolheiser", "sip"}
}
})
return originRepo
}
func fullRepoURL(ctx *cli.Context) string {
return ctx.String("url") + "/" + fullName(ctx)
}
func fullName(ctx *cli.Context) string {
return ctx.String("owner") + "/" + ctx.String("repo")
return gitea.NewClient(flag.URL, gitea.SetToken(token))
}

@ -1,7 +1,7 @@
package cmd
import (
"gitea.com/jolheiser/sip/modules/config"
"gitea.com/jolheiser/sip/config"
"github.com/AlecAivazis/survey/v2"
"github.com/urfave/cli/v2"
@ -34,7 +34,7 @@ func doConfig(ctx *cli.Context) error {
return doConfigUpstream(ctx)
}
func doConfigOrigin(ctx *cli.Context) error {
func doConfigOrigin(_ *cli.Context) error {
question := &survey.Input{
Message: "Default origin name",
Default: "origin",
@ -53,7 +53,7 @@ func doConfigOrigin(ctx *cli.Context) error {
return nil
}
func doConfigUpstream(ctx *cli.Context) error {
func doConfigUpstream(_ *cli.Context) error {
question := &survey.Input{
Message: "Default upstream name",
Default: "upstream",

@ -7,9 +7,10 @@ import (
"strconv"
"strings"
"gitea.com/jolheiser/sip/modules/csv"
"gitea.com/jolheiser/sip/modules/markdown"
"gitea.com/jolheiser/sip/modules/sdk"
"gitea.com/jolheiser/sip/csv"
"gitea.com/jolheiser/sip/flag"
"gitea.com/jolheiser/sip/markdown"
"gitea.com/jolheiser/sip/sdk"
"code.gitea.io/sdk/gitea"
"github.com/AlecAivazis/survey/v2"
@ -29,7 +30,7 @@ var Issues = cli.Command{
Flags: []cli.Flag{
&cli.StringFlag{
Name: "csv",
Usage: "Output results to a CSV file",
Usage: "Output results to a CSV file at `PATH`",
},
},
}
@ -42,7 +43,7 @@ func doIssuesSearch(ctx *cli.Context) error {
}
func issuesSearch(ctx *cli.Context, pulls bool) (*gitea.Issue, error) {
client, err := getClient(ctx, false)
client, err := getClient(false)
if err != nil {
return nil, err
}
@ -51,7 +52,7 @@ func issuesSearch(ctx *cli.Context, pulls bool) (*gitea.Issue, error) {
if pulls {
typ = "pulls"
}
issues, err := queryIssues(ctx, client, pulls)
issues, err := queryIssues(client, pulls)
if err != nil {
return nil, err
}
@ -113,8 +114,8 @@ func issuesSearch(ctx *cli.Context, pulls bool) (*gitea.Issue, error) {
return issueMap[selection], nil
}
func queryIssues(ctx *cli.Context, client *gitea.Client, pulls bool) ([]*gitea.Issue, error) {
owner, repo, err := askOwnerRepo(ctx)
func queryIssues(client *gitea.Client, pulls bool) ([]*gitea.Issue, error) {
owner, repo, err := askOwnerRepo()
if err != nil {
return nil, err
}
@ -150,11 +151,15 @@ func queryIssues(ctx *cli.Context, client *gitea.Client, pulls bool) ([]*gitea.I
return filtered, nil
}
func askOwnerRepo(ctx *cli.Context) (string, string, error) {
func askOwnerRepo() (string, string, error) {
// If --owner or --repo was set, assume the user knows where they are searching
if flag.OwnerRepoCtxSet {
return flag.Owner, flag.Repo, nil
}
question := []*survey.Question{
{
Name: "repo",
Prompt: &survey.Input{Message: "Full repository name", Default: fullName(ctx)},
Prompt: &survey.Input{Message: "Full repository name", Default: flag.FullName()},
Validate: validateFullName,
},
}

@ -3,7 +3,8 @@ package cmd
import (
"fmt"
"gitea.com/jolheiser/sip/modules/markdown"
"gitea.com/jolheiser/sip/flag"
"gitea.com/jolheiser/sip/markdown"
"code.gitea.io/sdk/gitea"
"github.com/AlecAivazis/survey/v2"
@ -18,12 +19,12 @@ var IssuesCreate = cli.Command{
Action: doIssueCreate,
}
func doIssueCreate(ctx *cli.Context) error {
func doIssueCreate(_ *cli.Context) error {
fmt.Println()
url := color.New(color.FgYellow).Format(fmt.Sprintf("%s/%s/%s", ctx.String("url"), ctx.String("owner"), ctx.String("repo")))
url := color.New(color.FgYellow).Format(flag.FullURL())
fmt.Println(color.New(color.FgCyan).Format("Creating a new issue for"), url)
client, err := getClient(ctx, true)
client, err := getClient(true)
if err != nil {
return err
}
@ -67,7 +68,7 @@ func doIssueCreate(ctx *cli.Context) error {
}
}
issue, _, err := client.CreateIssue(ctx.String("owner"), ctx.String("repo"), gitea.CreateIssueOption{Title: title, Body: body})
issue, _, err := client.CreateIssue(flag.Owner, flag.Repo, gitea.CreateIssueOption{Title: title, Body: body})
if err != nil {
return err
}
@ -75,6 +76,6 @@ func doIssueCreate(ctx *cli.Context) error {
info := color.Info
cyan := color.New(color.FgCyan)
fmt.Println(info.Format("Issue"), cyan.Format(fmt.Sprintf("#%d", issue.Index)), info.Format("created!"))
fmt.Println(cyan.Format(fmt.Sprintf("%s/%s/%s/issues/%d", ctx.String("url"), ctx.String("owner"), ctx.String("repo"), issue.Index)))
fmt.Println(cyan.Format(fmt.Sprintf("%s/issues/%d", flag.FullURL(), issue.Index)))
return nil
}

@ -6,6 +6,8 @@ import (
"strconv"
"strings"
"gitea.com/jolheiser/sip/flag"
"github.com/skratchdot/open-golang/open"
"github.com/urfave/cli/v2"
)
@ -18,9 +20,8 @@ var Open = cli.Command{
}
func doOpen(ctx *cli.Context) error {
repo := fullRepoURL(ctx)
if ctx.NArg() == 0 {
return open.Run(repo)
return open.Run(flag.FullURL())
}
arg := ctx.Args().First()
@ -28,18 +29,18 @@ func doOpen(ctx *cli.Context) error {
// Check if issue or PR
issue, err := strconv.ParseInt(arg, 10, 64)
if err == nil {
return open.Run(fmt.Sprintf("%s/issues/%d", repo, issue))
return open.Run(fmt.Sprintf("%s/issues/%d", flag.FullURL(), issue))
}
// Check if overriding repository (jolheiser/sip)
ownerRepoIssue := strings.Split(arg, "/")
if len(ownerRepoIssue) == 2 {
return open.Run(fmt.Sprintf("%s/%s", ctx.String("url"), arg))
return open.Run(fmt.Sprintf("%s/%s", flag.FullURL(), arg))
}
// Check if both? (jolheiser/sip/1234)
if len(ownerRepoIssue) == 3 {
return open.Run(fmt.Sprintf("%s/%s/%s/issues/%s", ctx.String("url"), ownerRepoIssue[0], ownerRepoIssue[1], ownerRepoIssue[2]))
return open.Run(fmt.Sprintf("%s/%s/%s/issues/%s", flag.URL, ownerRepoIssue[0], ownerRepoIssue[1], ownerRepoIssue[2]))
}
return errors.New("unknown argument: leave blank to open current repo, pass issue/PR as #1234, or override repo as owner/repo")

@ -17,7 +17,7 @@ var Pulls = cli.Command{
Flags: []cli.Flag{
&cli.StringFlag{
Name: "csv",
Usage: "Output results to a CSV file",
Usage: "Output results to a CSV file at `PATH`",
},
},
}

@ -7,7 +7,8 @@ import (
"os/exec"
"strconv"
"gitea.com/jolheiser/sip/modules/config"
"gitea.com/jolheiser/sip/config"
"gitea.com/jolheiser/sip/flag"
"code.gitea.io/sdk/gitea"
"github.com/AlecAivazis/survey/v2"
@ -23,7 +24,7 @@ var PullsCheckout = cli.Command{
}
func doPullCheckout(ctx *cli.Context) error {
client, err := getClient(ctx, true)
client, err := getClient(true)
if err != nil {
return err
}
@ -57,7 +58,7 @@ func doPullCheckout(ctx *cli.Context) error {
}
}
} else {
iss, _, err := client.GetIssue(upstreamRepo[1], upstreamRepo[2], prNum.Index)
iss, _, err := client.GetIssue(flag.Upstream.Owner, flag.Upstream.Repo, prNum.Index)
if err != nil {
return err
}

@ -2,9 +2,11 @@ package cmd
import (
"fmt"
"strings"
"gitea.com/jolheiser/sip/modules/git"
"gitea.com/jolheiser/sip/modules/markdown"
"gitea.com/jolheiser/sip/flag"
"gitea.com/jolheiser/sip/git"
"gitea.com/jolheiser/sip/markdown"
"code.gitea.io/sdk/gitea"
"github.com/AlecAivazis/survey/v2"
@ -19,30 +21,32 @@ var PullsCreate = cli.Command{
Action: doPullCreate,
}
func doPullCreate(ctx *cli.Context) error {
func doPullCreate(_ *cli.Context) error {
fmt.Println()
url := color.New(color.FgYellow).Format(fmt.Sprintf("%s/%s/%s", ctx.String("url"), ctx.String("owner"), ctx.String("repo")))
url := color.New(color.FgYellow).Format(flag.FullURL())
fmt.Println(color.New(color.FgCyan).Format("Creating a new pull request for"), url)
client, err := getClient(ctx, true)
client, err := getClient(true)
if err != nil {
return err
}
upstreams, _, err := client.ListRepoBranches(getUpstreamRepo()[1], getUpstreamRepo()[2], gitea.ListRepoBranchesOptions{})
repo, _, err := client.GetRepo(flag.Upstream.Owner, flag.Upstream.Repo)
if err != nil {
return err
}
upstreams, _, err := client.ListRepoBranches(flag.Upstream.Owner, flag.Upstream.Repo, gitea.ListRepoBranchesOptions{})
if err != nil {
return err
}
bases := make([]string, len(upstreams))
defUpstream := upstreams[0].Name
defUpstream := repo.DefaultBranch
for idx, upstream := range upstreams {
if upstream.Name == "master" {
defUpstream = upstream.Name
}
bases[idx] = upstream.Name
}
origins, _, err := client.ListRepoBranches(getOriginRepo()[1], getOriginRepo()[2], gitea.ListRepoBranchesOptions{})
origins, _, err := client.ListRepoBranches(flag.Origin.Owner, flag.Origin.Repo, gitea.ListRepoBranchesOptions{})
if err != nil {
return err
}
@ -50,10 +54,10 @@ func doPullCreate(ctx *cli.Context) error {
defOrigin := origins[0].Name
for idx, origin := range origins {
originName := origin.Name
if ctx.String("owner") != getOriginRepo()[1] {
originName = getOriginRepo()[1] + ":" + originName
if flag.Owner != flag.Origin.Owner {
originName = flag.Origin.Owner + ":" + originName
}
if origin.Name == git.Branch() {
if strings.EqualFold(origin.Name, git.Branch()) {
defOrigin = originName
}
heads[idx] = originName
@ -114,10 +118,10 @@ func doPullCreate(ctx *cli.Context) error {
}
}
pull, _, err := client.CreatePullRequest(ctx.String("owner"), ctx.String("repo"), gitea.CreatePullRequestOption{Title: title, Body: body, Base: base, Head: head})
pull, _, err := client.CreatePullRequest(flag.Owner, flag.Repo, gitea.CreatePullRequestOption{Title: title, Body: body, Base: base, Head: head})
if err != nil {
if fmt.Sprint(err) == "409 Conflict" { // Hard-coded in the SDK
return existingPR(client, getUpstreamRepo()[1], getUpstreamRepo()[2], head, err)
return existingPR(client, flag.Upstream.Owner, flag.Upstream.Repo, head, err)
}
return err
}
@ -125,7 +129,7 @@ func doPullCreate(ctx *cli.Context) error {
info := color.Info
cyan := color.New(color.FgCyan)
fmt.Println(info.Format("PR"), cyan.Format(fmt.Sprintf("#%d", pull.Index)), info.Format("created!"))
fmt.Println(cyan.Format(fmt.Sprintf("%s/%s/%s/pulls/%d", ctx.String("url"), ctx.String("owner"), ctx.String("repo"), pull.Index)))
fmt.Println(cyan.Format(fmt.Sprintf("%s/pulls/%d", flag.FullURL(), pull.Index)))
return nil
}

@ -4,8 +4,9 @@ import (
"fmt"
"strconv"
"gitea.com/jolheiser/sip/modules/git"
"gitea.com/jolheiser/sip/modules/sdk"
"gitea.com/jolheiser/sip/flag"
"gitea.com/jolheiser/sip/git"
"gitea.com/jolheiser/sip/sdk"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
@ -18,15 +19,15 @@ var PullsStatus = cli.Command{
Action: doPullStatus,
}
func doPullStatus(ctx *cli.Context) error {
client, err := getClient(ctx, false)
func doPullStatus(_ *cli.Context) error {
client, err := getClient(false)
if err != nil {
return err
}
head := fmt.Sprintf("%s:%s", getOriginRepo()[1], git.Branch())
head := fmt.Sprintf("%s:%s", flag.Origin.Owner, git.Branch())
pulls, err := sdk.GetPulls(client, getUpstreamRepo()[1], getUpstreamRepo()[2], gitea.ListPullRequestsOptions{State: "all"})
pulls, err := sdk.GetPulls(client, flag.Upstream.Owner, flag.Upstream.Repo, gitea.ListPullRequestsOptions{State: "all"})
if err != nil {
return err
}

@ -4,9 +4,9 @@ import (
"fmt"
"os"
"gitea.com/jolheiser/sip/modules/csv"
"gitea.com/jolheiser/sip/modules/markdown"
"gitea.com/jolheiser/sip/modules/sdk"
"gitea.com/jolheiser/sip/csv"
"gitea.com/jolheiser/sip/markdown"
"gitea.com/jolheiser/sip/sdk"
"code.gitea.io/sdk/gitea"
"github.com/AlecAivazis/survey/v2"
@ -26,18 +26,18 @@ var Release = cli.Command{
Flags: []cli.Flag{
&cli.StringFlag{
Name: "csv",
Usage: "Output results to a CSV file",
Usage: "Output results to a CSV file at `PATH`",
},
},
}
func doRelease(ctx *cli.Context) error {
client, err := getClient(ctx, false)
client, err := getClient(false)
if err != nil {
return err
}
owner, repo, err := askOwnerRepo(ctx)
owner, repo, err := askOwnerRepo()
if err != nil {
return err
}

@ -6,7 +6,8 @@ import (
"path/filepath"
"strings"
"gitea.com/jolheiser/sip/modules/sdk"
"gitea.com/jolheiser/sip/flag"
"gitea.com/jolheiser/sip/sdk"
"code.gitea.io/sdk/gitea"
"github.com/AlecAivazis/survey/v2"
@ -21,13 +22,13 @@ var ReleaseAttach = cli.Command{
Action: doReleaseAttach,
}
func doReleaseAttach(ctx *cli.Context) error {
client, err := getClient(ctx, true)
func doReleaseAttach(_ *cli.Context) error {
client, err := getClient(true)
if err != nil {
return err
}
releases, err := sdk.GetReleases(client, ctx.String("owner"), ctx.String("repo"), gitea.ListReleasesOptions{})
releases, err := sdk.GetReleases(client, flag.Owner, flag.Repo, gitea.ListReleasesOptions{})
if err != nil {
return err
}
@ -64,15 +65,14 @@ func doReleaseAttach(ctx *cli.Context) error {
if err != nil {
return err
}
if err := attachFiles(ctx, client, release.ID, files); err != nil {
if err := attachFiles(client, release.ID, files); err != nil {
return err
}
info := color.Info
cyan := color.New(color.FgCyan)
fmt.Println(info.Format("Release"), cyan.Format(release.TagName), info.Format("updated!"))
fmt.Println(cyan.Format(fmt.Sprintf("%s/%s/%s/releases/tag/%s",
ctx.String("url"), ctx.String("owner"), ctx.String("repo"), release.TagName)))
fmt.Println(cyan.Format(fmt.Sprintf("%s/releases/tag/%s", flag.FullURL(), release.TagName)))
return nil
}
@ -89,7 +89,7 @@ func fileGlobs(globList string) ([]string, error) {
return files, nil
}
func attachFiles(ctx *cli.Context, client *gitea.Client, releaseID int64, files []string) error {
func attachFiles(client *gitea.Client, releaseID int64, files []string) error {
beaver.Infof("Attachments:\n\t%s", strings.Join(files, "\n\t"))
var confirm bool
@ -104,7 +104,7 @@ func attachFiles(ctx *cli.Context, client *gitea.Client, releaseID int64, files
return err
}
if _, _, err := client.CreateReleaseAttachment(ctx.String("owner"), ctx.String("repo"), releaseID, fi, fi.Name()); err != nil {
if _, _, err := client.CreateReleaseAttachment(flag.Owner, flag.Repo, releaseID, fi, fi.Name()); err != nil {
return err
}

@ -3,7 +3,8 @@ package cmd
import (
"fmt"
"gitea.com/jolheiser/sip/modules/git"
"gitea.com/jolheiser/sip/flag"
"gitea.com/jolheiser/sip/git"
"code.gitea.io/sdk/gitea"
"github.com/AlecAivazis/survey/v2"
@ -18,8 +19,8 @@ var ReleaseCreate = cli.Command{
Action: doReleaseCreate,
}
func doReleaseCreate(ctx *cli.Context) error {
client, err := getClient(ctx, true)
func doReleaseCreate(_ *cli.Context) error {
client, err := getClient(true)
if err != nil {
return err
}
@ -73,7 +74,7 @@ func doReleaseCreate(ctx *cli.Context) error {
return err
}
release, _, err := client.CreateRelease(ctx.String("owner"), ctx.String("repo"), gitea.CreateReleaseOption{
release, _, err := client.CreateRelease(flag.Owner, flag.Repo, gitea.CreateReleaseOption{
TagName: answers.Tag,
Target: answers.Target,
Title: answers.Title,
@ -90,7 +91,7 @@ func doReleaseCreate(ctx *cli.Context) error {
if err != nil {
return err
}
if err := attachFiles(ctx, client, release.ID, files); err != nil {
if err := attachFiles(client, release.ID, files); err != nil {
return err
}
}
@ -98,7 +99,6 @@ func doReleaseCreate(ctx *cli.Context) error {
info := color.Info
cyan := color.New(color.FgCyan)
fmt.Println(info.Format("Release"), cyan.Format(release.TagName), info.Format("created!"))
fmt.Println(cyan.Format(fmt.Sprintf("%s/%s/%s/releases/tag/%s",
ctx.String("url"), ctx.String("owner"), ctx.String("repo"), release.TagName)))
fmt.Println(cyan.Format(fmt.Sprintf("%s/releases/tag/%s", flag.FullURL(), release.TagName)))
return nil
}

@ -3,7 +3,8 @@ package cmd
import (
"strconv"
"gitea.com/jolheiser/sip/modules/sdk"
"gitea.com/jolheiser/sip/flag"
"gitea.com/jolheiser/sip/sdk"
"code.gitea.io/sdk/gitea"
"github.com/urfave/cli/v2"
@ -20,18 +21,18 @@ var Repo = cli.Command{
},
}
func doRepo(ctx *cli.Context) error {
client, err := getClient(ctx, false)
func doRepo(_ *cli.Context) error {
client, err := getClient(false)
if err != nil {
return err
}
repo, _, err := client.GetRepo(ctx.String("owner"), ctx.String("repo"))
repo, _, err := client.GetRepo(flag.Owner, flag.Repo)
if err != nil {
return err
}
issues, err := sdk.GetIssues(client, ctx.String("owner"), ctx.String("repo"), gitea.ListIssueOption{State: "open"})
issues, err := sdk.GetIssues(client, flag.Owner, flag.Repo, gitea.ListIssueOption{State: "open"})
if err != nil {
return err
}

@ -16,8 +16,8 @@ var RepoCreate = cli.Command{
Action: doRepoCreate,
}
func doRepoCreate(ctx *cli.Context) error {
client, err := getClient(ctx, true)
func doRepoCreate(_ *cli.Context) error {
client, err := getClient(true)
if err != nil {
return err
}

@ -1,7 +1,7 @@
package cmd
import (
"gitea.com/jolheiser/sip/modules/config"
"gitea.com/jolheiser/sip/config"
"github.com/urfave/cli/v2"
"go.jolheiser.com/beaver"
@ -19,7 +19,7 @@ var Tokens = cli.Command{
},
}
func doTokenList(ctx *cli.Context) error {
func doTokenList(_ *cli.Context) error {
if len(config.Tokens) == 0 {
beaver.Errorf("No tokens found! Add one with %s", color.FgMagenta.Format("sip token create"))
}

@ -3,7 +3,8 @@ package cmd
import (
"errors"
"gitea.com/jolheiser/sip/modules/config"
"gitea.com/jolheiser/sip/config"
"gitea.com/jolheiser/sip/flag"
"code.gitea.io/sdk/gitea"
"github.com/AlecAivazis/survey/v2"
@ -28,7 +29,7 @@ func doTokenAdd(ctx *cli.Context) error {
},
{
Name: "url",
Prompt: &survey.Input{Message: "URL for the Gitea instance", Default: ctx.String("url")},
Prompt: &survey.Input{Message: "URL for the Gitea instance", Default: flag.URL},
Validate: survey.Required,
},
}

@ -3,7 +3,7 @@ package cmd
import (
"fmt"
"gitea.com/jolheiser/sip/modules/config"
"gitea.com/jolheiser/sip/config"
"github.com/AlecAivazis/survey/v2"
"github.com/urfave/cli/v2"
@ -17,7 +17,7 @@ var TokensRemove = cli.Command{
Action: doTokenRemove,
}
func doTokenRemove(ctx *cli.Context) error {
func doTokenRemove(_ *cli.Context) error {
opts := make([]string, len(config.Tokens))
for idx, token := range config.Tokens {
opts[idx] = fmt.Sprintf("%s (%s)", token.Name, token.URL)

@ -7,15 +7,12 @@ import (
"github.com/BurntSushi/toml"
"github.com/mitchellh/go-homedir"
"go.jolheiser.com/beaver"
)
var (
configPath string
cfg *config
// Config items
Origin string
Upstream string
Tokens []Token
@ -34,46 +31,49 @@ type Token struct {
}
// Load on init so that CLI contexts are correctly populated
func init() {
func Init() error {
home, err := homedir.Dir()
if err != nil {
beaver.Fatalf("could not locate home directory: %v", err)
return err
}
configPath = fmt.Sprintf("%s/.sip/config.toml", home)
if _, err := os.Stat(configPath); os.IsNotExist(err) {
if err := os.MkdirAll(path.Dir(configPath), os.ModePerm); err != nil {
beaver.Fatalf("could not create Sip home: %v", err)
return err
}
if _, err := os.Create(configPath); err != nil {
beaver.Fatalf("could not create Sip config: %v", err)
return err
}
}
var cfg config
if _, err := toml.DecodeFile(configPath, &cfg); err != nil {
beaver.Fatalf("could not decode Sip config: %v", err)
return err
}
Origin = cfg.Origin
Upstream = cfg.Upstream
Tokens = cfg.Tokens
return nil
}
func Save() error {
cfg.Origin = Origin
cfg.Upstream = Upstream
cfg.Tokens = Tokens
cfg := config{
Origin: Origin,
Upstream: Upstream,
Tokens: Tokens,
}
fi, err := os.Create(configPath)
if err != nil {
return err
}
defer fi.Close()
if err := toml.NewEncoder(fi).Encode(cfg); err != nil {
return err
}
return nil
return fi.Close()
}

35
docs.go Normal file

@ -0,0 +1,35 @@
// +build docs
package main
import (
"os"
"strings"
"gitea.com/jolheiser/sip/cmd"
)
func main() {
app := cmd.NewApp("docs")
fi, err := os.Create("docs.md")
if err != nil {
panic(err)
}
md, err := app.ToMarkdown()
if err != nil {
panic(err)
}
// Clean up the header
md = md[strings.Index(md, "#"):]
if _, err := fi.WriteString(md); err != nil {
panic(err)
}
if err := fi.Close(); err != nil {
panic(err)
}
}

117
docs.md Normal file

@ -0,0 +1,117 @@
# NAME
Sip - Command line tool to interact with Gitea
# SYNOPSIS
Sip
```
[--origin]=[value]
[--owner|-o]=[value]
[--repo|-r]=[value]
[--token|-t]=[value]
[--upstream]=[value]
[--url|-u]=[value]
```
**Usage**:
```
Sip [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...]
```
# GLOBAL OPTIONS
**--origin**="": The origin remote
**--owner, -o**="": The owner to target (default: jolheiser)
**--repo, -r**="": The repo to target (default: sip)
**--token, -t**="": The access token to use (by name)
**--upstream**="": The upstream remote
**--url, -u**="": The base URL to the Gitea instance (default: https://gitea.com)
# COMMANDS
## config, cfg
Modify Sip config
### origin
Specify default origin name
### upstream
Specify default upstream name
## tokens, token
Manage access tokens
### add, create
Add a new access token
### remove, delete
Remove access tokens
## repo
Commands for interacting with a Gitea repository
### create
Create a new repository
## issues, issue
Commands for interacting with issues
**--csv**="": Output results to a CSV file at `PATH`
### create, new
Create a new issue
## pulls, pull, pr
Commands for interacting with pull requests
**--csv**="": Output results to a CSV file at `PATH`
### create, new
Create a new pull request
### status
View the status of a pull request
### checkout
Checkout a pull request for testing
## releases, release
Commands for interacting with releases
**--csv**="": Output results to a CSV file at `PATH`
### create, new
Create a new release
### attach
Attach files to a release
## open, o
Open a repository or issue/pull request

121
flag/flag.go Normal file

@ -0,0 +1,121 @@
package flag
import (
"fmt"
"sync"
"gitea.com/jolheiser/sip/config"
"gitea.com/jolheiser/sip/git"
"github.com/urfave/cli/v2"
)
type Remote struct {
Name string
URL string
Owner string
Repo string
}
var (
// Set via config
Origin Remote
Upstream Remote
// Set via flags
URL string
Owner string
Repo string
Token string
// Set via Before
OwnerRepoCtxSet bool
Flags = []cli.Flag{
&cli.StringFlag{
Name: "origin",
Usage: "The origin remote",
Value: config.Origin,
Destination: &Origin.Name,
},
&cli.StringFlag{
Name: "upstream",
Usage: "The upstream remote",
Value: config.Upstream,
Destination: &Upstream.Name,
},
&cli.StringFlag{
Name: "url",
Aliases: []string{"u"},
Usage: "The base URL to the Gitea instance",
Value: getUpstream().URL,
Destination: &URL,
},
&cli.StringFlag{
Name: "owner",
Aliases: []string{"o"},
Usage: "The owner to target",
Value: getUpstream().Owner,
Destination: &Owner,
},
&cli.StringFlag{
Name: "repo",
Aliases: []string{"r"},
Usage: "The repo to target",
Value: getUpstream().Repo,
Destination: &Repo,
},
&cli.StringFlag{
Name: "token",
Aliases: []string{"t"},
Usage: "The access token to use (by name)",
Destination: &Token,
},
}
originOnce sync.Once
upstreamOnce sync.Once
)
func FullName() string {
return fmt.Sprintf("%s/%s", Owner, Repo)
}
func FullURL() string {
return fmt.Sprintf("%s/%s", URL, FullName())
}
func Before(ctx *cli.Context) error {
OwnerRepoCtxSet = ctx.IsSet("owner") || ctx.IsSet("repo")
return nil
}
func defRemote(remote, def string) string {
if remote == "" {
return def
}
return remote
}
func getUpstream() Remote {
upstreamOnce.Do(func() {
Upstream.URL, Upstream.Owner, Upstream.Repo = getOrigin().URL, getOrigin().Owner, getOrigin().Name
upstream, err := git.GetRepo(defRemote(config.Upstream, "upstream"))
if err == nil {
Upstream.URL, Upstream.Owner, Upstream.Repo = upstream[0], upstream[1], upstream[2]
}
})
return Upstream
}
func getOrigin() Remote {
originOnce.Do(func() {
Origin.URL, Origin.Owner, Origin.Name = "https://gitea.com", "jolheiser", "sip"
origin, err := git.GetRepo(defRemote(config.Origin, "origin"))
if err == nil {
Origin.URL, Origin.Owner, Origin.Repo = origin[0], origin[1], origin[2]
}
})
return Origin
}

2
go.sum

@ -1,5 +1,3 @@
code.gitea.io/sdk/gitea v0.12.2 h1:NQI8b/CT9AEQjsxbVIZ6gsPUXv38moT5y1ocN7n1YcQ=
code.gitea.io/sdk/gitea v0.12.2/go.mod h1:z3uwDV/b9Ls47NGukYM9XhnHtqPh/J+t40lsUrR6JDY=
code.gitea.io/sdk/gitea v0.13.0 h1:iHognp8ZMhMFLooUUNZFpm8IHaC9qoHJDvAE5vTm5aw=
code.gitea.io/sdk/gitea v0.13.0/go.mod h1:z3uwDV/b9Ls47NGukYM9XhnHtqPh/J+t40lsUrR6JDY=
github.com/AlecAivazis/survey/v2 v2.1.1 h1:LEMbHE0pLj75faaVEKClEX1TM4AJmmnOh9eimREzLWI=

24
main.go

@ -4,34 +4,22 @@ import (
"os"
"gitea.com/jolheiser/sip/cmd"
"gitea.com/jolheiser/sip/config"
"github.com/urfave/cli/v2"
"go.jolheiser.com/beaver"
)
var Version = "develop"
func main() {
app := cmd.NewApp(Version)
// config loads on init
app := cli.NewApp()
app.Name = "Sip"
app.Usage = "Command line tool to interact with Gitea"
app.Version = Version
app.Commands = []*cli.Command{
&cmd.Config,
&cmd.Tokens,
&cmd.Repo,
&cmd.Issues,
&cmd.Pulls,
&cmd.Release,
&cmd.Open,
if err := config.Init(); err != nil {
beaver.Fatal(err)
}
app.Flags = cmd.Flags
app.EnableBashCompletion = true
err := app.Run(os.Args)
if err != nil {
beaver.Error(err)
beaver.Fatal(err)
}
}

@ -3,7 +3,7 @@ package sdk
import (
"strings"
"gitea.com/jolheiser/sip/modules/qualify"
"gitea.com/jolheiser/sip/qualify"
"code.gitea.io/sdk/gitea"
)