Skip to content

Commit

Permalink
Added “git:config” commands
Browse files Browse the repository at this point in the history
  • Loading branch information
rykov committed Oct 7, 2021
1 parent a37a9d8 commit f3f7d66
Show file tree
Hide file tree
Showing 7 changed files with 337 additions and 15 deletions.
1 change: 1 addition & 0 deletions api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ func (c *Client) prepareJSONBody(req *request, data interface{}) error {
return err
}

req.ContentLength = int64(len(body))
req.Header.Set("Content-Type", "application/json")
req.Body = ioutil.NopCloser(bytes.NewReader(body))
return nil
Expand Down
43 changes: 43 additions & 0 deletions api/git_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package api

import (
"context"
"net/url"
)

// Packages returns the details of the package listing
func (c *Client) GitConfig(cc context.Context, repo string) ([]GitConfigPair, error) {
path := "/git/repos/{acct}/" + url.PathEscape(repo) + "/config-vars"
req := c.newRequest(cc, "GET", path, false)

resp := gitConfigJSON{}
if err := req.doJSON(&resp); err != nil {
return nil, err
}

out := make([]GitConfigPair, 0, len(resp.ConfigVars))
for k, v := range resp.ConfigVars {
out = append(out, GitConfigPair{k, v})
}

return out, nil
}

// Git Config request/response
type gitConfigJSON struct {
ConfigVars map[string]string `json:"config_vars"`
}

// Repo represents Git Config KV pair
type GitConfigPair struct {
Key string
Value string
}

// GitConfigSet updates Git Config with passed-in map of new variables
func (c *Client) GitConfigSet(cc context.Context, repo string, vars map[string]string) error {
path := "/git/repos/{acct}/" + url.PathEscape(repo) + "/config-vars"
req := c.newRequest(cc, "PATCH", path, false)
c.prepareJSONBody(req, &gitConfigJSON{vars})
return req.doJSON(nil)
}
1 change: 1 addition & 0 deletions cli/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ func NewCmdGitRoot() *cobra.Command {
Short: "Git repository commands",
}

gitCmd.AddCommand(NewCmdGitConfig())
gitCmd.AddCommand(NewCmdGitRebuild())
gitCmd.AddCommand(NewCmdGitRename())
gitCmd.AddCommand(NewCmdGitReset())
Expand Down
130 changes: 130 additions & 0 deletions cli/git_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package cli

import (
"github.com/gemfury/cli/api"
"github.com/gemfury/cli/internal/ctx"
"github.com/spf13/cobra"
"sort"
"strings"
"text/tabwriter"

"fmt"
)

// NewCmdGitConfig is the root for Git Config
func NewCmdGitConfig() *cobra.Command {
gitConfigCmd := &cobra.Command{
Use: "config",
Short: "Configure Git build",
RunE: func(cmd *cobra.Command, args []string) error {
return filteredGitConfig(cmd, args, false)
},
}

gitConfigCmd.AddCommand(NewCmdGitConfigSet())
gitConfigCmd.AddCommand(NewCmdGitConfigGet())

return gitConfigCmd
}

// NewCmdGitConfigGet updates one or more configuration keys
func NewCmdGitConfigGet() *cobra.Command {
gitConfigGetCmd := &cobra.Command{
Use: "get KEY",
Short: "Get Git build environment key",
RunE: func(cmd *cobra.Command, args []string) error {
return filteredGitConfig(cmd, args, true)
},
}

return gitConfigGetCmd
}

// Filtered/unfiltered retrieval of Git Config for commands above
func filteredGitConfig(cmd *cobra.Command, args []string, filter bool) error {
if filter && len(args) < 2 {
return fmt.Errorf("Please specify a repository and a key")
} else if !filter && len(args) != 1 {
return fmt.Errorf("Command requires only a repository")
}

cc := cmd.Context()
term := ctx.Terminal(cc)
c, err := newAPIClient(cc)
if err != nil {
return err
}

config, err := c.GitConfig(cc, args[0])
if err != nil {
return err
}

filteredConfig := config
if keys := args[1:]; filter && len(keys) > 0 {
filteredConfig = make([]api.GitConfigPair, 0, len(keys))
for _, c := range config {
for _, k := range keys {
if c.Key == k {
filteredConfig = append(filteredConfig, c)
break
}
}
}
}

if len(filteredConfig) > 0 {
sort.Slice(filteredConfig, func(i, j int) bool {
return filteredConfig[i].Key < filteredConfig[j].Key
})
}

term.Printf("\n*** GIT CONFIG ***\n\n")
w := tabwriter.NewWriter(term.IOOut(), 0, 0, 2, ' ', 0)

for _, c := range filteredConfig {
fmt.Fprintf(w, "%s:\t%s\n", c.Key, c.Value)
}

w.Flush()
return nil
}

// NewCmdGitConfigSet updates one or more configuration keys
func NewCmdGitConfigSet() *cobra.Command {
gitConfigSetCmd := &cobra.Command{
Use: "set KEY=VAL",
Short: "Set Git build environment key",
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) < 2 {
return fmt.Errorf("Please specify a repository and a KEY=VALUE")
}

cc := cmd.Context()
term := ctx.Terminal(cc)
c, err := newAPIClient(cc)
if err != nil {
return err
}

vars := map[string]string{}
for _, pairStr := range args[1:] {
pair := strings.SplitN(pairStr, "=", 2)
if len(pair) != 2 {
return fmt.Errorf("Argument has no value: %s", pairStr)
}
vars[pair[0]] = pair[1]
}

err = c.GitConfigSet(cc, args[0], vars)
if err != nil {
return err
}

term.Printf("Updated %s repository config\n", args[0])
return nil
},
}

return gitConfigSetCmd
}
140 changes: 140 additions & 0 deletions cli/git_config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package cli_test

import (
"github.com/gemfury/cli/cli"
"github.com/gemfury/cli/internal/ctx"
"github.com/gemfury/cli/internal/testutil"
"github.com/gemfury/cli/pkg/terminal"
"strings"
"testing"
)

const (
gitConfigResponse = `{ "config_vars": {
"KEY1": "VALUE1",
"KEY2": "VALUE2"
}}
`
)

// ==== GIT CONFIG ====

func TestGitConfigCommandSuccess(t *testing.T) {
auth := terminal.TestAuther("user", "abc123", nil)
term := terminal.NewForTest()

// Fire up test server
path := "/git/repos/me/repo-name/config-vars"
server := testutil.APIServer(t, "GET", path, gitConfigResponse, 200)
defer server.Close()

cc := cli.TestContext(term, auth)
flags := ctx.GlobalFlags(cc)
flags.Endpoint = server.URL

err := runCommandNoErr(cc, []string{"git", "config", "repo-name"})
if err != nil {
t.Fatal(err)
}

exp := "KEY1: VALUE1 KEY2: VALUE2"
if outStr := compactString(term.OutBytes()); !strings.HasSuffix(outStr, exp) {
t.Errorf("Expected output to include %q, got %q", exp, outStr)
}
}

func TestGitConfigCommandUnauthorized(t *testing.T) {
path := "/git/repos/me/repo-name/config-vars"
server := testutil.APIServer(t, "GET", path, "{}", 200)
testCommandLoginPreCheck(t, []string{"git", "config", "repo-name"}, server)
server.Close()
}

func TestGitConfigCommandForbidden(t *testing.T) {
path := "/git/repos/me/repo-name/config-vars"
server := testutil.APIServer(t, "GET", path, "", 403)
testCommandForbiddenResponse(t, []string{"git", "config", "repo-name"}, server)
server.Close()
}

// ==== GIT CONFIG GET ====

func TestGitConfigGetCommandSuccess(t *testing.T) {
auth := terminal.TestAuther("user", "abc123", nil)
term := terminal.NewForTest()

// Fire up test server
path := "/git/repos/me/repo-name/config-vars"
server := testutil.APIServer(t, "GET", path, gitConfigResponse, 200)
defer server.Close()

cc := cli.TestContext(term, auth)
flags := ctx.GlobalFlags(cc)
flags.Endpoint = server.URL

err := runCommandNoErr(cc, []string{"git", "config", "get", "repo-name", "KEY2"})
if err != nil {
t.Fatal(err)
}

exp := "KEY2: VALUE2"
if outStr := compactString(term.OutBytes()); !strings.HasSuffix(outStr, exp) {
t.Errorf("Expected output to include %q, got %q", exp, outStr)
} else if strings.Contains(outStr, "KEY1") {
t.Errorf("Expected output to be filtered, got %q", outStr)
}
}

func TestGitConfigGetCommandUnauthorized(t *testing.T) {
path := "/git/repos/me/repo-name/config-vars"
server := testutil.APIServer(t, "GET", path, "{}", 200)
testCommandLoginPreCheck(t, []string{"git", "config", "get", "repo-name", "KEY2"}, server)
server.Close()
}

func TestGitConfigGetCommandForbidden(t *testing.T) {
path := "/git/repos/me/repo-name/config-vars"
server := testutil.APIServer(t, "GET", path, "", 403)
testCommandForbiddenResponse(t, []string{"git", "config", "get", "repo-name", "KEY2"}, server)
server.Close()
}

// ==== GIT CONFIG SET ====

func TestGitConfigSetCommandSuccess(t *testing.T) {
auth := terminal.TestAuther("user", "abc123", nil)
term := terminal.NewForTest()

// Fire up test server
path := "/git/repos/me/repo-name/config-vars"
server := testutil.APIServer(t, "PATCH", path, gitConfigResponse, 200)
defer server.Close()

cc := cli.TestContext(term, auth)
flags := ctx.GlobalFlags(cc)
flags.Endpoint = server.URL

err := runCommandNoErr(cc, []string{"git", "config", "set", "repo-name", "KEY2=VALUE2"})
if err != nil {
t.Fatal(err)
}

exp := "Updated repo-name repository config"
if outStr := compactString(term.OutBytes()); outStr != exp {
t.Errorf("Expected output to include %q, got %q", exp, outStr)
}
}

func TestGitConfigSetCommandUnauthorized(t *testing.T) {
path := "/git/repos/me/repo-name/config-vars"
server := testutil.APIServer(t, "PATCH", path, "{}", 200)
testCommandLoginPreCheck(t, []string{"git", "config", "set", "repo-name", "KEY2=VALUE2"}, server)
server.Close()
}

func TestGitConfigSetCommandForbidden(t *testing.T) {
path := "/git/repos/me/repo-name/config-vars"
server := testutil.APIServer(t, "PATCH", path, "", 403)
testCommandForbiddenResponse(t, []string{"git", "config", "set", "repo-name", "KEY2=VALUE2"}, server)
server.Close()
}
10 changes: 5 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ require (
require (
github.com/VividCortex/ewma v1.2.0 // indirect
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect
github.com/fatih/color v1.12.0 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/juju/ansiterm v0.0.0-20210706145210-9283cdf370b5 // indirect
github.com/juju/ansiterm v0.0.0-20210929141451-8b71cc96ebdc // indirect
github.com/lunixbochs/vtclean v1.0.0 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-isatty v0.0.13 // indirect
github.com/mattn/go-colorable v0.1.11 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect
)
Loading

0 comments on commit f3f7d66

Please sign in to comment.