diff --git a/README.md b/README.md index 465fd53..fa69f45 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,31 @@ +- [Gong](#gong) + - [Build Status](#build-status) + - [Summary](#summary) + - [Usage](#usage) + - [Installation](#installation) + - [Currently supported clients](#currently-supported-clients) + - [Login](#login) + - [Start working on an issue](#start-working-on-an-issue) + - [`gong browse`](#gong-browse) + - [`gong comment`](#gong-comment) + - [Why a pipe?](#why-a-pipe) + - [githooks](#githooks) + - [`gong prepare-commit-message`](#gong-prepare-commit-message) + - [`commit-msg`](#commit-msg) + - [Install commit hooks on your repository](#install-commit-hooks-on-your-repository) + - [`gong create`](#gong-create) + - [Issues/Feedback](#issuesfeedback) + - [CHANGELOG](#changelog) + - [1.6.0](#160) + - [1.4.0](#140) + - [1.3.4](#134) + - [Upcoming features](#upcoming-features) + - [`gong slack`](#gong-slack) + - [`gong next/pick`](#gong-nextpick) + - [Development](#development) ### Build Status * Develop: ![Build Status](https://travis-ci.org/KensoDev/gong.svg?branch=develop) @@ -45,6 +70,20 @@ Once you input all of the details the client will attempt to login. If succeeded [![asciicast](https://asciinema.org/a/dcko3kv5xwobpf4rgj0e4ulyo.png)](https://asciinema.org/a/dcko3kv5xwobpf4rgj0e4ulyo) +You can also define `~/.gong.json`: + + { + "client":"jira", + "domain":"", + "password":"", + "project_prefix":"", + "transitions":"In Progress", + "username":"" + } + +NOTE: +For Passowrd get an API Token. +https://support.atlassian.com/atlassian-account/docs/manage-api-tokens-for-your-atlassian-account/ ### Start working on an issue `gong start {issue-id} --type feature` @@ -59,6 +98,10 @@ This will do a couple of things [![asciicast](https://asciinema.org/a/c5libsysjmb5f8f8gizkbldzv.png)](https://asciinema.org/a/c5libsysjmb5f8f8gizkbldzv) +NOTE: +You can define a default (branch) type with env var: + + echo "export GONG_DEFAULT_BRANCH_TYPE=story" >> ~/.zshrc ### `gong browse` While working on a branch that matches the gong regular expression (look @@ -80,7 +123,9 @@ With this approach, I find I write much better comments to tickets. You will do ![asciicast](https://asciinema.org/a/d0rcjavbv55lbq1xpsrqiyyu6.png)](https://asciinema.org/a/d0rcjavbv55lbq1xpsrqiyyu6) -### `gong prepare-commit-message` +### githooks + +#### `gong prepare-commit-message` This is **not** meant to be used directly, instead it is meant to be wrapped with simple wrapper git hooks. @@ -90,15 +135,24 @@ All you need to do is to copy them into your `.git/hooks` directory. This will add a link to the issue to every commit. Whether you do `git commit "commit message" or edit the commit message using the editor with `git commit` -### Install commit hooks on your repository +#### `commit-msg` + +This githook will produce duplicate Jira links if using `git commit --ammend` +You can add an alias to skip this hook and avoid duplication: + + alias git-commit-ammend="git commit --amend --no-edit --no-verify" + + -``` -curl https://raw.githubusercontent.com/KensoDev/gong/develop/git-hooks/prepare-commit-msg > .git/hooks/prepare-commit-msg -chmod +x .git/hooks/prepare-commit-msg +#### Install commit hooks on your repository -curl https://raw.githubusercontent.com/KensoDev/gong/develop/git-hooks/commit-msg > .git/hooks/commit-msg -chmod +x .git/hooks/commit-msg -``` + mkdir ~/.githooks + git config --global core.hooksPath ~/.githooks + curl https://raw.githubusercontent.com/KensoDev/gong/develop/git-hooks/prepare-commit-msg > ~/.githooks/prepare-commit-msg + chmod +x .git/hooks/prepare-commit-msg + + curl https://raw.githubusercontent.com/KensoDev/gong/develop/git-hooks/commit-msg > ~/.githooks/commit-msg + chmod +x .git/hooks/commit-msg ### `gong create` @@ -138,3 +192,18 @@ Send a message to a slack channel, tagging the issue you are working on Show you the next items on your backlog, be able to start one without opening the browser +## Development +Update version: + + go get + +Build: + + cd cmd/gong/ + ./build.sh + +Add gong to your PATH and restart SEHLL, e.g.: + + mv gong /usr/local/bin/gong + exec $SHELL + diff --git a/client.go b/client.go index 8645024..2260e9d 100644 --- a/client.go +++ b/client.go @@ -3,7 +3,6 @@ package gong import ( "encoding/json" "fmt" - "github.com/segmentio/go-prompt" "io/ioutil" "os/user" "path/filepath" @@ -94,9 +93,9 @@ func Login(client Client) (bool, error) { message := fmt.Sprintf("Please enter your %v %v", clientName, k) promptValue := "" if v { - promptValue = prompt.PasswordMasked(message) + promptValue = PromptPasswordMasked(message) } else { - promptValue = prompt.String(message) + promptValue = PromptString(message) } fields[k] = client.FormatField(k, promptValue) diff --git a/cmd/gong/.bumpversion.cfg b/cmd/gong/.bumpversion.cfg index 47b807a..261024e 100644 --- a/cmd/gong/.bumpversion.cfg +++ b/cmd/gong/.bumpversion.cfg @@ -1,9 +1,8 @@ [bumpversion] -current_version = 1.7.0 +current_version = 1.8.0 commit = False tag = False [bumpversion:file:main.go] search = {current_version} replace = {new_version} - diff --git a/cmd/gong/build.sh b/cmd/gong/build.sh index ebe6648..6234c6c 100755 --- a/cmd/gong/build.sh +++ b/cmd/gong/build.sh @@ -1,2 +1,2 @@ bumpversion $1 --allow-dirty -go build \ No newline at end of file +go build -o gong \ No newline at end of file diff --git a/cmd/gong/main.go b/cmd/gong/main.go index ec2e171..80997ce 100644 --- a/cmd/gong/main.go +++ b/cmd/gong/main.go @@ -13,7 +13,7 @@ import ( func main() { app := cli.NewApp() - app.Version = "1.7.0" + app.Version = "1.8.0" var branchType string @@ -51,7 +51,7 @@ func main() { Value: "feature", Usage: "Type of branch to create eg: feature/{ticket-id}-ticket-title", Destination: &branchType, - EnvVar: "GONG_DEFAULT_BRANCH_TYPE" + EnvVar: "GONG_DEFAULT_BRANCH_TYPE", }, }, Action: func(c *cli.Context) error { diff --git a/git-hooks/commit-msg b/git-hooks/commit-msg index b8ccf26..9b9645b 100755 --- a/git-hooks/commit-msg +++ b/git-hooks/commit-msg @@ -1,6 +1,18 @@ #!/bin/sh # # Automatically adds branch name and branch description to every commit message. -# - -echo "$1\n\n$(cat $1 | gong prepare-commit-message)" \ No newline at end of file +# To avoid duplictaes: git commit --ammend --no-verify +# Source: https://medium.com/@nicklee1/prepending-your-git-commit-messages-with-user-story-ids-3bfea00eab5a +if [ -z "$BRANCHES_TO_SKIP" ]; then + BRANCHES_TO_SKIP=(master develop staging test) +fi +# Get the current branch name and check if it is excluded +BRANCH_NAME=$(git symbolic-ref --short HEAD) +BRANCH_EXCLUDED=$(printf "%s\n" "${BRANCHES_TO_SKIP[@]}" | grep -c "^$BRANCH_NAME$") +# Trim it down to get the parts we're interested in +# TRIMMED=$(echo $BRANCH_NAME | sed -e 's:^\([^-]*-[^-]*\)-.*:\1:' -e \ +# 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/') +# If it isn't excluded, preprend the trimmed branch identifier to the given message +if [ -n "$BRANCH_NAME" ] && ! [[ $BRANCH_EXCLUDED -eq 1 ]]; then + echo "$(cat $1)\n\n$(cat $1 | gong prepare-commit-message)" > $1 +fi \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..5a2d659 --- /dev/null +++ b/go.mod @@ -0,0 +1,12 @@ +module github.com/KensoDev/gong + +go 1.16 + +require ( + github.com/andygrunwald/go-jira v1.13.0 + github.com/fatih/color v1.10.0 + github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c + github.com/urfave/cli v1.22.5 + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c + gopkg.in/salsita/go-pivotaltracker.v1 v1.5.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..3f1f6e4 --- /dev/null +++ b/go.sum @@ -0,0 +1,54 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/andygrunwald/go-jira v1.13.0 h1:vvIImGgX32bHfoiyUwkNo+/YrPnRczNarvhLOncP6dE= +github.com/andygrunwald/go-jira v1.13.0/go.mod h1:jYi4kFDbRPZTJdJOVJO4mpMMIwdB+rcZwSO58DzPd2I= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= +github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/fatih/structs v1.0.0 h1:BrX964Rv5uQ3wwS+KRUAJCBBw5PQmgJfJ6v4yly5QwU= +github.com/fatih/structs v1.0.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 h1:zLTLjkaOFEFIOxY5BWLFLwh+cL8vOBW4XJ2aqLE/Tf0= +github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c h1:aY2hhxLhjEAbfXOx2nRJxCXezC6CO2V/yN+OCr1srtk= +github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/trivago/tgo v1.0.1 h1:bxatjJIXNIpV18bucU4Uk/LaoxvxuOlp/oowRHyncLQ= +github.com/trivago/tgo v1.0.1/go.mod h1:w4dpD+3tzNIIiIfkWWa85w5/B77tlvdZckQ+6PkFnhc= +github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU= +github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 h1:p/H982KKEjUnLJkM3tt/LemDnOc1GiZL5FCVlORJ5zo= +golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/salsita/go-pivotaltracker.v1 v1.5.0 h1:kryrdIZtOFISZ8LKUCyl3ihfigfdqOSXkTwWtVMz0nc= +gopkg.in/salsita/go-pivotaltracker.v1 v1.5.0/go.mod h1:GWZI+O3bfQsZJz6JhjIMo6PjRxoxurQy+qGXaLO/I+w= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/prompt.go b/prompt.go new file mode 100644 index 0000000..c2c21cc --- /dev/null +++ b/prompt.go @@ -0,0 +1,98 @@ +// source: https://github.com/segmentio/go-prompt +package gong + +import ( + "fmt" + "strconv" + "strings" + + "github.com/howeyc/gopass" +) + +// String prompt. +func PromptString(prompt string, args ...interface{}) string { + var s string + fmt.Printf(prompt+": ", args...) + fmt.Scanln(&s) + return s +} + +// String prompt (required). +func PromptStringRequired(prompt string, args ...interface{}) (s string) { + for strings.Trim(s, " ") == "" { + s = PromptString(prompt, args...) + } + return s +} + +// Confirm continues prompting until the input is boolean-ish. +func PromptConfirm(prompt string, args ...interface{}) bool { + for { + switch PromptString(prompt, args...) { + case "Yes", "yes", "y", "Y": + return true + case "No", "no", "n", "N": + return false + } + } +} + +// Choose prompts for a single selection from `list`, returning in the index. +func PromptChoose(prompt string, list []string) int { + fmt.Println() + for i, val := range list { + fmt.Printf(" %d) %s\n", i+1, val) + } + + fmt.Println() + i := -1 + + for { + s := PromptString(prompt) + + // index + n, err := strconv.Atoi(s) + if err == nil { + if n > 0 && n <= len(list) { + i = n - 1 + break + } else { + continue + } + } + + // value + i = indexOf(s, list) + if i != -1 { + break + } + } + + return i +} + +// Password prompt. +func PromptPassword(prompt string, args ...interface{}) string { + fmt.Printf(prompt+": ", args...) + password, _ := gopass.GetPasswd() + s := string(password[0:]) + return s +} + +// Password prompt with mask. +func PromptPasswordMasked(prompt string, args ...interface{}) string { + fmt.Printf(prompt+": ", args...) + password, _ := gopass.GetPasswdMasked() + s := string(password[0:]) + return s +} + +// index of `s` in `list`. +func PromptIndexOf(s string, list []string) int { + for i, val := range list { + if val == s { + return i + } + } + return -1 +}