mirror of
https://github.com/Threnklyn/jira.git
synced 2026-05-19 20:53:27 +02:00
Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| adc08935b4 | |||
| 073c8a3694 | |||
| c4a31a498e | |||
| bcad37089a | |||
| b2ba8de15d | |||
| 6016bda571 | |||
| 34ca09cf1a | |||
| d7fb88ee41 | |||
| de4fe76fec | |||
| 5b870cb7a2 | |||
| 89bb82b3f2 | |||
| dd0f5efd32 | |||
| 68b5e60dd9 | |||
| 71acc5d7fc | |||
| 4f91cecf25 | |||
| 688b987895 | |||
| 71bb04fabb | |||
| 3a9f763f9d | |||
| d86d85f7b2 | |||
| 4b798cbfb4 | |||
| 598924b51d | |||
| 674957af5d | |||
| c568d7e921 | |||
| 6eb3567ca5 | |||
| 87ec73c5c3 | |||
| 23551abb11 | |||
| 693e1441f7 | |||
| 6e5cc9821e | |||
| 9e90376816 | |||
| 20b32c2ed6 | |||
| ac170e9ab1 | |||
| d8bce08d3a | |||
| 382bf4faeb | |||
| 595a5212b4 | |||
| f595801202 | |||
| 404caf6400 | |||
| f7eb04e36d | |||
| b0d4f7273d | |||
| a927181db1 | |||
| ff56136937 | |||
| e58625b00c |
@@ -5,3 +5,5 @@ src/github.com/docopt/
|
||||
src/github.com/mgutz/
|
||||
src/github.com/op/
|
||||
src/gopkg.in/
|
||||
jira
|
||||
jira.exe
|
||||
@@ -1,5 +1,30 @@
|
||||
# Changelog
|
||||
|
||||
## 0.1.3 - 2016-07-30
|
||||
|
||||
* [[#43](https://github.com/Netflix-Skunkworks/go-jira/issues/43)] add support for jira done|todo|prog commands [Cory Bennett] [[dd7d1cc](https://github.com/Netflix-Skunkworks/go-jira/commit/dd7d1cc)]
|
||||
* Reporter is not generally editable. [Mike Pountney] [[a637b43](https://github.com/Netflix-Skunkworks/go-jira/commit/a637b43)]
|
||||
|
||||
## 0.1.2 - 2016-06-29
|
||||
|
||||
* [[#44](https://github.com/Netflix-Skunkworks/go-jira/issues/44)] Close tmpfile before rename to work around "The process cannot access the file because it is being used by another process" error on windows. [Cory Bennett] [[0980f8e](https://github.com/Netflix-Skunkworks/go-jira/commit/0980f8e)]
|
||||
|
||||
## 0.1.1 - 2016-06-28
|
||||
|
||||
* use USERPROFILE instead of HOME for windows, rework paths to use filepath.Join for better cross platform support [Cory Bennett] [[adcedc4](https://github.com/Netflix-Skunkworks/go-jira/commit/adcedc4)]
|
||||
* Include templates from a system path [Mike Pountney] [[cf10f53](https://github.com/Netflix-Skunkworks/go-jira/commit/cf10f53)]
|
||||
* Added support for the ```expand``` option for Issues [tobyjoe] [[fb4afc9](https://github.com/Netflix-Skunkworks/go-jira/commit/fb4afc9)]
|
||||
* change for api changes to go-logging [Cory Bennett] [[7bfc6e8](https://github.com/Netflix-Skunkworks/go-jira/commit/7bfc6e8)]
|
||||
* Fix issuetype calls adding URL escaping [Jonathan Wright] [[e4a25e2](https://github.com/Netflix-Skunkworks/go-jira/commit/e4a25e2)]
|
||||
|
||||
## 0.1.0 - 2016-01-29
|
||||
|
||||
* Fixes [#32](https://github.com/Netflix-Skunkworks/go-jira/issues/32) - make path to cookieFile if it's not present [Mike Pountney] [[6644579](https://github.com/Netflix-Skunkworks/go-jira/commit/6644579)]
|
||||
* Add component/components support: add and list for now. [Mike Pountney] [[d7b3226](https://github.com/Netflix-Skunkworks/go-jira/commit/d7b3226)]
|
||||
* Tweak the CmdWatch contract and add watcher remove support [Mike Pountney] [[383847a](https://github.com/Netflix-Skunkworks/go-jira/commit/383847a)]
|
||||
* Amend vote/unvote to be vote/vote --down [Mike Pountney] [[797edef](https://github.com/Netflix-Skunkworks/go-jira/commit/797edef)]
|
||||
* Add 'vote' and 'unvote' [Mike Pountney] [[c95e66e](https://github.com/Netflix-Skunkworks/go-jira/commit/c95e66e)]
|
||||
|
||||
## 0.0.20 - 2016-01-21
|
||||
|
||||
* [issue [#28](https://github.com/Netflix-Skunkworks/go-jira/issues/28)] check to make sure we got back issuetypes for create metadata [Cory Bennett] [[ee0e780](https://github.com/Netflix-Skunkworks/go-jira/commit/ee0e780)]
|
||||
|
||||
@@ -1,29 +1,41 @@
|
||||
PLATFORMS= \
|
||||
freebsd-386 \
|
||||
freebsd-amd64 \
|
||||
freebsd-arm \
|
||||
linux-386 \
|
||||
linux-amd64 \
|
||||
linux-arm \
|
||||
openbsd-386 \
|
||||
openbsd-amd64 \
|
||||
windows-386 \
|
||||
windows-amd64 \
|
||||
darwin-386 \
|
||||
darwin-amd64 \
|
||||
$(NULL)
|
||||
|
||||
DIST=$(shell pwd)/dist
|
||||
# freebsd-386 \
|
||||
# freebsd-arm \
|
||||
# linux-arm \
|
||||
# openbsd-386 \
|
||||
# openbsd-amd64 \
|
||||
# darwin-386
|
||||
|
||||
export GOPATH=$(shell pwd)
|
||||
|
||||
GOBIN ?= $(shell pwd)
|
||||
NAME=jira
|
||||
|
||||
BIN ?= $(GOBIN)/$(NAME)
|
||||
OS=$(shell uname -s)
|
||||
ifeq ($(filter CYGWIN%,$(OS)),$(OS))
|
||||
export CWD=$(shell cygpath -wa .)
|
||||
export SEP=\\
|
||||
export CYGWIN=winsymlinks:native
|
||||
BIN ?= $(GOBIN)$(SEP)$(NAME).exe
|
||||
else
|
||||
export CWD=$(shell pwd)
|
||||
export SEP=/
|
||||
BIN ?= $(GOBIN)$(SEP)$(NAME)
|
||||
endif
|
||||
|
||||
CURVER ?= $(shell [ -d .git ] && git describe --abbrev=0 --tags || grep ^\#\# CHANGELOG.md | awk '{print $$2; exit}')
|
||||
LDFLAGS:=-X jira.VERSION=$(patsubst v%,%,$(CURVER)) -w
|
||||
export GOPATH=$(CWD)
|
||||
|
||||
DIST=$(CWD)$(SEP)dist
|
||||
|
||||
GOBIN ?= $(CWD)
|
||||
|
||||
CURVER ?= $(patsubst v%,%,$(shell [ -d .git ] && git describe --abbrev=0 --tags || grep ^\#\# CHANGELOG.md | awk '{print $$2; exit}'))
|
||||
LDFLAGS:=-X jira.VERSION=$(CURVER) -w
|
||||
|
||||
# use make DEBUG=1 and you can get a debuggable golang binary
|
||||
# see https://github.com/mailgun/godebug
|
||||
@@ -34,16 +46,16 @@ else
|
||||
endif
|
||||
|
||||
build: src/github.com/Netflix-Skunkworks/go-jira
|
||||
$(GOBUILD) -o $(BIN) main/main.go
|
||||
$(GOBUILD) -o '$(BIN)' main/main.go
|
||||
|
||||
src/%:
|
||||
mkdir -p $(@D)
|
||||
test -L $@ || ln -sf ../../.. $@
|
||||
test -L $@ || ln -sf '$(GOPATH)' $@
|
||||
go get -v $* $*/main
|
||||
|
||||
cross-setup:
|
||||
for p in $(PLATFORMS); do \
|
||||
echo "Building for $$p"; \
|
||||
echo Building for $$p"; \
|
||||
cd $(GOROOT)/src && sudo GOROOT_BOOTSTRAP=$(GOROOT) GOOS=$${p/-*/} GOARCH=$${p/*-/} bash ./make.bash --no-clean; \
|
||||
done
|
||||
|
||||
@@ -54,6 +66,7 @@ all:
|
||||
echo "Building for $$p"; \
|
||||
${MAKE} build GOOS=$${p/-*/} GOARCH=$${p/*-/} BIN=$(DIST)/$(NAME)-$$p; \
|
||||
done
|
||||
for x in $(DIST)/jira-windows-*; do mv $$x $$x.exe; done
|
||||
|
||||
fmt:
|
||||
gofmt -s -w main/*.go *.go
|
||||
@@ -65,7 +78,7 @@ NEWVER ?= $(shell echo $(CURVER) | awk -F. '{print $$1"."$$2"."$$3+1}')
|
||||
TODAY := $(shell date +%Y-%m-%d)
|
||||
|
||||
changes:
|
||||
@git log --pretty=format:"* %s [%cn] [%h]" --no-merges ^$(CURVER) HEAD main/*.go *.go | grep -vE 'gofmt|go fmt'
|
||||
@git log --pretty=format:"* %s [%cn] [%h]" --no-merges ^v$(CURVER) HEAD main/*.go *.go | grep -vE 'gofmt|go fmt'
|
||||
|
||||
update-changelog:
|
||||
@echo "# Changelog" > CHANGELOG.md.new; \
|
||||
@@ -81,7 +94,7 @@ update-changelog:
|
||||
git tag v$(NEWVER)
|
||||
|
||||
version:
|
||||
@echo $(patsubst v%,%,$(CURVER))
|
||||
@echo $(CURVER)
|
||||
|
||||
clean:
|
||||
rm -rf pkg dist bin src ./$(NAME)
|
||||
|
||||
@@ -69,25 +69,13 @@ You can download one of the pre-built binaries for **go-jira** [here](https://gi
|
||||
|
||||
* **NOTE** You will need **`go-1.4.1`** minimum
|
||||
|
||||
* If you do not have a **GOPATH** setup, these are simple build steps:
|
||||
|
||||
* To build the `jira` binary the current directory just run:
|
||||
```bash
|
||||
git clone git@github.com:Netflix-Skunkworks/go-jira.git
|
||||
cd go-jira
|
||||
export GOPATH=$(pwd)
|
||||
export GOBIN=$GOPATH/bin
|
||||
export PATH=$GOBIN:$PATH
|
||||
cd src/github.com/Netflix-Skunkworks/go-jira/jira
|
||||
go get -v
|
||||
make
|
||||
```
|
||||
|
||||
* If you do have a **GOPATH** setup, these are the standard steps to build:
|
||||
|
||||
```
|
||||
cd $GOPATH
|
||||
git clone git@github.com:Netflix-Skunkworks/go-jira.git src/github.com/Netflix-Skunkworks/go-jira
|
||||
cd src/github.com/Netflix-Skunkworks/go-jira/jira
|
||||
go get -v
|
||||
* To install the binary to you ~/bin directory you can run:
|
||||
```bash
|
||||
make install
|
||||
```
|
||||
|
||||
## Configuration
|
||||
@@ -117,6 +105,8 @@ endpoint: https://jira.mycompany.com
|
||||
EOM
|
||||
```
|
||||
|
||||
Then use `jira login` to authenticate yourself.
|
||||
|
||||
### Dynamic Configuration
|
||||
|
||||
If the **.jira.d/config.yml** file is executable, then **go-jira** will attempt to execute the file and use the stdout for configuration. You can use this to customize templates or other overrides depending on what type of operation you are running. For example if you would like to use the "table" template when ever you run `jira ls`, then you can create a template like this:
|
||||
@@ -167,6 +157,26 @@ When running a command like `jira edit` it will look through the current directo
|
||||
if found it will use that file as the template, otherwise it will use the default **edit** template hard-coded into **go-jira**. You can export the default
|
||||
hard-coded templates with `jira export-templates` which will write them to **~/.jira.d/templates/**.
|
||||
|
||||
#### Writing/Editing Templates
|
||||
|
||||
First the basic templating functionality is defined by the Go language 'text/template' library. The library reference documentation can be found [here](https://golang.org/pkg/text/template/), and there is a good primer document [here](https://gohugo.io/templates/go-templates/). `go-jira` also provides a few extra helper functions to make it a bit easlier to format the data, those functions are defined [here](https://github.com/Netflix-Skunkworks/go-jira/blob/master/util.go#L133).
|
||||
|
||||
Knowing what data and fields are available to any given template is not obvious. The easiest approach to determine what is available is to use the `debug` template on any given operation. For eample to find out what is available to the "view" templates, you can use:
|
||||
```
|
||||
jira view GOJIRA-321 -t debug
|
||||
```
|
||||
|
||||
This will print out the data in JSON format that is available to the template. You can do this for any other operation, like "list":
|
||||
```
|
||||
jira list -t debug
|
||||
```
|
||||
|
||||
Figuring out what is available to input templates (like for the `create` operation) is a bit more tricky, but similar. To find the data available for a `create` template you can run:
|
||||
```
|
||||
jira create --dryrun -t debug --editor /bin/cat
|
||||
```
|
||||
This will attempt to fetch metadata for your default project (you can provide any options that you would normally specify for the `create` operation). It uses the `--dryrun` option to prevent any actual updates being sent to Jira. The `-t debug` is like before to cause the input to be serialized to JSON and printed for your inspection. Finally the `--editor /bin/cat` will cause `go-jira` to just print the template rather than open up an editor and wait for you to edit/save it.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
@@ -205,7 +215,7 @@ General Options:
|
||||
-e --endpoint=URI URI to use for jira
|
||||
-h --help Show this usage
|
||||
-t --template=FILE Template file to use for output/editing
|
||||
-u --user=USER Username to use for authenticaion (default: $USER)
|
||||
-u --user=USER Username to use for authentication (default: $USER)
|
||||
-v --verbose Increase output logging
|
||||
|
||||
Query Options:
|
||||
|
||||
@@ -6,14 +6,16 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/kballard/go-shellquote"
|
||||
"github.com/op/go-logging"
|
||||
"gopkg.in/coryb/yaml.v2"
|
||||
"gopkg.in/op/go-logging.v1"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -32,7 +34,7 @@ type Cli struct {
|
||||
}
|
||||
|
||||
func New(opts map[string]interface{}) *Cli {
|
||||
homedir := os.Getenv("HOME")
|
||||
homedir := homedir()
|
||||
cookieJar, _ := cookiejar.New(nil)
|
||||
endpoint, _ := opts["endpoint"].(string)
|
||||
url, _ := url.Parse(strings.TrimRight(endpoint, "/"))
|
||||
@@ -52,7 +54,7 @@ func New(opts map[string]interface{}) *Cli {
|
||||
cli := &Cli{
|
||||
endpoint: url,
|
||||
opts: opts,
|
||||
cookieFile: fmt.Sprintf("%s/.jira.d/cookies.js", homedir),
|
||||
cookieFile: filepath.Join(homedir, ".jira.d", "cookies.js"),
|
||||
ua: &http.Client{
|
||||
Jar: cookieJar,
|
||||
Transport: transport,
|
||||
@@ -87,6 +89,7 @@ func (c *Cli) saveCookies(cookies []*http.Cookie) {
|
||||
}
|
||||
jsonWrite(c.cookieFile, mergedCookies)
|
||||
} else {
|
||||
mkdir(path.Dir(c.cookieFile))
|
||||
jsonWrite(c.cookieFile, cookies)
|
||||
}
|
||||
}
|
||||
@@ -98,15 +101,15 @@ func (c *Cli) loadCookies() []*http.Cookie {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
log.Error("Failed to open %s: %s", c.cookieFile, err)
|
||||
log.Errorf("Failed to open %s: %s", c.cookieFile, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
cookies := make([]*http.Cookie, 0)
|
||||
err = json.Unmarshal(bytes, &cookies)
|
||||
if err != nil {
|
||||
log.Error("Failed to parse json from file %s: %s", c.cookieFile, err)
|
||||
log.Errorf("Failed to parse json from file %s: %s", c.cookieFile, err)
|
||||
}
|
||||
log.Debug("Loading Cookies: %s", cookies)
|
||||
log.Debugf("Loading Cookies: %s", cookies)
|
||||
return cookies
|
||||
}
|
||||
|
||||
@@ -118,15 +121,33 @@ func (c *Cli) put(uri string, content string) (*http.Response, error) {
|
||||
return c.makeRequestWithContent("PUT", uri, content)
|
||||
}
|
||||
|
||||
func (c *Cli) delete(uri string) (*http.Response, error) {
|
||||
method := "DELETE"
|
||||
req, _ := http.NewRequest(method, uri, nil)
|
||||
log.Infof("%s %s", req.Method, req.URL.String())
|
||||
if resp, err := c.makeRequest(req); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
if resp.StatusCode == 401 {
|
||||
if err := c.CmdLogin(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req, _ = http.NewRequest(method, uri, nil)
|
||||
return c.makeRequest(req)
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cli) makeRequestWithContent(method string, uri string, content string) (*http.Response, error) {
|
||||
buffer := bytes.NewBufferString(content)
|
||||
req, _ := http.NewRequest(method, uri, buffer)
|
||||
|
||||
log.Info("%s %s", req.Method, req.URL.String())
|
||||
log.Infof("%s %s", req.Method, req.URL.String())
|
||||
if log.IsEnabledFor(logging.DEBUG) {
|
||||
logBuffer := bytes.NewBuffer(make([]byte, 0, len(content)))
|
||||
req.Write(logBuffer)
|
||||
log.Debug("%s", logBuffer)
|
||||
log.Debugf("%s", logBuffer)
|
||||
// need to recreate the buffer since the offset is now at the end
|
||||
// need to be able to rewind the buffer offset, dont know how yet
|
||||
req, _ = http.NewRequest(method, uri, bytes.NewBufferString(content))
|
||||
@@ -148,11 +169,11 @@ func (c *Cli) makeRequestWithContent(method string, uri string, content string)
|
||||
|
||||
func (c *Cli) get(uri string) (*http.Response, error) {
|
||||
req, _ := http.NewRequest("GET", uri, nil)
|
||||
log.Info("%s %s", req.Method, req.URL.String())
|
||||
log.Infof("%s %s", req.Method, req.URL.String())
|
||||
if log.IsEnabledFor(logging.DEBUG) {
|
||||
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
||||
req.Write(logBuffer)
|
||||
log.Debug("%s", logBuffer)
|
||||
log.Debugf("%s", logBuffer)
|
||||
}
|
||||
|
||||
if resp, err := c.makeRequest(req); err != nil {
|
||||
@@ -171,11 +192,11 @@ func (c *Cli) get(uri string) (*http.Response, error) {
|
||||
func (c *Cli) makeRequest(req *http.Request) (resp *http.Response, err error) {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
if resp, err = c.ua.Do(req); err != nil {
|
||||
log.Error("Failed to %s %s: %s", req.Method, req.URL.String(), err)
|
||||
log.Errorf("Failed to %s %s: %s", req.Method, req.URL.String(), err)
|
||||
return nil, err
|
||||
} else {
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 && resp.StatusCode != 401 {
|
||||
log.Error("response status: %s", resp.Status)
|
||||
log.Errorf("response status: %s", resp.Status)
|
||||
}
|
||||
|
||||
runtime.SetFinalizer(resp, func(r *http.Response) {
|
||||
@@ -193,33 +214,33 @@ func (c *Cli) GetTemplate(name string) string {
|
||||
return c.getTemplate(name)
|
||||
}
|
||||
|
||||
func getLookedUpTemplate(name string, dflt string) string {
|
||||
if file, err := FindClosestParentPath(filepath.Join(".jira.d", "templates", name)); err == nil {
|
||||
return readFile(file)
|
||||
}
|
||||
if _, err := os.Stat(fmt.Sprintf("/etc/go-jira/templates/%s", name)); err == nil {
|
||||
file := fmt.Sprintf("/etc/go-jira/templates/%s", name)
|
||||
return readFile(file)
|
||||
}
|
||||
return dflt
|
||||
}
|
||||
|
||||
func (c *Cli) getTemplate(name string) string {
|
||||
if override, ok := c.opts["template"].(string); ok {
|
||||
if _, err := os.Stat(override); err == nil {
|
||||
return readFile(override)
|
||||
} else {
|
||||
if file, err := FindClosestParentPath(fmt.Sprintf(".jira.d/templates/%s", override)); err == nil {
|
||||
return readFile(file)
|
||||
}
|
||||
if dflt, ok := all_templates[override]; ok {
|
||||
return dflt
|
||||
if t := getLookedUpTemplate(override, all_templates[override]); t != "" {
|
||||
return t
|
||||
}
|
||||
}
|
||||
}
|
||||
if file, err := FindClosestParentPath(fmt.Sprintf(".jira.d/templates/%s", name)); err != nil {
|
||||
// create-bug etc are special, if we dont find it in the path
|
||||
// then just return a generic create template
|
||||
if strings.HasPrefix(name, "create-") {
|
||||
if file, err := FindClosestParentPath(".jira.d/templates/create"); err != nil {
|
||||
return all_templates["create"]
|
||||
} else {
|
||||
return readFile(file)
|
||||
}
|
||||
}
|
||||
return all_templates[name]
|
||||
} else {
|
||||
return readFile(file)
|
||||
// create-bug etc are special, if we dont find it in the path
|
||||
// then just return the create template
|
||||
if strings.HasPrefix(name, "create-") {
|
||||
return getLookedUpTemplate(name, c.getTemplate("create"))
|
||||
}
|
||||
return getLookedUpTemplate(name, all_templates[name])
|
||||
}
|
||||
|
||||
type NoChangesFound struct{}
|
||||
@@ -230,23 +251,35 @@ func (f NoChangesFound) Error() string {
|
||||
|
||||
func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData map[string]interface{}, templateProcessor func(string) error) error {
|
||||
|
||||
tmpdir := fmt.Sprintf("%s/.jira.d/tmp", os.Getenv("HOME"))
|
||||
tmpdir := filepath.Join(homedir(), ".jira.d", "tmp")
|
||||
if err := mkdir(tmpdir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fh, err := ioutil.TempFile(tmpdir, tmpFilePrefix)
|
||||
if err != nil {
|
||||
log.Error("Failed to make temp file in %s: %s", tmpdir, err)
|
||||
log.Errorf("Failed to make temp file in %s: %s", tmpdir, err)
|
||||
return err
|
||||
}
|
||||
defer fh.Close()
|
||||
|
||||
tmpFileName := fmt.Sprintf("%s.yml", fh.Name())
|
||||
if err := os.Rename(fh.Name(), tmpFileName); err != nil {
|
||||
log.Error("Failed to rename %s to %s: %s", fh.Name(), fmt.Sprintf("%s.yml", fh.Name()), err)
|
||||
oldFileName := fh.Name()
|
||||
tmpFileName := fmt.Sprintf("%s.yml", oldFileName)
|
||||
|
||||
// close tmpfile so we can rename on windows
|
||||
fh.Close()
|
||||
|
||||
if err := os.Rename(oldFileName, tmpFileName); err != nil {
|
||||
log.Errorf("Failed to rename %s to %s: %s", oldFileName, tmpFileName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
fh, err = os.OpenFile(tmpFileName, os.O_RDWR|os.O_EXCL, 0600)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to reopen temp file file in %s: %s", tmpFileName, err)
|
||||
return err
|
||||
}
|
||||
|
||||
defer fh.Close()
|
||||
defer func() {
|
||||
os.Remove(tmpFileName)
|
||||
}()
|
||||
@@ -281,11 +314,11 @@ func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData m
|
||||
if editing {
|
||||
shell, _ := shellquote.Split(editor)
|
||||
shell = append(shell, tmpFileName)
|
||||
log.Debug("Running: %#v", shell)
|
||||
log.Debugf("Running: %#v", shell)
|
||||
cmd := exec.Command(shell[0], shell[1:]...)
|
||||
cmd.Stdout, cmd.Stderr, cmd.Stdin = os.Stdout, os.Stderr, os.Stdin
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Error("Failed to edit template with %s: %s", editor, err)
|
||||
log.Errorf("Failed to edit template with %s: %s", editor, err)
|
||||
if promptYN("edit again?", true) {
|
||||
continue
|
||||
}
|
||||
@@ -301,14 +334,14 @@ func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData m
|
||||
|
||||
edited := make(map[string]interface{})
|
||||
if fh, err := ioutil.ReadFile(tmpFileName); err != nil {
|
||||
log.Error("Failed to read tmpfile %s: %s", tmpFileName, err)
|
||||
log.Errorf("Failed to read tmpfile %s: %s", tmpFileName, err)
|
||||
if editing && promptYN("edit again?", true) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
} else {
|
||||
if err := yaml.Unmarshal(fh, &edited); err != nil {
|
||||
log.Error("Failed to parse YAML: %s", err)
|
||||
log.Errorf("Failed to parse YAML: %s", err)
|
||||
if editing && promptYN("edit again?", true) {
|
||||
continue
|
||||
}
|
||||
@@ -326,7 +359,7 @@ func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData m
|
||||
// you can add the "abort: true" flag to the document
|
||||
// and we will abort now
|
||||
if val, ok := edited["abort"].(bool); ok && val {
|
||||
log.Info("abort flag found in template, quiting")
|
||||
log.Infof("abort flag found in template, quiting")
|
||||
return fmt.Errorf("abort flag found in template, quiting")
|
||||
}
|
||||
|
||||
@@ -336,7 +369,7 @@ func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData m
|
||||
for k := range f {
|
||||
if _, ok := mf.(map[string]interface{})[k]; !ok {
|
||||
err := fmt.Errorf("Field %s is not editable", k)
|
||||
log.Error("%s", err)
|
||||
log.Errorf("%s", err)
|
||||
if editing && promptYN("edit again?", true) {
|
||||
continue
|
||||
}
|
||||
@@ -352,7 +385,7 @@ func (c *Cli) editTemplate(template string, tmpFilePrefix string, templateData m
|
||||
}
|
||||
|
||||
if err := templateProcessor(json); err != nil {
|
||||
log.Error("%s", err)
|
||||
log.Errorf("%s", err)
|
||||
if editing && promptYN("edit again?", true) {
|
||||
continue
|
||||
}
|
||||
@@ -382,6 +415,10 @@ func (c *Cli) SaveData(data interface{}) error {
|
||||
|
||||
func (c *Cli) ViewIssue(issue string) (interface{}, error) {
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue)
|
||||
if x := c.expansions(); len(x) > 0 {
|
||||
uri = fmt.Sprintf("%s?expand=%s", uri, strings.Join(x, ","))
|
||||
}
|
||||
|
||||
data, err := responseToJson(c.get(uri))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -398,7 +435,7 @@ func (c *Cli) FindIssues() (interface{}, error) {
|
||||
qbuff := bytes.NewBufferString("resolution = unresolved")
|
||||
if project, ok := c.opts["project"]; !ok {
|
||||
err := fmt.Errorf("Missing required arguments, either 'query' or 'project' are required")
|
||||
log.Error("%s", err)
|
||||
log.Errorf("%s", err)
|
||||
return nil, err
|
||||
} else {
|
||||
qbuff.WriteString(fmt.Sprintf(" AND project = '%s'", project))
|
||||
@@ -443,6 +480,7 @@ func (c *Cli) FindIssues() (interface{}, error) {
|
||||
"startAt": "0",
|
||||
"maxResults": c.opts["max_results"],
|
||||
"fields": fields,
|
||||
"expand": c.expansions(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -456,6 +494,10 @@ func (c *Cli) FindIssues() (interface{}, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cli) GetOptString(optName string, dflt string) string {
|
||||
return c.getOptString(optName, dflt)
|
||||
}
|
||||
|
||||
func (c *Cli) getOptString(optName string, dflt string) string {
|
||||
if val, ok := c.opts[optName].(string); ok {
|
||||
return val
|
||||
@@ -464,6 +506,10 @@ func (c *Cli) getOptString(optName string, dflt string) string {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cli) GetOptBool(optName string, dflt bool) bool {
|
||||
return c.getOptBool(optName, dflt)
|
||||
}
|
||||
|
||||
func (c *Cli) getOptBool(optName string, dflt bool) bool {
|
||||
if val, ok := c.opts[optName].(bool); ok {
|
||||
return val
|
||||
@@ -471,3 +517,12 @@ func (c *Cli) getOptBool(optName string, dflt bool) bool {
|
||||
return dflt
|
||||
}
|
||||
}
|
||||
|
||||
// expansions returns a comma-separated list of values for field expansion
|
||||
func (c *Cli) expansions() []string {
|
||||
expansions := make([]string, 0)
|
||||
if x, ok := c.opts["expand"].(string); ok {
|
||||
expansions = strings.Split(x, ",")
|
||||
}
|
||||
return expansions
|
||||
}
|
||||
|
||||
+181
-67
@@ -2,10 +2,12 @@ package jira
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
// "github.com/kr/pretty"
|
||||
@@ -22,22 +24,22 @@ func (c *Cli) CmdLogin() error {
|
||||
passwd := string(pwbytes)
|
||||
|
||||
req.SetBasicAuth(user, passwd)
|
||||
log.Info("%s %s", req.Method, req.URL.String())
|
||||
log.Infof("%s %s", req.Method, req.URL.String())
|
||||
if resp, err := c.makeRequest(req); err != nil {
|
||||
return err
|
||||
} else {
|
||||
out, _ := httputil.DumpResponse(resp, true)
|
||||
log.Debug("%s", out)
|
||||
log.Debugf("%s", out)
|
||||
if resp.StatusCode == 403 {
|
||||
// probably got this, need to redirect the user to login manually
|
||||
// X-Authentication-Denied-Reason: CAPTCHA_CHALLENGE; login-url=https://jira/login.jsp
|
||||
if reason := resp.Header.Get("X-Authentication-Denied-Reason"); reason != "" {
|
||||
err := fmt.Errorf("Authenticaion Failed: %s", reason)
|
||||
log.Error("%s", err)
|
||||
log.Errorf("%s", err)
|
||||
return err
|
||||
}
|
||||
err := fmt.Errorf("Authentication Failed: Unknown Reason")
|
||||
log.Error("%s", err)
|
||||
log.Errorf("%s", err)
|
||||
return err
|
||||
|
||||
} else if resp.StatusCode == 200 {
|
||||
@@ -58,7 +60,7 @@ func (c *Cli) CmdLogin() error {
|
||||
}
|
||||
|
||||
func (c *Cli) CmdFields() error {
|
||||
log.Debug("fields called")
|
||||
log.Debugf("fields called")
|
||||
uri := fmt.Sprintf("%s/rest/api/2/field", c.endpoint)
|
||||
data, err := responseToJson(c.get(uri))
|
||||
if err != nil {
|
||||
@@ -69,7 +71,7 @@ func (c *Cli) CmdFields() error {
|
||||
}
|
||||
|
||||
func (c *Cli) CmdList() error {
|
||||
log.Debug("list called")
|
||||
log.Debugf("list called")
|
||||
if data, err := c.FindIssues(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
@@ -78,7 +80,7 @@ func (c *Cli) CmdList() error {
|
||||
}
|
||||
|
||||
func (c *Cli) CmdView(issue string) error {
|
||||
log.Debug("view called")
|
||||
log.Debugf("view called")
|
||||
c.Browse(issue)
|
||||
data, err := c.ViewIssue(issue)
|
||||
if err != nil {
|
||||
@@ -88,7 +90,7 @@ func (c *Cli) CmdView(issue string) error {
|
||||
}
|
||||
|
||||
func (c *Cli) CmdEdit(issue string) error {
|
||||
log.Debug("edit called")
|
||||
log.Debugf("edit called")
|
||||
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/editmeta", c.endpoint, issue)
|
||||
editmeta, err := responseToJson(c.get(uri))
|
||||
@@ -113,8 +115,8 @@ func (c *Cli) CmdEdit(issue string) error {
|
||||
issueData,
|
||||
func(json string) error {
|
||||
if c.getOptBool("dryrun", false) {
|
||||
log.Debug("PUT: %s", json)
|
||||
log.Debug("Dryrun mode, skipping PUT")
|
||||
log.Debugf("PUT: %s", json)
|
||||
log.Debugf("Dryrun mode, skipping PUT")
|
||||
return nil
|
||||
}
|
||||
resp, err := c.put(uri, json)
|
||||
@@ -132,7 +134,7 @@ func (c *Cli) CmdEdit(issue string) error {
|
||||
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
||||
resp.Write(logBuffer)
|
||||
err := fmt.Errorf("Unexpected Response From PUT")
|
||||
log.Error("%s:\n%s", err, logBuffer)
|
||||
log.Errorf("%s:\n%s", err, logBuffer)
|
||||
return err
|
||||
}
|
||||
},
|
||||
@@ -140,7 +142,7 @@ func (c *Cli) CmdEdit(issue string) error {
|
||||
}
|
||||
|
||||
func (c *Cli) CmdEditMeta(issue string) error {
|
||||
log.Debug("editMeta called")
|
||||
log.Debugf("editMeta called")
|
||||
c.Browse(issue)
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/editmeta", c.endpoint, issue)
|
||||
data, err := responseToJson(c.get(uri))
|
||||
@@ -152,7 +154,7 @@ func (c *Cli) CmdEditMeta(issue string) error {
|
||||
}
|
||||
|
||||
func (c *Cli) CmdTransitionMeta(issue string) error {
|
||||
log.Debug("tranisionMeta called")
|
||||
log.Debugf("tranisionMeta called")
|
||||
c.Browse(issue)
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/transitions?expand=transitions.fields", c.endpoint, issue)
|
||||
data, err := responseToJson(c.get(uri))
|
||||
@@ -165,7 +167,7 @@ func (c *Cli) CmdTransitionMeta(issue string) error {
|
||||
|
||||
func (c *Cli) CmdIssueTypes() error {
|
||||
project := c.opts["project"].(string)
|
||||
log.Debug("issueTypes called")
|
||||
log.Debugf("issueTypes called")
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/createmeta?projectKeys=%s", c.endpoint, project)
|
||||
data, err := responseToJson(c.get(uri))
|
||||
if err != nil {
|
||||
@@ -179,8 +181,8 @@ func (c *Cli) CmdCreateMeta() error {
|
||||
project := c.opts["project"].(string)
|
||||
issuetype := c.getOptString("issuetype", "Bug")
|
||||
|
||||
log.Debug("createMeta called")
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/createmeta?projectKeys=%s&issuetypeNames=%s&expand=projects.issuetypes.fields", c.endpoint, project, issuetype)
|
||||
log.Debugf("createMeta called")
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/createmeta?projectKeys=%s&issuetypeNames=%s&expand=projects.issuetypes.fields", c.endpoint, project, url.QueryEscape(issuetype))
|
||||
data, err := responseToJson(c.get(uri))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -189,7 +191,7 @@ func (c *Cli) CmdCreateMeta() error {
|
||||
if val, ok := data.(map[string]interface{})["projects"]; ok {
|
||||
if len(val.([]interface{})) == 0 {
|
||||
err = fmt.Errorf("Project '%s' or issuetype '%s' unknown. Unable to createmeta.", project, issuetype)
|
||||
log.Error("%s", err)
|
||||
log.Errorf("%s", err)
|
||||
return err
|
||||
}
|
||||
if val, ok = val.([]interface{})[0].(map[string]interface{})["issuetypes"]; ok {
|
||||
@@ -200,8 +202,18 @@ func (c *Cli) CmdCreateMeta() error {
|
||||
return runTemplate(c.getTemplate("createmeta"), data, nil)
|
||||
}
|
||||
|
||||
func (c *Cli) CmdComponents(project string) error {
|
||||
log.Debugf("Components called")
|
||||
uri := fmt.Sprintf("%s/rest/api/2/project/%s/components", c.endpoint, project)
|
||||
data, err := responseToJson(c.get(uri))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return runTemplate(c.getTemplate("components"), data, nil)
|
||||
}
|
||||
|
||||
func (c *Cli) CmdTransitions(issue string) error {
|
||||
log.Debug("Transitions called")
|
||||
log.Debugf("Transitions called")
|
||||
c.Browse(issue)
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/transitions", c.endpoint, issue)
|
||||
data, err := responseToJson(c.get(uri))
|
||||
@@ -214,9 +226,9 @@ func (c *Cli) CmdTransitions(issue string) error {
|
||||
func (c *Cli) CmdCreate() error {
|
||||
project := c.opts["project"].(string)
|
||||
issuetype := c.getOptString("issuetype", "Bug")
|
||||
log.Debug("create called")
|
||||
log.Debugf("create called")
|
||||
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/createmeta?projectKeys=%s&issuetypeNames=%s&expand=projects.issuetypes.fields", c.endpoint, project, issuetype)
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/createmeta?projectKeys=%s&issuetypeNames=%s&expand=projects.issuetypes.fields", c.endpoint, project, url.QueryEscape(issuetype))
|
||||
data, err := responseToJson(c.get(uri))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -229,13 +241,13 @@ func (c *Cli) CmdCreate() error {
|
||||
if val, ok := data.(map[string]interface{})["projects"]; ok {
|
||||
if len(val.([]interface{})) == 0 {
|
||||
err = fmt.Errorf("Project '%s' or issuetype '%s' unknown. Unable to create issue.", project, issuetype)
|
||||
log.Error("%s", err)
|
||||
log.Errorf("%s", err)
|
||||
return err
|
||||
}
|
||||
if val, ok = val.([]interface{})[0].(map[string]interface{})["issuetypes"]; ok {
|
||||
if len(val.([]interface{})) == 0 {
|
||||
err = fmt.Errorf("Project '%s' does not support issuetype '%s'. Unable to create issue.", project, issuetype)
|
||||
log.Error("%s", err)
|
||||
log.Errorf("%s", err)
|
||||
return err
|
||||
}
|
||||
issueData["meta"] = val.([]interface{})[0]
|
||||
@@ -248,11 +260,11 @@ func (c *Cli) CmdCreate() error {
|
||||
fmt.Sprintf("create-%s-", sanitizedType),
|
||||
issueData,
|
||||
func(json string) error {
|
||||
log.Debug("JSON: %s", json)
|
||||
log.Debugf("JSON: %s", json)
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue", c.endpoint)
|
||||
if c.getOptBool("dryrun", false) {
|
||||
log.Debug("POST: %s", json)
|
||||
log.Debug("Dryrun mode, skipping POST")
|
||||
log.Debugf("POST: %s", json)
|
||||
log.Debugf("Dryrun mode, skipping POST")
|
||||
return nil
|
||||
}
|
||||
resp, err := c.post(uri, json)
|
||||
@@ -281,7 +293,7 @@ func (c *Cli) CmdCreate() error {
|
||||
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
||||
resp.Write(logBuffer)
|
||||
err := fmt.Errorf("Unexpected Response From POST")
|
||||
log.Error("%s:\n%s", err, logBuffer)
|
||||
log.Errorf("%s:\n%s", err, logBuffer)
|
||||
return err
|
||||
}
|
||||
},
|
||||
@@ -290,7 +302,7 @@ func (c *Cli) CmdCreate() error {
|
||||
}
|
||||
|
||||
func (c *Cli) CmdIssueLinkTypes() error {
|
||||
log.Debug("Transitions called")
|
||||
log.Debugf("Transitions called")
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issueLinkType", c.endpoint)
|
||||
data, err := responseToJson(c.get(uri))
|
||||
if err != nil {
|
||||
@@ -300,7 +312,7 @@ func (c *Cli) CmdIssueLinkTypes() error {
|
||||
}
|
||||
|
||||
func (c *Cli) CmdBlocks(blocker string, issue string) error {
|
||||
log.Debug("blocks called")
|
||||
log.Debugf("blocks called")
|
||||
|
||||
json, err := jsonEncode(map[string]interface{}{
|
||||
"type": map[string]string{
|
||||
@@ -319,8 +331,8 @@ func (c *Cli) CmdBlocks(blocker string, issue string) error {
|
||||
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issueLink", c.endpoint)
|
||||
if c.getOptBool("dryrun", false) {
|
||||
log.Debug("POST: %s", json)
|
||||
log.Debug("Dryrun mode, skipping POST")
|
||||
log.Debugf("POST: %s", json)
|
||||
log.Debugf("Dryrun mode, skipping POST")
|
||||
return nil
|
||||
}
|
||||
resp, err := c.post(uri, json)
|
||||
@@ -336,14 +348,14 @@ func (c *Cli) CmdBlocks(blocker string, issue string) error {
|
||||
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
||||
resp.Write(logBuffer)
|
||||
err := fmt.Errorf("Unexpected Response From POST")
|
||||
log.Error("%s:\n%s", err, logBuffer)
|
||||
log.Errorf("%s:\n%s", err, logBuffer)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cli) CmdDups(duplicate string, issue string) error {
|
||||
log.Debug("dups called")
|
||||
log.Debugf("dups called")
|
||||
|
||||
json, err := jsonEncode(map[string]interface{}{
|
||||
"type": map[string]string{
|
||||
@@ -362,8 +374,8 @@ func (c *Cli) CmdDups(duplicate string, issue string) error {
|
||||
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issueLink", c.endpoint)
|
||||
if c.getOptBool("dryrun", false) {
|
||||
log.Debug("POST: %s", json)
|
||||
log.Debug("Dryrun mode, skipping POST")
|
||||
log.Debugf("POST: %s", json)
|
||||
log.Debugf("Dryrun mode, skipping POST")
|
||||
return nil
|
||||
}
|
||||
resp, err := c.post(uri, json)
|
||||
@@ -379,28 +391,40 @@ func (c *Cli) CmdDups(duplicate string, issue string) error {
|
||||
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
||||
resp.Write(logBuffer)
|
||||
err := fmt.Errorf("Unexpected Response From POST")
|
||||
log.Error("%s:\n%s", err, logBuffer)
|
||||
log.Errorf("%s:\n%s", err, logBuffer)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cli) CmdWatch(issue string) error {
|
||||
watcher := c.getOptString("watcher", c.opts["user"].(string))
|
||||
log.Debug("watch called")
|
||||
func (c *Cli) CmdWatch(issue string, watcher string, remove bool) error {
|
||||
log.Debugf("watch called: watcher: %q, remove: %n", watcher, remove)
|
||||
|
||||
var uri string
|
||||
json, err := jsonEncode(watcher)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/watchers", c.endpoint, issue)
|
||||
if c.getOptBool("dryrun", false) {
|
||||
log.Debug("POST: %s", json)
|
||||
log.Debug("Dryrun mode, skipping POST")
|
||||
if !remove {
|
||||
log.Debugf("POST: %s", json)
|
||||
log.Debugf("Dryrun mode, skipping POST")
|
||||
} else {
|
||||
log.Debugf("DELETE: %s", watcher)
|
||||
log.Debugf("Dryrun mode, skipping POST")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
resp, err := c.post(uri, json)
|
||||
|
||||
var resp *http.Response
|
||||
if !remove {
|
||||
uri = fmt.Sprintf("%s/rest/api/2/issue/%s/watchers", c.endpoint, issue)
|
||||
resp, err = c.post(uri, json)
|
||||
} else {
|
||||
uri = fmt.Sprintf("%s/rest/api/2/issue/%s/watchers?username=%s", c.endpoint, issue, watcher)
|
||||
resp, err = c.delete(uri)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -412,15 +436,62 @@ func (c *Cli) CmdWatch(issue string) error {
|
||||
} else {
|
||||
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
||||
resp.Write(logBuffer)
|
||||
err := fmt.Errorf("Unexpected Response From POST")
|
||||
log.Error("%s:\n%s", err, logBuffer)
|
||||
if !remove {
|
||||
err = fmt.Errorf("Unexpected Response From POST")
|
||||
} else {
|
||||
err = fmt.Errorf("Unexpected Response From DELETE")
|
||||
}
|
||||
log.Errorf("%s:\n%s", err, logBuffer)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cli) CmdVote(issue string, up bool) error {
|
||||
log.Debugf("vote called, with up: %n", up)
|
||||
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/votes", c.endpoint, issue)
|
||||
if c.getOptBool("dryrun", false) {
|
||||
if up {
|
||||
log.Debugf("POST: %s", "")
|
||||
log.Debugf("Dryrun mode, skipping POST")
|
||||
} else {
|
||||
log.Debugf("DELETE: %s", "")
|
||||
log.Debugf("Dryrun mode, skipping DELETE")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
var resp *http.Response
|
||||
var err error
|
||||
if up {
|
||||
resp, err = c.post(uri, "")
|
||||
} else {
|
||||
resp, err = c.delete(uri)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode == 204 {
|
||||
c.Browse(issue)
|
||||
if !c.opts["quiet"].(bool) {
|
||||
fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue)
|
||||
}
|
||||
} else {
|
||||
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
||||
resp.Write(logBuffer)
|
||||
if up {
|
||||
err = fmt.Errorf("Unexpected Response From POST")
|
||||
} else {
|
||||
err = fmt.Errorf("Unexpected Response From DELETE")
|
||||
}
|
||||
log.Errorf("%s:\n%s", err, logBuffer)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cli) CmdTransition(issue string, trans string) error {
|
||||
log.Debug("transition called")
|
||||
log.Debugf("transition called")
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/transitions?expand=transitions.fields", c.endpoint, issue)
|
||||
data, err := responseToJson(c.get(uri))
|
||||
if err != nil {
|
||||
@@ -443,17 +514,17 @@ func (c *Cli) CmdTransition(issue string, trans string) error {
|
||||
}
|
||||
if transId == "" {
|
||||
err := fmt.Errorf("Invalid Transition '%s', Available: %s", trans, strings.Join(found, ", "))
|
||||
log.Error("%s", err)
|
||||
log.Errorf("%s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
handlePost := func(json string) error {
|
||||
log.Debug("POST: %s", json)
|
||||
log.Debugf("POST: %s", json)
|
||||
// os.Exit(0)
|
||||
uri = fmt.Sprintf("%s/rest/api/2/issue/%s/transitions", c.endpoint, issue)
|
||||
if c.getOptBool("dryrun", false) {
|
||||
log.Debug("POST: %s", json)
|
||||
log.Debug("Dryrun mode, skipping POST")
|
||||
log.Debugf("POST: %s", json)
|
||||
log.Debugf("Dryrun mode, skipping POST")
|
||||
return nil
|
||||
}
|
||||
resp, err := c.post(uri, json)
|
||||
@@ -469,7 +540,7 @@ func (c *Cli) CmdTransition(issue string, trans string) error {
|
||||
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
||||
resp.Write(logBuffer)
|
||||
err := fmt.Errorf("Unexpected Response From POST")
|
||||
log.Error("%s:\n%s", err, logBuffer)
|
||||
log.Errorf("%s:\n%s", err, logBuffer)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -498,14 +569,14 @@ func (c *Cli) CmdTransition(issue string, trans string) error {
|
||||
}
|
||||
|
||||
func (c *Cli) CmdComment(issue string) error {
|
||||
log.Debug("comment called")
|
||||
log.Debugf("comment called")
|
||||
|
||||
handlePost := func(json string) error {
|
||||
log.Debug("JSON: %s", json)
|
||||
log.Debugf("JSON: %s", json)
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/comment", c.endpoint, issue)
|
||||
if c.getOptBool("dryrun", false) {
|
||||
log.Debug("POST: %s", json)
|
||||
log.Debug("Dryrun mode, skipping POST")
|
||||
log.Debugf("POST: %s", json)
|
||||
log.Debugf("Dryrun mode, skipping POST")
|
||||
return nil
|
||||
}
|
||||
resp, err := c.post(uri, json)
|
||||
@@ -523,7 +594,7 @@ func (c *Cli) CmdComment(issue string) error {
|
||||
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
||||
resp.Write(logBuffer)
|
||||
err := fmt.Errorf("Unexpected Response From POST")
|
||||
log.Error("%s:\n%s", err, logBuffer)
|
||||
log.Errorf("%s:\n%s", err, logBuffer)
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -547,19 +618,62 @@ func (c *Cli) CmdComment(issue string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cli) CmdComponent(action string, project string, name string, desc string, lead string) error {
|
||||
log.Debugf("component called")
|
||||
|
||||
switch action {
|
||||
case "add":
|
||||
default:
|
||||
return errors.New(fmt.Sprintf("CmdComponent: %q is not a valid action", action))
|
||||
}
|
||||
|
||||
json, err := jsonEncode(map[string]interface{}{
|
||||
"name": name,
|
||||
"description": desc,
|
||||
"leadUserName": lead,
|
||||
"project": project,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uri := fmt.Sprintf("%s/rest/api/2/component", c.endpoint)
|
||||
if c.getOptBool("dryrun", false) {
|
||||
log.Debugf("POST: %s", json)
|
||||
log.Debugf("Dryrun mode, skipping POST")
|
||||
return nil
|
||||
}
|
||||
resp, err := c.post(uri, json)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode == 201 {
|
||||
if !c.opts["quiet"].(bool) {
|
||||
fmt.Printf("OK %s %s\n", project, name)
|
||||
}
|
||||
} else {
|
||||
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
||||
resp.Write(logBuffer)
|
||||
err := fmt.Errorf("Unexpected Response From POST")
|
||||
log.Errorf("%s:\n%s", err, logBuffer)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cli) CmdLabels(action string, issue string, labels []string) error {
|
||||
log.Debug("label called")
|
||||
log.Debugf("label called")
|
||||
|
||||
if action != "add" && action != "remove" && action != "set" {
|
||||
return fmt.Errorf("action must be 'add', 'set' or 'remove': %q is invalid", action)
|
||||
}
|
||||
|
||||
handlePut := func(json string) error {
|
||||
log.Debug("JSON: %s", json)
|
||||
log.Debugf("JSON: %s", json)
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue)
|
||||
if c.getOptBool("dryrun", false) {
|
||||
log.Debug("PUT: %s", json)
|
||||
log.Debug("Dryrun mode, skipping POST")
|
||||
log.Debugf("PUT: %s", json)
|
||||
log.Debugf("Dryrun mode, skipping POST")
|
||||
return nil
|
||||
}
|
||||
resp, err := c.put(uri, json)
|
||||
@@ -577,7 +691,7 @@ func (c *Cli) CmdLabels(action string, issue string, labels []string) error {
|
||||
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
||||
resp.Write(logBuffer)
|
||||
err := fmt.Errorf("Unexpected Response From PUT")
|
||||
log.Error("%s:\n%s", err, logBuffer)
|
||||
log.Errorf("%s:\n%s", err, logBuffer)
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -613,7 +727,7 @@ func (c *Cli) CmdLabels(action string, issue string, labels []string) error {
|
||||
}
|
||||
|
||||
func (c *Cli) CmdAssign(issue string, user string) error {
|
||||
log.Debug("assign called")
|
||||
log.Debugf("assign called")
|
||||
|
||||
json, err := jsonEncode(map[string]interface{}{
|
||||
"name": user,
|
||||
@@ -624,8 +738,8 @@ func (c *Cli) CmdAssign(issue string, user string) error {
|
||||
|
||||
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/assignee", c.endpoint, issue)
|
||||
if c.getOptBool("dryrun", false) {
|
||||
log.Debug("PUT: %s", json)
|
||||
log.Debug("Dryrun mode, skipping PUT")
|
||||
log.Debugf("PUT: %s", json)
|
||||
log.Debugf("Dryrun mode, skipping PUT")
|
||||
return nil
|
||||
}
|
||||
resp, err := c.put(uri, json)
|
||||
@@ -641,7 +755,7 @@ func (c *Cli) CmdAssign(issue string, user string) error {
|
||||
logBuffer := bytes.NewBuffer(make([]byte, 0))
|
||||
resp.Write(logBuffer)
|
||||
err := fmt.Errorf("Unexpected Response From PUT")
|
||||
log.Error("%s:\n%s", err, logBuffer)
|
||||
log.Errorf("%s:\n%s", err, logBuffer)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -663,11 +777,11 @@ func (c *Cli) CmdExportTemplates() error {
|
||||
continue
|
||||
}
|
||||
if fh, err := os.OpenFile(templateFile, os.O_WRONLY|os.O_CREATE, 0644); err != nil {
|
||||
log.Error("Failed to open %s for writing: %s", templateFile, err)
|
||||
log.Errorf("Failed to open %s for writing: %s", templateFile, err)
|
||||
return err
|
||||
} else {
|
||||
defer fh.Close()
|
||||
log.Notice("Creating %s", templateFile)
|
||||
log.Noticef("Creating %s", templateFile)
|
||||
fh.Write([]byte(template))
|
||||
}
|
||||
}
|
||||
@@ -675,7 +789,7 @@ func (c *Cli) CmdExportTemplates() error {
|
||||
}
|
||||
|
||||
func (c *Cli) CmdRequest(uri, content string) (err error) {
|
||||
log.Debug("request called")
|
||||
log.Debugf("request called")
|
||||
|
||||
if !strings.HasPrefix(uri, "http") {
|
||||
uri = fmt.Sprintf("%s%s", c.endpoint, uri)
|
||||
|
||||
+75
-20
@@ -5,8 +5,8 @@ import (
|
||||
"fmt"
|
||||
"github.com/Netflix-Skunkworks/go-jira"
|
||||
"github.com/coryb/optigo"
|
||||
"github.com/op/go-logging"
|
||||
"gopkg.in/coryb/yaml.v2"
|
||||
"gopkg.in/op/go-logging.v1"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -56,7 +56,8 @@ Usage:
|
||||
jira create [--noedit] [-p PROJECT] <Create Options>
|
||||
jira DUPLICATE dups ISSUE
|
||||
jira BLOCKER blocks ISSUE
|
||||
jira watch ISSUE [-w WATCHER]
|
||||
jira vote ISSUE [--down]
|
||||
jira watch ISSUE [-w WATCHER] [--remove]
|
||||
jira (trans|transition) TRANSITION ISSUE [--noedit] <Edit Options>
|
||||
jira ack ISSUE [--edit] <Edit Options>
|
||||
jira close ISSUE [--edit] <Edit Options>
|
||||
@@ -64,6 +65,9 @@ Usage:
|
||||
jira reopen ISSUE [--edit] <Edit Options>
|
||||
jira start ISSUE [--edit] <Edit Options>
|
||||
jira stop ISSUE [--edit] <Edit Options>
|
||||
jira todo ISSUE [--edit] <Edit Options>
|
||||
jira done ISSUE [--edit] <Edit Options>
|
||||
jira prog|progress|in-progress [--edit] <Edit Options>
|
||||
jira comment ISSUE [--noedit] <Edit Options>
|
||||
jira (set,add,remove) labels ISSUE [LABEL] ...
|
||||
jira take ISSUE
|
||||
@@ -72,6 +76,8 @@ Usage:
|
||||
jira issuelinktypes
|
||||
jira transmeta ISSUE
|
||||
jira editmeta ISSUE
|
||||
jira add component [-p PROJECT] NAME DESCRIPTION LEAD
|
||||
jira components [-p PROJECT]
|
||||
jira issuetypes [-p PROJECT]
|
||||
jira createmeta [-p PROJECT] [-i ISSUETYPE]
|
||||
jira transitions ISSUE
|
||||
@@ -137,9 +143,16 @@ Command Options:
|
||||
"reopen": "reopen",
|
||||
"start": "start",
|
||||
"stop": "stop",
|
||||
"todo": "todo",
|
||||
"done": "done",
|
||||
"prog": "in-progress",
|
||||
"progress": "in-progress",
|
||||
"in-progress": "in-progress",
|
||||
"comment": "comment",
|
||||
"label": "labels",
|
||||
"labels": "labels",
|
||||
"component": "component",
|
||||
"components": "components",
|
||||
"take": "take",
|
||||
"assign": "assign",
|
||||
"give": "assign",
|
||||
@@ -155,6 +168,7 @@ Command Options:
|
||||
"login": "login",
|
||||
"req": "request",
|
||||
"request": "request",
|
||||
"vote": "vote",
|
||||
}
|
||||
|
||||
defaults := map[string]interface{}{
|
||||
@@ -194,8 +208,10 @@ Command Options:
|
||||
"a|assignee=s": setopt,
|
||||
"i|issuetype=s": setopt,
|
||||
"w|watcher=s": setopt,
|
||||
"remove": setopt,
|
||||
"r|reporter=s": setopt,
|
||||
"f|queryfields=s": setopt,
|
||||
"x|expand=s": setopt,
|
||||
"s|sort=s": setopt,
|
||||
"l|limit|max_results=i": setopt,
|
||||
"o|override=s%": &opts,
|
||||
@@ -206,10 +222,11 @@ Command Options:
|
||||
"M|method=s": setopt,
|
||||
"S|saveFile=s": setopt,
|
||||
"Q|quiet": setopt,
|
||||
"down": setopt,
|
||||
})
|
||||
|
||||
if err := op.ProcessAll(os.Args[1:]); err != nil {
|
||||
log.Error("%s", err)
|
||||
log.Errorf("%s", err)
|
||||
usage(false)
|
||||
}
|
||||
args := op.Args
|
||||
@@ -250,36 +267,36 @@ Command Options:
|
||||
// apply defaults
|
||||
for k, v := range defaults {
|
||||
if _, ok := opts[k]; !ok {
|
||||
log.Debug("Setting %q to %#v from defaults", k, v)
|
||||
log.Debugf("Setting %q to %#v from defaults", k, v)
|
||||
opts[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
log.Debug("opts: %v", opts)
|
||||
log.Debug("args: %v", args)
|
||||
log.Debugf("opts: %v", opts)
|
||||
log.Debugf("args: %v", args)
|
||||
|
||||
if _, ok := opts["endpoint"]; !ok {
|
||||
log.Error("endpoint option required. Either use --endpoint or set a endpoint option in your ~/.jira.d/config.yml file")
|
||||
log.Errorf("endpoint option required. Either use --endpoint or set a endpoint option in your ~/.jira.d/config.yml file")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
c := jira.New(opts)
|
||||
|
||||
log.Debug("opts: %s", opts)
|
||||
log.Debugf("opts: %s", opts)
|
||||
|
||||
setEditing := func(dflt bool) {
|
||||
log.Debug("Default Editing: %t", dflt)
|
||||
log.Debugf("Default Editing: %t", dflt)
|
||||
if dflt {
|
||||
if val, ok := opts["noedit"].(bool); ok && val {
|
||||
log.Debug("Setting edit = false")
|
||||
log.Debugf("Setting edit = false")
|
||||
opts["edit"] = false
|
||||
} else {
|
||||
log.Debug("Setting edit = true")
|
||||
log.Debugf("Setting edit = true")
|
||||
opts["edit"] = true
|
||||
}
|
||||
} else {
|
||||
if _, ok := opts["edit"].(bool); !ok {
|
||||
log.Debug("Setting edit = %t", dflt)
|
||||
log.Debugf("Setting edit = %t", dflt)
|
||||
opts["edit"] = dflt
|
||||
}
|
||||
}
|
||||
@@ -287,7 +304,7 @@ Command Options:
|
||||
|
||||
requireArgs := func(count int) {
|
||||
if len(args) < count {
|
||||
log.Error("Not enough arguments. %d required, %d provided", count, len(args))
|
||||
log.Errorf("Not enough arguments. %d required, %d provided", count, len(args))
|
||||
usage(false)
|
||||
}
|
||||
}
|
||||
@@ -350,7 +367,9 @@ Command Options:
|
||||
}
|
||||
case "watch":
|
||||
requireArgs(1)
|
||||
err = c.CmdWatch(args[0])
|
||||
watcher := c.GetOptString("watcher", opts["user"].(string))
|
||||
remove := c.GetOptBool("remove", false)
|
||||
err = c.CmdWatch(args[0], watcher, remove)
|
||||
case "transition":
|
||||
requireArgs(2)
|
||||
setEditing(true)
|
||||
@@ -379,6 +398,18 @@ Command Options:
|
||||
requireArgs(1)
|
||||
setEditing(false)
|
||||
err = c.CmdTransition(args[0], "stop")
|
||||
case "todo":
|
||||
requireArgs(1)
|
||||
setEditing(false)
|
||||
err = c.CmdTransition(args[0], "To Do")
|
||||
case "done":
|
||||
requireArgs(1)
|
||||
setEditing(false)
|
||||
err = c.CmdTransition(args[0], "Done")
|
||||
case "in-progress":
|
||||
requireArgs(1)
|
||||
setEditing(false)
|
||||
err = c.CmdTransition(args[0], "In Progress")
|
||||
case "comment":
|
||||
requireArgs(1)
|
||||
setEditing(true)
|
||||
@@ -389,6 +420,23 @@ Command Options:
|
||||
issue := args[1]
|
||||
labels := args[2:]
|
||||
err = c.CmdLabels(action, issue, labels)
|
||||
case "component":
|
||||
requireArgs(2)
|
||||
action := args[0]
|
||||
project := opts["project"].(string)
|
||||
name := args[1]
|
||||
var lead string
|
||||
var description string
|
||||
if len(args) > 2 {
|
||||
description = args[2]
|
||||
}
|
||||
if len(args) > 3 {
|
||||
lead = args[2]
|
||||
}
|
||||
err = c.CmdComponent(action, project, name, description, lead)
|
||||
case "components":
|
||||
project := opts["project"].(string)
|
||||
err = c.CmdComponents(project)
|
||||
case "take":
|
||||
requireArgs(1)
|
||||
err = c.CmdAssign(args[0], opts["user"].(string))
|
||||
@@ -404,6 +452,13 @@ Command Options:
|
||||
case "view":
|
||||
requireArgs(1)
|
||||
err = c.CmdView(args[0])
|
||||
case "vote":
|
||||
requireArgs(1)
|
||||
if val, ok := opts["down"]; ok {
|
||||
err = c.CmdVote(args[0], !val.(bool))
|
||||
} else {
|
||||
err = c.CmdVote(args[0], true)
|
||||
}
|
||||
case "request":
|
||||
requireArgs(1)
|
||||
data := ""
|
||||
@@ -412,12 +467,12 @@ Command Options:
|
||||
}
|
||||
err = c.CmdRequest(args[0], data)
|
||||
default:
|
||||
log.Error("Unknown command %s", command)
|
||||
log.Errorf("Unknown command %s", command)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Error("%s", err)
|
||||
log.Errorf("%s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
@@ -425,7 +480,7 @@ Command Options:
|
||||
|
||||
func parseYaml(file string, opts map[string]interface{}) {
|
||||
if fh, err := ioutil.ReadFile(file); err == nil {
|
||||
log.Debug("Found Config file: %s", file)
|
||||
log.Debugf("Found Config file: %s", file)
|
||||
yaml.Unmarshal(fh, &opts)
|
||||
}
|
||||
}
|
||||
@@ -465,21 +520,21 @@ func loadConfigs(opts map[string]interface{}) {
|
||||
if stat.Mode()&0111 == 0 {
|
||||
parseYaml(file, tmp)
|
||||
} else {
|
||||
log.Debug("Found Executable Config file: %s", file)
|
||||
log.Debugf("Found Executable Config file: %s", file)
|
||||
// it is executable, so run it and try to parse the output
|
||||
cmd := exec.Command(file)
|
||||
stdout := bytes.NewBufferString("")
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = bytes.NewBufferString("")
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Error("%s is exectuable, but it failed to execute: %s\n%s", file, err, cmd.Stderr)
|
||||
log.Errorf("%s is exectuable, but it failed to execute: %s\n%s", file, err, cmd.Stderr)
|
||||
os.Exit(1)
|
||||
}
|
||||
yaml.Unmarshal(stdout.Bytes(), &tmp)
|
||||
}
|
||||
for k, v := range tmp {
|
||||
if _, ok := opts[k]; !ok {
|
||||
log.Debug("Setting %q to %#v from %s", k, v, file)
|
||||
log.Debugf("Setting %q to %#v from %s", k, v, file)
|
||||
opts[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -12,6 +12,7 @@ var all_templates = map[string]string{
|
||||
"view": default_view_template,
|
||||
"edit": default_edit_template,
|
||||
"transitions": default_transitions_template,
|
||||
"components": default_components_template,
|
||||
"issuetypes": default_issuetypes_template,
|
||||
"create": default_create_template,
|
||||
"comment": default_comment_template,
|
||||
@@ -64,8 +65,8 @@ fields:
|
||||
- name: {{ .name }}{{end}}{{end}}
|
||||
assignee:
|
||||
name: {{ if .overrides.assignee }}{{.overrides.assignee}}{{else}}{{if .fields.assignee }}{{ .fields.assignee.name }}{{end}}{{end}}
|
||||
reporter:
|
||||
name: {{ if .overrides.reporter }}{{ .overrides.reporter }}{{else if .fields.reporter}}{{ .fields.reporter.name }}{{end}}
|
||||
# reporter:
|
||||
# name: {{ if .overrides.reporter }}{{ .overrides.reporter }}{{else if .fields.reporter}}{{ .fields.reporter.name }}{{end}}
|
||||
# watchers
|
||||
customfield_10110: {{ range .fields.customfield_10110 }}
|
||||
- name: {{ .name }}{{end}}{{if .overrides.watcher}}
|
||||
@@ -82,6 +83,9 @@ fields:
|
||||
const default_transitions_template = `{{ range .transitions }}{{.id }}: {{.name}}
|
||||
{{end}}`
|
||||
|
||||
const default_components_template = `{{ range . }}{{.id }}: {{.name}}
|
||||
{{end}}`
|
||||
|
||||
const default_issuetypes_template = `{{ range .projects }}{{ range .issuetypes }}{{color "+bh"}}{{.name | append ":" | printf "%-13s" }}{{color "reset"}} {{.description}}
|
||||
{{end}}{{end}}`
|
||||
|
||||
|
||||
@@ -12,35 +12,46 @@ import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
)
|
||||
|
||||
func homedir() string {
|
||||
if runtime.GOOS == "windows" {
|
||||
return os.Getenv("USERPROFILE")
|
||||
}
|
||||
return os.Getenv("HOME")
|
||||
}
|
||||
|
||||
func FindParentPaths(fileName string) []string {
|
||||
cwd, _ := os.Getwd()
|
||||
|
||||
paths := make([]string, 0)
|
||||
|
||||
// special case if homedir is not in current path then check there anyway
|
||||
homedir := os.Getenv("HOME")
|
||||
if !strings.HasPrefix(cwd, homedir) {
|
||||
file := fmt.Sprintf("%s/%s", homedir, fileName)
|
||||
if _, err := os.Stat(file); err == nil {
|
||||
paths = append(paths, file)
|
||||
homedir := homedir()
|
||||
if !filepath.HasPrefix(cwd, homedir) {
|
||||
path := filepath.Join(homedir, fileName)
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
paths = append(paths, path)
|
||||
}
|
||||
}
|
||||
|
||||
var dir string
|
||||
for _, part := range strings.Split(cwd, string(os.PathSeparator)) {
|
||||
if dir == "/" {
|
||||
dir = fmt.Sprintf("/%s", part)
|
||||
} else {
|
||||
dir = fmt.Sprintf("%s/%s", dir, part)
|
||||
path := filepath.Join(cwd, fileName)
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
paths = append(paths, path)
|
||||
}
|
||||
for true {
|
||||
cwd = filepath.Dir(cwd)
|
||||
path := filepath.Join(cwd, fileName)
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
paths = append(paths, path)
|
||||
}
|
||||
file := fmt.Sprintf("%s/%s", dir, fileName)
|
||||
if _, err := os.Stat(file); err == nil {
|
||||
paths = append(paths, file)
|
||||
if cwd[len(cwd)-1] == filepath.Separator {
|
||||
break
|
||||
}
|
||||
}
|
||||
return paths
|
||||
@@ -57,8 +68,9 @@ func FindClosestParentPath(fileName string) (string, error) {
|
||||
func readFile(file string) string {
|
||||
var bytes []byte
|
||||
var err error
|
||||
log.Debugf("readFile: reading %q", file)
|
||||
if bytes, err = ioutil.ReadFile(file); err != nil {
|
||||
log.Error("Failed to read file %s: %s", file, err)
|
||||
log.Errorf("Failed to read file %s: %s", file, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return string(bytes)
|
||||
@@ -195,11 +207,11 @@ func runTemplate(templateContent string, data interface{}, out io.Writer) error
|
||||
},
|
||||
}
|
||||
if tmpl, err := template.New("template").Funcs(funcs).Parse(templateContent); err != nil {
|
||||
log.Error("Failed to parse template: %s", err)
|
||||
log.Errorf("Failed to parse template: %s", err)
|
||||
return err
|
||||
} else {
|
||||
if err := tmpl.Execute(out, data); err != nil {
|
||||
log.Error("Failed to execute template: %s", err)
|
||||
log.Errorf("Failed to execute template: %s", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -215,7 +227,7 @@ func responseToJson(resp *http.Response, err error) (interface{}, error) {
|
||||
if resp.StatusCode == 400 {
|
||||
if val, ok := data.(map[string]interface{})["errorMessages"]; ok {
|
||||
for _, errMsg := range val.([]interface{}) {
|
||||
log.Error("%s", errMsg)
|
||||
log.Errorf("%s", errMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -228,7 +240,7 @@ func jsonDecode(io io.Reader) interface{} {
|
||||
var data interface{}
|
||||
err = json.Unmarshal(content, &data)
|
||||
if err != nil {
|
||||
log.Error("JSON Parse Error: %s from %s", err, content)
|
||||
log.Errorf("JSON Parse Error: %s from %s", err, content)
|
||||
}
|
||||
return data
|
||||
}
|
||||
@@ -239,7 +251,7 @@ func jsonEncode(data interface{}) (string, error) {
|
||||
|
||||
err := enc.Encode(data)
|
||||
if err != nil {
|
||||
log.Error("Failed to encode data %s: %s", data, err)
|
||||
log.Errorf("Failed to encode data %s: %s", data, err)
|
||||
return "", err
|
||||
}
|
||||
return buffer.String(), nil
|
||||
@@ -249,7 +261,7 @@ func jsonWrite(file string, data interface{}) {
|
||||
fh, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
defer fh.Close()
|
||||
if err != nil {
|
||||
log.Error("Failed to open %s: %s", file, err)
|
||||
log.Errorf("Failed to open %s: %s", file, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
enc := json.NewEncoder(fh)
|
||||
@@ -260,11 +272,11 @@ func yamlWrite(file string, data interface{}) {
|
||||
fh, err := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
defer fh.Close()
|
||||
if err != nil {
|
||||
log.Error("Failed to open %s: %s", file, err)
|
||||
log.Errorf("Failed to open %s: %s", file, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if out, err := yaml.Marshal(data); err != nil {
|
||||
log.Error("Failed to marshal yaml %v: %s", data, err)
|
||||
log.Errorf("Failed to marshal yaml %v: %s", data, err)
|
||||
os.Exit(1)
|
||||
} else {
|
||||
fh.Write(out)
|
||||
@@ -306,7 +318,7 @@ func yamlFixup(data interface{}) (interface{}, error) {
|
||||
}
|
||||
default:
|
||||
err := fmt.Errorf("YAML: key %s is type '%T', require 'string'", key, k)
|
||||
log.Error("%s", err)
|
||||
log.Errorf("%s", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -343,16 +355,16 @@ func yamlFixup(data interface{}) (interface{}, error) {
|
||||
|
||||
func mkdir(dir string) error {
|
||||
if stat, err := os.Stat(dir); err != nil && !os.IsNotExist(err) {
|
||||
log.Error("Failed to stat %s: %s", dir, err)
|
||||
log.Errorf("Failed to stat %s: %s", dir, err)
|
||||
return err
|
||||
} else if err == nil && !stat.IsDir() {
|
||||
err := fmt.Errorf("%s exists and is not a directory!", dir)
|
||||
log.Error("%s", err)
|
||||
log.Errorf("%s", err)
|
||||
return err
|
||||
} else {
|
||||
// dir does not exist, so try to create it
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
log.Error("Failed to mkdir -p %s: %s", dir, err)
|
||||
log.Errorf("Failed to mkdir -p %s: %s", dir, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user