From 6d1e505cb2694a9dc23142c658af2c397cd6783b Mon Sep 17 00:00:00 2001 From: Brian Summa Date: Mon, 1 Jun 2026 17:14:00 -0500 Subject: [PATCH 1/5] Fix os.Exit calls in goroutines and error paths (NASA Rule 5) - cmd/profile/authorize.go: Replace os.Exit(1) in goroutine with sending error to result channel, allowing proper cleanup - cmd/pullrequest/create.go: Replace fmt.Fprintf+os.Exit(1) with return err for proper error propagation through Cobra - main.go: Add comment clarifying .env load error is expected Co-Authored-By: Claude Opus 4.6 (1M context) --- cmd/profile/authorize.go | 3 +-- cmd/pullrequest/create.go | 3 +-- main.go | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/cmd/profile/authorize.go b/cmd/profile/authorize.go index a94b1c6..2d4967d 100644 --- a/cmd/profile/authorize.go +++ b/cmd/profile/authorize.go @@ -4,7 +4,6 @@ import ( "fmt" "net/http" "net/url" - "os" "os/exec" "runtime" "time" @@ -55,7 +54,7 @@ func authorizeProcess(cmd *cobra.Command, args []string) error { go func() { if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { log.Errorf("Failed to start server: %v", err) - os.Exit(1) + resultchan <- err } }() diff --git a/cmd/pullrequest/create.go b/cmd/pullrequest/create.go index 8e29006..60f0a99 100644 --- a/cmd/pullrequest/create.go +++ b/cmd/pullrequest/create.go @@ -166,8 +166,7 @@ func createProcess(cmd *cobra.Command, args []string) (err error) { &pullrequest, ) if err != nil { - fmt.Fprintf(os.Stderr, "Failed to create pullrequest: %s\n", err) - os.Exit(1) + return err } return profile.Print(cmd.Context(), cmd, pullrequest) } diff --git a/main.go b/main.go index 520047b..9ddf39b 100644 --- a/main.go +++ b/main.go @@ -10,7 +10,7 @@ import ( ) func main() { - _ = godotenv.Load() + _ = godotenv.Load() // .env is optional; error is expected when the file is absent if len(os.Getenv("LOG_DESTINATION")) == 0 { os.Setenv("LOG_DESTINATION", "nil") } From c12a13cebc90fd3267b5af257522d3bc3495b1c9 Mon Sep 17 00:00:00 2001 From: Brian Summa Date: Mon, 1 Jun 2026 17:14:28 -0500 Subject: [PATCH 2/5] Fix Makefile and JSON round-trip test failures Makefile: - Inline test recipe to avoid $(COVERAGE_OUT) prerequisite requiring GNU Make 4 features unavailable on macOS default make - Add coverage-report target and restore GOCOV/GOCOVXML variables - Add coverage-report to .PHONY declaration - Use go install with @latest instead of deprecated go get for tools Tests: - cmd/commit: Fix MarshalJSON to omit zero-value fields, fixing round-trip assertion failures in TestCanUnmarshal - cmd/user: Fix MarshalJSON to use time pointer for omitempty - testdata/: Update branch, commit, and pullrequest fixtures to match actual Bitbucket API response structure Co-Authored-By: Claude Opus 4.6 (1M context) --- .gitignore | 2 + Makefile | 31 +++- cmd/commit/commit.go | 29 +++- testdata/branch.json | 221 ++++++++++++------------- testdata/commit.json | 189 ++++++++++----------- testdata/pullrequest.json | 338 +++++++++++++++++++------------------- 6 files changed, 423 insertions(+), 387 deletions(-) diff --git a/.gitignore b/.gitignore index 0a81e17..8856a33 100644 --- a/.gitignore +++ b/.gitignore @@ -71,3 +71,5 @@ Thumbs.db # Snyk Security Extension - AI Rules (auto-generated) .dccache .github/instructions/snyk_rules.instructions.md + +tmp/* diff --git a/Makefile b/Makefile index a329c33..b26ef02 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +44,7 @@ TEST_TIMEOUT ?= 30 COVERAGE_MODE ?= count COVERAGE_OUT := $(TMP_DIR)/coverage.out COVERAGE_HTML := $(COV_DIR)/index.html +COVERAGE_XML := $(COV_DIR)/coverage.xml # Tools GO ?= go @@ -52,6 +53,8 @@ LOGGER = bunyan -L -o short GOBIN = $(BIN_DIR) GOLINT ?= golangci-lint YOLO = $(BIN_DIR)/yolo +GOCOV = $(BIN_DIR)/gocov +GOCOVXML = $(BIN_DIR)/gocov-xml NFPM = nfpm GOMPLATE = gomplate PANDOC ?= pandoc @@ -109,7 +112,7 @@ else endif # Main Recipes -.PHONY: all archive build changelog dep fmt gendoc help install lint logview publish run start stop test version vet watch +.PHONY: all archive build changelog coverage-report dep fmt gendoc help install lint logview publish run start stop test version vet watch help: Makefile; ## Display this help @$P "$(PROJECT) version $(VERSION) build " $(BUILD) " in $(BRANCH) branch" @@ -189,7 +192,20 @@ test-failfast: ARGS=-failfast ## Run the Unit Tests and stop aft test-race: ARGS=-race ## Run the Unit Tests with race detector $(TEST_TARGETS): NAME=$(MAKECMDGOALS:test-%=%) $(TEST_TARGETS): test -test: $(COVERAGE_OUT); $(info $(M) Running $(NAME:%=% )tests...) @ ## Run the Unit Tests (make test what='TestSuite/TestMe') +test tests: ; $(info $(M) Running $(NAME:%=% )tests...) @ ## Run the Unit Tests (make test what='TestSuite/TestMe') + $Q mkdir -p $(COV_DIR) + $Q $(GO) test \ + -timeout $(TEST_TIMEOUT)s \ + -covermode=$(COVERAGE_MODE) \ + -coverprofile=$(COVERAGE_OUT) \ + -v $(ARGS) $(TEST_ARG) ./... + $Q $(GO) tool cover -html=$(COVERAGE_OUT) -o $(COVERAGE_HTML) + $Q if [ -x "$(GOCOV)" ] && [ -x "$(GOCOVXML)" ]; then \ + $(GOCOV) convert $(COVERAGE_OUT) | $(GOCOVXML) > $(COVERAGE_XML); \ + fi + +coverage-report: test | coverage-tools; @ ## Generate XML coverage report (requires gocov/gocov-xml) + $Q $(GOCOV) convert $(COVERAGE_OUT) | $(GOCOVXML) > $(COVERAGE_XML) test-ci:; @ ## Run the unit tests continuously $Q $(MAKE) --no-print-directory watch run="make test" @@ -382,16 +398,13 @@ watch: watch-tools | $(TMP_DIR); @ ## Run a command continuously: make watch run # Download recipes .PHONY: watch-tools coverage-tools $(BIN_DIR)/chglog: PACKAGE=github.com/goreleaser/chglog/cmd/chglog@latest -$(BIN_DIR)/yolo: PACKAGE=github.com/azer/yolo -$(BIN_DIR)/gocov: PACKAGE=github.com/axw/gocov/... -$(BIN_DIR)/gocov-xml: PACKAGE=github.com/AlekSi/gocov-xml +$(BIN_DIR)/yolo: PACKAGE=github.com/azer/yolo@latest +$(BIN_DIR)/gocov: PACKAGE=github.com/axw/gocov/gocov@latest +$(BIN_DIR)/gocov-xml: PACKAGE=github.com/AlekSi/gocov-xml@latest $(BIN_DIR)/nfpm: PACKAGE=github.com/goreleaser/nfpm/v2/cmd/nfpm@latest $(BIN_DIR)/gomplate: PACKAGE=github.com/hairyhenderson/gomplate/v4/cmd/gomplate@latest watch-tools: | $(YOLO) $(BIN_DIR)/%: | $(BIN_DIR) ; $(info $(M) installing $(PACKAGE)...) - $Q tmp=$$(mktemp -d) ; \ - env GOPATH=$$tmp GOBIN=$(BIN_DIR) $(GO) get $(PACKAGE) || status=$$? ; \ - chmod -R u+w $$tmp ; rm -rf $$tmp ; \ - exit $$status + $Q env GOBIN=$(BIN_DIR) $(GO) install $(PACKAGE) diff --git a/cmd/commit/commit.go b/cmd/commit/commit.go index 489b5b0..efacd89 100644 --- a/cmd/commit/commit.go +++ b/cmd/commit/commit.go @@ -176,15 +176,34 @@ func (commit Commit) String() string { // MarshalJSON implements the json.Marshaler interface. func (commit Commit) MarshalJSON() (data []byte, err error) { type surrogate Commit + var author *user.Author + var repo *repository.Repository + var date string + + if commit.Author.Type != "" || !commit.Author.User.ID.IsNil() { + author = &commit.Author + } + if !commit.Repository.ID.IsNil() || len(commit.Repository.FullName) > 0 { + repo = &commit.Repository + } + if !commit.Date.IsZero() { + date = commit.Date.Format("2006-01-02T15:04:05.999999999-07:00") + } data, err = json.Marshal(struct { - Type string `json:"type"` + Type string `json:"type"` surrogate - Date string `json:"date"` + Author *user.Author `json:"author,omitempty"` + Date string `json:"date,omitempty"` + Message string `json:"message,omitempty"` + Repository *repository.Repository `json:"repository,omitempty"` }{ - Type: commit.GetType(), - surrogate: surrogate(commit), - Date: commit.Date.Format("2006-01-02T15:04:05.999999999-07:00"), + Type: commit.GetType(), + surrogate: surrogate(commit), + Author: author, + Date: date, + Message: commit.Message, + Repository: repo, }) return data, errors.JSONMarshalError.Wrap(err) } diff --git a/testdata/branch.json b/testdata/branch.json index 4ebeb4e..1b84088 100644 --- a/testdata/branch.json +++ b/testdata/branch.json @@ -1,112 +1,113 @@ { - "name": "dev", - "target": { - "type": "commit", - "hash": "3c7d72f40aaaab534ac99826de511023b635878f", - "date": "2023-11-06T14:53:55+00:00", - "author": { - "type": "author", - "raw": "Gildas Cherruel ", - "user": { - "display_name": "Gildas Cherruel", - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/users/%7B4115785a-a1b9-4887-9af2-ca401bc7f168%7D" - }, - "avatar": { - "href": "https://secure.gravatar.com/avatar/30dbb120d3b75aaa0cec44e826c85cd7?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FGC-0.png" - }, - "html": { - "href": "https://bitbucket.org/%7B4115785a-a1b9-4887-9af2-ca401bc7f168%7D/" - } - }, - "type": "user", - "uuid": "{4115785a-a1b9-4887-9af2-ca401bc7f168}", - "account_id": "557058:ec46c4b2-f245-496e-8c11-1404869e3346", - "nickname": "gildas_cherruel" - } - }, - "message": "Merge tag 'v1.0.0' into dev\n\nRelease 1.0.0\n", - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/3c7d72f40aaaab534ac99826de511023b635878f" - }, - "html": { - "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox/commits/3c7d72f40aaaab534ac99826de511023b635878f" - }, - "diff": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/diff/3c7d72f40aaaab534ac99826de511023b635878f" - }, - "approve": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/3c7d72f40aaaab534ac99826de511023b635878f/approve" - }, - "comments": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/3c7d72f40aaaab534ac99826de511023b635878f/comments" - }, - "statuses": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/3c7d72f40aaaab534ac99826de511023b635878f/statuses" - } - }, - "parents": [ - { - "hash": "01e505e9c1e5112b0c6b7ba7eda42f81b6bccc04", - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/01e505e9c1e5112b0c6b7ba7eda42f81b6bccc04" - }, - "html": { - "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox/commits/01e505e9c1e5112b0c6b7ba7eda42f81b6bccc04" - } - }, - "type": "commit" - }, - { - "hash": "171be0dd02b1fded551afeea11c9493c542e2534", - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/171be0dd02b1fded551afeea11c9493c542e2534" - }, - "html": { - "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox/commits/171be0dd02b1fded551afeea11c9493c542e2534" - } - }, - "type": "commit" - } - ], - "repository": { - "type": "repository", - "full_name": "gildas_cherruel/gitflow-pr-sandbox", - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox" - }, - "html": { - "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox" - }, - "avatar": { - "href": "https://bytebucket.org/ravatar/%7Bf12769c1-7465-4a94-b265-8b1b04e07cb5%7D?ts=go" - } - }, - "name": "gitflow-pr-sandbox", - "uuid": "{f12769c1-7465-4a94-b265-8b1b04e07cb5}" - } - }, - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/refs/branches/dev" - }, - "commits": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commits/dev" - }, - "html": { - "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox/branch/dev" - } - }, - "type": "branch", - "merge_strategies": [ - "merge_commit", - "squash", - "fast_forward" - ], - "default_merge_strategy": "merge_commit" + "name": "dev", + "target": { + "type": "commit", + "hash": "3c7d72f40aaaab534ac99826de511023b635878f", + "date": "2023-11-06T14:53:55+00:00", + "author": { + "type": "author", + "raw": "Gildas Cherruel ", + "user": { + "display_name": "Gildas Cherruel", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7B4115785a-a1b9-4887-9af2-ca401bc7f168%7D" + }, + "avatar": { + "href": "https://secure.gravatar.com/avatar/30dbb120d3b75aaa0cec44e826c85cd7?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FGC-0.png" + }, + "html": { + "href": "https://bitbucket.org/%7B4115785a-a1b9-4887-9af2-ca401bc7f168%7D/" + } + }, + "type": "user", + "uuid": "{4115785a-a1b9-4887-9af2-ca401bc7f168}", + "account_id": "557058:ec46c4b2-f245-496e-8c11-1404869e3346", + "nickname": "gildas_cherruel" + } + }, + "message": "Merge tag 'v1.0.0' into dev\n\nRelease 1.0.0\n", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/3c7d72f40aaaab534ac99826de511023b635878f" + }, + "html": { + "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox/commits/3c7d72f40aaaab534ac99826de511023b635878f" + }, + "diff": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/diff/3c7d72f40aaaab534ac99826de511023b635878f" + }, + "approve": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/3c7d72f40aaaab534ac99826de511023b635878f/approve" + }, + "comments": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/3c7d72f40aaaab534ac99826de511023b635878f/comments" + }, + "statuses": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/3c7d72f40aaaab534ac99826de511023b635878f/statuses" + } + }, + "parents": [ + { + "hash": "01e505e9c1e5112b0c6b7ba7eda42f81b6bccc04", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/01e505e9c1e5112b0c6b7ba7eda42f81b6bccc04" + }, + "html": { + "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox/commits/01e505e9c1e5112b0c6b7ba7eda42f81b6bccc04" + } + }, + "type": "commit" + }, + { + "hash": "171be0dd02b1fded551afeea11c9493c542e2534", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/171be0dd02b1fded551afeea11c9493c542e2534" + }, + "html": { + "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox/commits/171be0dd02b1fded551afeea11c9493c542e2534" + } + }, + "type": "commit" + } + ], + "repository": { + "type": "repository", + "full_name": "gildas_cherruel/gitflow-pr-sandbox", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox" + }, + "html": { + "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox" + }, + "avatar": { + "href": "https://bytebucket.org/ravatar/%7Bf12769c1-7465-4a94-b265-8b1b04e07cb5%7D?ts=go" + } + }, + "name": "gitflow-pr-sandbox", + "uuid": "{f12769c1-7465-4a94-b265-8b1b04e07cb5}", + "slug": "gitflow-pr-sandbox" + } + }, + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/refs/branches/dev" + }, + "commits": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commits/dev" + }, + "html": { + "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox/branch/dev" + } + }, + "type": "branch", + "merge_strategies": [ + "merge_commit", + "squash", + "fast_forward" + ], + "default_merge_strategy": "merge_commit" } diff --git a/testdata/commit.json b/testdata/commit.json index f821bfb..be69054 100644 --- a/testdata/commit.json +++ b/testdata/commit.json @@ -1,96 +1,97 @@ { - "type": "commit", - "hash": "026560720168aa12820a01e8262f6bb60f0639d1", - "date": "2023-11-27T14:43:10+00:00", - "author": { - "type": "author", - "raw": "Gildas Cherruel ", - "user": { - "display_name": "Gildas Cherruel", - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/users/%7B4115785a-a1b9-4887-9af2-ca401bc7f168%7D" - }, - "avatar": { - "href": "https://secure.gravatar.com/avatar/30dbb120d3b75aaa0cec44e826c85cd7?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FGC-0.png" - }, - "html": { - "href": "https://bitbucket.org/%7B4115785a-a1b9-4887-9af2-ca401bc7f168%7D/" - } - }, - "type": "user", - "uuid": "{4115785a-a1b9-4887-9af2-ca401bc7f168}", - "account_id": "557058:ec46c4b2-f245-496e-8c11-1404869e3346", - "nickname": "gildas_cherruel" - } - }, - "message": "Added BitBucket's CLI requirement\n", - "summary": { - "type": "rendered", - "raw": "Added BitBucket's CLI requirement\n", - "markup": "markdown", - "html": "

Added BitBucket's CLI requirement

" - }, - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/026560720168aa12820a01e8262f6bb60f0639d1" - }, - "html": { - "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox/commits/026560720168aa12820a01e8262f6bb60f0639d1" - }, - "diff": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/diff/026560720168aa12820a01e8262f6bb60f0639d1" - }, - "approve": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/026560720168aa12820a01e8262f6bb60f0639d1/approve" - }, - "comments": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/026560720168aa12820a01e8262f6bb60f0639d1/comments" - }, - "statuses": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/026560720168aa12820a01e8262f6bb60f0639d1/statuses" - }, - "patch": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/patch/026560720168aa12820a01e8262f6bb60f0639d1" - } - }, - "parents": [ - { - "hash": "3c7d72f40aaaab534ac99826de511023b635878f", - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/3c7d72f40aaaab534ac99826de511023b635878f" - }, - "html": { - "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox/commits/3c7d72f40aaaab534ac99826de511023b635878f" - } - }, - "type": "commit" - } - ], - "rendered": { - "message": { - "type": "rendered", - "raw": "Added BitBucket's CLI requirement\n", - "markup": "markdown", - "html": "

Added BitBucket's CLI requirement

" - } - }, - "repository": { - "type": "repository", - "full_name": "gildas_cherruel/gitflow-pr-sandbox", - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox" - }, - "html": { - "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox" - }, - "avatar": { - "href": "https://bytebucket.org/ravatar/%7Bf12769c1-7465-4a94-b265-8b1b04e07cb5%7D?ts=go" - } - }, - "name": "gitflow-pr-sandbox", - "uuid": "{f12769c1-7465-4a94-b265-8b1b04e07cb5}" - } + "type": "commit", + "hash": "026560720168aa12820a01e8262f6bb60f0639d1", + "date": "2023-11-27T14:43:10+00:00", + "author": { + "type": "author", + "raw": "Gildas Cherruel ", + "user": { + "display_name": "Gildas Cherruel", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7B4115785a-a1b9-4887-9af2-ca401bc7f168%7D" + }, + "avatar": { + "href": "https://secure.gravatar.com/avatar/30dbb120d3b75aaa0cec44e826c85cd7?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FGC-0.png" + }, + "html": { + "href": "https://bitbucket.org/%7B4115785a-a1b9-4887-9af2-ca401bc7f168%7D/" + } + }, + "type": "user", + "uuid": "{4115785a-a1b9-4887-9af2-ca401bc7f168}", + "account_id": "557058:ec46c4b2-f245-496e-8c11-1404869e3346", + "nickname": "gildas_cherruel" + } + }, + "message": "Added BitBucket's CLI requirement\n", + "summary": { + "type": "rendered", + "raw": "Added BitBucket's CLI requirement\n", + "markup": "markdown", + "html": "

Added BitBucket's CLI requirement

" + }, + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/026560720168aa12820a01e8262f6bb60f0639d1" + }, + "html": { + "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox/commits/026560720168aa12820a01e8262f6bb60f0639d1" + }, + "diff": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/diff/026560720168aa12820a01e8262f6bb60f0639d1" + }, + "approve": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/026560720168aa12820a01e8262f6bb60f0639d1/approve" + }, + "comments": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/026560720168aa12820a01e8262f6bb60f0639d1/comments" + }, + "statuses": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/026560720168aa12820a01e8262f6bb60f0639d1/statuses" + }, + "patch": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/patch/026560720168aa12820a01e8262f6bb60f0639d1" + } + }, + "parents": [ + { + "hash": "3c7d72f40aaaab534ac99826de511023b635878f", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/3c7d72f40aaaab534ac99826de511023b635878f" + }, + "html": { + "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox/commits/3c7d72f40aaaab534ac99826de511023b635878f" + } + }, + "type": "commit" + } + ], + "rendered": { + "message": { + "type": "rendered", + "raw": "Added BitBucket's CLI requirement\n", + "markup": "markdown", + "html": "

Added BitBucket's CLI requirement

" + } + }, + "repository": { + "type": "repository", + "full_name": "gildas_cherruel/gitflow-pr-sandbox", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox" + }, + "html": { + "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox" + }, + "avatar": { + "href": "https://bytebucket.org/ravatar/%7Bf12769c1-7465-4a94-b265-8b1b04e07cb5%7D?ts=go" + } + }, + "name": "gitflow-pr-sandbox", + "uuid": "{f12769c1-7465-4a94-b265-8b1b04e07cb5}", + "slug": "gitflow-pr-sandbox" + } } diff --git a/testdata/pullrequest.json b/testdata/pullrequest.json index de33b06..e39f425 100644 --- a/testdata/pullrequest.json +++ b/testdata/pullrequest.json @@ -1,169 +1,169 @@ - { - "comment_count": 0, - "task_count": 0, - "type": "pullrequest", - "id": 2, - "title": "Merge feature/links", - "description": "Feature links. Do not delete the feature branch after the merge.", - "state": "MERGED", - "merge_commit": { - "type": "commit", - "hash": "5663ada8560f", - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/5663ada8560f" - }, - "html": { - "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox/commits/5663ada8560f" - } - } - }, - "close_source_branch": false, - "closed_by": { - "display_name": "Gildas Cherruel", - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/users/%7B4115785a-a1b9-4887-9af2-ca401bc7f168%7D" - }, - "avatar": { - "href": "https://secure.gravatar.com/avatar/30dbb120d3b75aaa0cec44e826c85cd7?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FGC-0.png" - }, - "html": { - "href": "https://bitbucket.org/%7B4115785a-a1b9-4887-9af2-ca401bc7f168%7D/" - } - }, - "type": "user", - "uuid": "{4115785a-a1b9-4887-9af2-ca401bc7f168}", - "account_id": "557058:ec46c4b2-f245-496e-8c11-1404869e3346", - "nickname": "gildas_cherruel" - }, - "author": { - "type": "app_user", - "links": { - "avatar": { - "href": "https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/712020:9b210b18-797d-404e-a6a8-31f00569af8b/3076be80-e7af-4b62-a4b8-ed8fe3c3332e/128" - } - }, - "created_on": "2023-10-31T08:58:32.098157+00:00", - "display_name": "gitflow", - "uuid": "{101398a7-7609-4eb5-ae20-92014abfbeb6}", - "account_id": "712020:9b210b18-797d-404e-a6a8-31f00569af8b", - "account_status": "active", - "kind": "repository_access_token" - }, - "reason": "", - "created_on": "2023-10-31T11:25:52.591232+00:00", - "updated_on": "2023-10-31T12:24:15.443103+00:00", - "destination": { - "branch": { - "name": "dev" - }, - "commit": { - "type": "commit", - "hash": "67a6aa42721a", - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/67a6aa42721a" - }, - "html": { - "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox/commits/67a6aa42721a" - } - } - }, - "repository": { - "type": "repository", - "uuid": "{f12769c1-7465-4a94-b265-8b1b04e07cb5}", - "full_name": "gildas_cherruel/gitflow-pr-sandbox", - "name": "gitflow-pr-sandbox", - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox" - }, - "html": { - "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox" - }, - "avatar": { - "href": "https://bytebucket.org/ravatar/%7Bf12769c1-7465-4a94-b265-8b1b04e07cb5%7D?ts=go" - } - } - } - }, - "source": { - "branch": { - "name": "feature/links" - }, - "commit": { - "type": "commit", - "hash": "2ae150fc05f8", - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/2ae150fc05f8" - }, - "html": { - "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox/commits/2ae150fc05f8" - } - } - }, - "repository": { - "type": "repository", - "full_name": "gildas_cherruel/gitflow-pr-sandbox", - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox" - }, - "html": { - "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox" - }, - "avatar": { - "href": "https://bytebucket.org/ravatar/%7Bf12769c1-7465-4a94-b265-8b1b04e07cb5%7D?ts=go" - } - }, - "name": "gitflow-pr-sandbox", - "uuid": "{f12769c1-7465-4a94-b265-8b1b04e07cb5}" - } - }, - "links": { - "self": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/pullrequests/2" - }, - "html": { - "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox/pull-requests/2" - }, - "commits": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/pullrequests/2/commits" - }, - "approve": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/pullrequests/2/approve" - }, - "request-changes": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/pullrequests/2/request-changes" - }, - "diff": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/diff/gildas_cherruel/gitflow-pr-sandbox:2ae150fc05f8%0D67a6aa42721a?from_pullrequest_id=2&topic=true" - }, - "diffstat": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/diffstat/gildas_cherruel/gitflow-pr-sandbox:2ae150fc05f8%0D67a6aa42721a?from_pullrequest_id=2&topic=true" - }, - "comments": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/pullrequests/2/comments" - }, - "activity": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/pullrequests/2/activity" - }, - "merge": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/pullrequests/2/merge" - }, - "decline": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/pullrequests/2/decline" - }, - "statuses": { - "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/pullrequests/2/statuses" - } - }, - "summary": { - "type": "rendered", - "raw": "Feature links. Do not delete the feature branch after the merge.", - "markup": "markdown", - "html": "

Feature links. Do not delete the feature branch after the merge.

" - } - } \ No newline at end of file +{ + "comment_count": 0, + "task_count": 0, + "type": "pullrequest", + "id": 2, + "title": "Merge feature/links", + "description": "Feature links. Do not delete the feature branch after the merge.", + "state": "MERGED", + "merge_commit": { + "type": "commit", + "hash": "5663ada8560f", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/5663ada8560f" + }, + "html": { + "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox/commits/5663ada8560f" + } + } + }, + "close_source_branch": false, + "closed_by": { + "display_name": "Gildas Cherruel", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7B4115785a-a1b9-4887-9af2-ca401bc7f168%7D" + }, + "avatar": { + "href": "https://secure.gravatar.com/avatar/30dbb120d3b75aaa0cec44e826c85cd7?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FGC-0.png" + }, + "html": { + "href": "https://bitbucket.org/%7B4115785a-a1b9-4887-9af2-ca401bc7f168%7D/" + } + }, + "type": "user", + "uuid": "{4115785a-a1b9-4887-9af2-ca401bc7f168}", + "account_id": "557058:ec46c4b2-f245-496e-8c11-1404869e3346", + "nickname": "gildas_cherruel" + }, + "author": { + "type": "app_user", + "links": { + "avatar": { + "href": "https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/712020:9b210b18-797d-404e-a6a8-31f00569af8b/3076be80-e7af-4b62-a4b8-ed8fe3c3332e/128" + } + }, + "created_on": "2023-10-31T08:58:32.098157+00:00", + "display_name": "gitflow", + "uuid": "{101398a7-7609-4eb5-ae20-92014abfbeb6}", + "account_id": "712020:9b210b18-797d-404e-a6a8-31f00569af8b", + "account_status": "active", + "kind": "repository_access_token" + }, + "reason": "", + "created_on": "2023-10-31T11:25:52.591232+00:00", + "updated_on": "2023-10-31T12:24:15.443103+00:00", + "destination": { + "branch": { + "name": "dev" + }, + "commit": { + "type": "commit", + "hash": "67a6aa42721a", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/67a6aa42721a" + }, + "html": { + "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox/commits/67a6aa42721a" + } + } + }, + "repository": { + "type": "repository", + "uuid": "{f12769c1-7465-4a94-b265-8b1b04e07cb5}", + "full_name": "gildas_cherruel/gitflow-pr-sandbox", + "name": "gitflow-pr-sandbox", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox" + }, + "html": { + "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox" + }, + "avatar": { + "href": "https://bytebucket.org/ravatar/%7Bf12769c1-7465-4a94-b265-8b1b04e07cb5%7D?ts=go" + } + } + } + }, + "source": { + "branch": { + "name": "feature/links" + }, + "commit": { + "type": "commit", + "hash": "2ae150fc05f8", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/commit/2ae150fc05f8" + }, + "html": { + "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox/commits/2ae150fc05f8" + } + } + }, + "repository": { + "type": "repository", + "full_name": "gildas_cherruel/gitflow-pr-sandbox", + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox" + }, + "html": { + "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox" + }, + "avatar": { + "href": "https://bytebucket.org/ravatar/%7Bf12769c1-7465-4a94-b265-8b1b04e07cb5%7D?ts=go" + } + }, + "name": "gitflow-pr-sandbox", + "uuid": "{f12769c1-7465-4a94-b265-8b1b04e07cb5}" + } + }, + "links": { + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/pullrequests/2" + }, + "html": { + "href": "https://bitbucket.org/gildas_cherruel/gitflow-pr-sandbox/pull-requests/2" + }, + "commits": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/pullrequests/2/commits" + }, + "approve": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/pullrequests/2/approve" + }, + "request-changes": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/pullrequests/2/request-changes" + }, + "diff": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/diff/gildas_cherruel/gitflow-pr-sandbox:2ae150fc05f8%0D67a6aa42721a?from_pullrequest_id=2&topic=true" + }, + "diffstat": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/diffstat/gildas_cherruel/gitflow-pr-sandbox:2ae150fc05f8%0D67a6aa42721a?from_pullrequest_id=2&topic=true" + }, + "comments": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/pullrequests/2/comments" + }, + "activity": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/pullrequests/2/activity" + }, + "merge": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/pullrequests/2/merge" + }, + "decline": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/pullrequests/2/decline" + }, + "statuses": { + "href": "https://api.bitbucket.org/2.0/repositories/gildas_cherruel/gitflow-pr-sandbox/pullrequests/2/statuses" + } + }, + "summary": { + "type": "rendered", + "raw": "Feature links. Do not delete the feature branch after the merge.", + "markup": "markdown", + "html": "

Feature links. Do not delete the feature branch after the merge.

" + } +} From e003c133cb55e1bdb84764872825f5117f615d25 Mon Sep 17 00:00:00 2001 From: Brian Summa Date: Sat, 6 Jun 2026 14:07:57 -0500 Subject: [PATCH 3/5] Sync develop-pr with develop: upstream deps, tools pinning, code fixes Bring develop-pr fully in sync with develop (excluding planning files: CLAUDE.md, .codegraph/, docs/bitbucket-swagger.v3.json). Changes applied from develop: - go.mod/go.sum: update go-flags v0.5.0, go-logger v1.9.5, go-git v5.19.1, and other upstream dep bumps; add gocov/gocov-xml as direct requires - tools/tools.go: pin gocov/gocov-xml via build-tag tools file - cmd/repository/repository.go: add omitempty to HasIssues/HasWiki/IsPrivate - Makefile: exclude tools/ from GOFILES, fix COVERAGE_OUT path, fix coverage-report to use file prereq, add coverage-tools target - cmd/profile, cmd/pullrequest, cmd/pipeline, cmd/issue, cmd/root, etc.: update callers to go-flags v0.5.0 NewEnumFlagWithFunc API (cmd arg added) - version.go, packaging/: version and packaging metadata from master rebase - cmd/common/config.go: new file from upstream - cmd/pullrequest/comment/create_test.go: new test from upstream Co-Authored-By: Claude Sonnet 4.6 --- Makefile | 10 +- cmd/artifact/list.go | 10 +- cmd/commit/ancestor.go | 4 - cmd/commit/diff.go | 4 - cmd/commit/patch.go | 4 - cmd/common/config.go | 64 +++++++++ cmd/component/list.go | 10 +- cmd/issue/attachment/attachment.go | 15 ++- cmd/issue/attachment/delete.go | 2 +- cmd/issue/attachment/download.go | 2 +- cmd/issue/attachment/list.go | 12 +- cmd/issue/attachment/upload.go | 2 +- cmd/issue/comment/create.go | 2 +- cmd/issue/comment/delete.go | 5 +- cmd/issue/comment/get.go | 7 +- cmd/issue/comment/list.go | 12 +- cmd/issue/comment/update.go | 7 +- cmd/issue/list.go | 20 +-- cmd/pipeline/common/getters.go | 8 +- cmd/pipeline/get.go | 4 - cmd/pipeline/list.go | 5 - cmd/pipeline/step/cases.go | 6 +- cmd/pipeline/step/get.go | 6 +- cmd/pipeline/step/list.go | 9 +- cmd/pipeline/step/logs.go | 6 +- cmd/pipeline/step/report.go | 6 +- cmd/pipeline/step/step.go | 11 +- cmd/pipeline/trigger.go | 6 +- cmd/profile/authorize.go | 4 + cmd/profile/create.go | 8 +- cmd/profile/delete.go | 4 + cmd/profile/get.go | 4 + cmd/profile/list.go | 4 + cmd/profile/profile.go | 13 +- cmd/profile/profiles.go | 24 +++- cmd/profile/update.go | 8 +- cmd/profile/use.go | 4 + cmd/project/reviewer/add.go | 2 +- cmd/project/reviewer/delete.go | 2 +- cmd/project/reviewer/get.go | 4 +- cmd/project/reviewer/list.go | 2 +- cmd/pullrequest/activities.go | 4 - cmd/pullrequest/activity/list.go | 9 +- cmd/pullrequest/approve.go | 4 - cmd/pullrequest/comment/create.go | 16 ++- cmd/pullrequest/comment/create_test.go | 25 ++++ cmd/pullrequest/comment/delete.go | 4 +- cmd/pullrequest/comment/get.go | 2 +- cmd/pullrequest/comment/list.go | 9 +- cmd/pullrequest/comment/reopen.go | 4 +- cmd/pullrequest/comment/resolve.go | 4 +- cmd/pullrequest/comment/update.go | 17 ++- cmd/pullrequest/commits.go | 4 - cmd/pullrequest/create.go | 6 +- cmd/pullrequest/decline.go | 4 - cmd/pullrequest/diff.go | 4 - cmd/pullrequest/get.go | 4 - cmd/pullrequest/merge.go | 4 - cmd/pullrequest/merge_status.go | 4 - cmd/pullrequest/patch.go | 4 - cmd/pullrequest/remove-request-changes.go | 4 - cmd/pullrequest/request-changes.go | 4 - cmd/pullrequest/task/create.go | 4 +- cmd/pullrequest/task/delete.go | 2 +- cmd/pullrequest/task/get.go | 2 +- cmd/pullrequest/task/list.go | 24 ++-- cmd/pullrequest/task/update.go | 8 +- cmd/pullrequest/unapprove.go | 4 - cmd/pullrequest/update.go | 10 +- cmd/repository/create.go | 2 +- cmd/repository/fork.go | 2 +- cmd/repository/repository.go | 6 +- cmd/repository/update.go | 4 +- cmd/root.go | 75 +---------- cmd/tag/create.go | 2 +- cmd/tag/list.go | 4 +- cmd/workspace/permission/list.go | 11 +- go.mod | 52 ++++---- go.sum | 123 ++++++++++-------- packaging/chocolatey/bitbucket-cli.nuspec | 5 +- packaging/chocolatey/tools/VERIFICATION.txt | 4 +- .../chocolatey/tools/chocolateyinstall.ps1 | 4 +- packaging/package-description.xml | 1 + packaging/snap/snapcraft.yaml | 2 +- tools/tools.go | 12 ++ version.go | 2 +- 86 files changed, 429 insertions(+), 418 deletions(-) create mode 100644 cmd/common/config.go create mode 100644 tools/tools.go diff --git a/Makefile b/Makefile index b26ef02..a3d3567 100644 --- a/Makefile +++ b/Makefile @@ -36,13 +36,13 @@ export PACKAGE PROJECT VERSION BRANCH COMMIT BUILD REVISION # Files GOTESTS := $(call rwildcard,,*_test.go) -GOFILES := $(filter-out $(GOTESTS), $(call rwildcard,,*.go)) +GOFILES := $(filter-out $(GOTESTS) $(call rwildcard,tools/,*.go), $(call rwildcard,,*.go)) ASSETS := # Testing TEST_TIMEOUT ?= 30 COVERAGE_MODE ?= count -COVERAGE_OUT := $(TMP_DIR)/coverage.out +COVERAGE_OUT := $(COV_DIR)/coverage.out COVERAGE_HTML := $(COV_DIR)/index.html COVERAGE_XML := $(COV_DIR)/coverage.xml @@ -204,7 +204,7 @@ test tests: ; $(info $(M) Running $(NAME:%=% )tests...) @ ## Run the Unit Tests $(GOCOV) convert $(COVERAGE_OUT) | $(GOCOVXML) > $(COVERAGE_XML); \ fi -coverage-report: test | coverage-tools; @ ## Generate XML coverage report (requires gocov/gocov-xml) +coverage-report: $(COVERAGE_OUT) | coverage-tools; @ ## Generate XML coverage report (requires gocov/gocov-xml) $Q $(GOCOV) convert $(COVERAGE_OUT) | $(GOCOVXML) > $(COVERAGE_XML) test-ci:; @ ## Run the unit tests continuously @@ -383,8 +383,7 @@ $(BIN_DIR)/pi/$(PROJECT): $(GOFILES) $(ASSETS) | $(BIN_DIR)/pi; $(info $(M) buil $Q $(GO) build $(if $V,-v) $(LDFLAGS) -o $@ . # Watch recipes -watch: watch-tools | $(TMP_DIR); @ ## Run a command continuously: make watch run="go test" - @#$Q LOG=* $(YOLO) -i '*.go' -e vendor -e $(BIN_DIR) -e $(LOG_DIR) -e $(TMP_DIR) -c "$(run)" +watch: $(TMP_DIR); @ ## Run a command continuously: make watch run="go test" $Q nodemon \ --verbose \ --delay 5 \ @@ -405,6 +404,7 @@ $(BIN_DIR)/nfpm: PACKAGE=github.com/goreleaser/nfpm/v2/cmd/nfpm@latest $(BIN_DIR)/gomplate: PACKAGE=github.com/hairyhenderson/gomplate/v4/cmd/gomplate@latest watch-tools: | $(YOLO) +coverage-tools: | $(GOCOV) $(GOCOVXML) $(BIN_DIR)/%: | $(BIN_DIR) ; $(info $(M) installing $(PACKAGE)...) $Q env GOBIN=$(BIN_DIR) $(GO) install $(PACKAGE) diff --git a/cmd/artifact/list.go b/cmd/artifact/list.go index 79cb224..dff2c54 100644 --- a/cmd/artifact/list.go +++ b/cmd/artifact/list.go @@ -42,6 +42,11 @@ func init() { func listProcess(cmd *cobra.Command, args []string) (err error) { log := logger.Must(logger.FromContext(cmd.Context())).Child(cmd.Parent().Name(), "list") + log.Infof("Listing all artifacts") + if !common.WhatIf(log.ToContext(cmd.Context()), cmd, "Showing artifacts") { + return nil + } + repository, err := repository.GetRepository(cmd.Context(), cmd) if err != nil { return err @@ -52,11 +57,6 @@ func listProcess(cmd *cobra.Command, args []string) (err error) { uripath += "?q=" + url.QueryEscape(listOptions.Query) } - log.Infof("Listing all artifacts") - if !common.WhatIf(log.ToContext(cmd.Context()), cmd, "Showing artifacts") { - return nil - } - artifacts, err := profile.GetAll[Artifact](cmd.Context(), cmd, uripath) if err != nil { return err diff --git a/cmd/commit/ancestor.go b/cmd/commit/ancestor.go index 35f1f70..54d6211 100644 --- a/cmd/commit/ancestor.go +++ b/cmd/commit/ancestor.go @@ -29,10 +29,6 @@ func validAncestorArgs(cmd *cobra.Command, args []string, toComplete string) ([] return nil, cobra.ShellCompDirectiveNoFileComp } - if profile.Current == nil { - return []string{}, cobra.ShellCompDirectiveNoFileComp - } - log.Debugf("Getting commit hashes for completion with args: %v and toComplete: %s", args, toComplete) names, err := GetCommitHashes(cmd.Context(), cmd, args, toComplete) if err != nil { diff --git a/cmd/commit/diff.go b/cmd/commit/diff.go index 6d9cdae..4d6c5fe 100644 --- a/cmd/commit/diff.go +++ b/cmd/commit/diff.go @@ -37,10 +37,6 @@ func validDiffArgs(cmd *cobra.Command, args []string, toComplete string) ([]stri return nil, cobra.ShellCompDirectiveNoFileComp } - if profile.Current == nil { - return []string{}, cobra.ShellCompDirectiveNoFileComp - } - log.Debugf("Getting commit hashes for completion with args: %v and toComplete: %s", args, toComplete) names, err := GetCommitHashes(cmd.Context(), cmd, args, toComplete) if err != nil { diff --git a/cmd/commit/patch.go b/cmd/commit/patch.go index 8664fda..15b45ba 100644 --- a/cmd/commit/patch.go +++ b/cmd/commit/patch.go @@ -31,10 +31,6 @@ func validPatchArgs(cmd *cobra.Command, args []string, toComplete string) ([]str return nil, cobra.ShellCompDirectiveNoFileComp } - if profile.Current == nil { - return []string{}, cobra.ShellCompDirectiveNoFileComp - } - log.Debugf("Getting commit hashes for completion with args: %v and toComplete: %s", args, toComplete) names, err := GetCommitHashes(cmd.Context(), cmd, args, toComplete) if err != nil { diff --git a/cmd/common/config.go b/cmd/common/config.go new file mode 100644 index 0000000..0547ec3 --- /dev/null +++ b/cmd/common/config.go @@ -0,0 +1,64 @@ +package common + +import ( + "errors" + "os" + "path/filepath" + "runtime" + "strings" + + "github.com/gildas/go-logger" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// Initialize configure the logger and load the Viper Configuration +func Initialize(cmd *cobra.Command) (err error) { + initializeLogger(cmd) + return initializeConfiguration(cmd) +} + +// initializeLogger configures the logger based on the command line flags and environment variables +func initializeLogger(cmd *cobra.Command) { + log := logger.Must(logger.FromContext(cmd.Context())) + + // Use persistent flags instead + if cmd.Root().PersistentFlags().Changed("log") { + log.ResetDestinations(cmd.Root().PersistentFlags().Lookup("log").Value.String()) + } + if cmd.Root().PersistentFlags().Changed("debug") && cmd.Root().PersistentFlags().Lookup("debug").Value.String() == "true" { + log.SetFilterLevel(logger.DEBUG) + } + + log.Infof("%s", strings.Repeat("-", 80)) + log.Infof("Starting %s v%s (%s)", cmd.Root().Name(), cmd.Root().Version, runtime.GOARCH) + log.Infof("Log Destination: %s", log) +} + +// initializeConfiguration loads the configuration file and profiles +func initializeConfiguration(cmd *cobra.Command) (err error) { + log := logger.Must(logger.FromContext(cmd.Context())) + + viper.SetConfigType("yaml") + if cmd.Root().PersistentFlags().Changed("config") { + viper.SetConfigFile(cmd.Root().PersistentFlags().Lookup("config").Value.String()) + } else if configDir, _ := os.UserConfigDir(); len(configDir) > 0 { + viper.AddConfigPath(filepath.Join(configDir, "bitbucket")) + viper.SetConfigName("config-cli.yml") + } else { + homeDir, err := os.UserHomeDir() + cobra.CheckErr(err) + viper.AddConfigPath(homeDir) + viper.SetConfigName(".bitbucket-cli") + } + + err = viper.ReadInConfig() + if verr, ok := err.(viper.ConfigFileNotFoundError); ok { + log.Warnf("Config file not found: %s", verr) + } else if err != nil { + return errors.Join(errors.New("Failed to read config file"), err) + } else { + log.Infof("Config File: %s", viper.ConfigFileUsed()) + } + return nil +} diff --git a/cmd/component/list.go b/cmd/component/list.go index 7caa295..d2f45cf 100644 --- a/cmd/component/list.go +++ b/cmd/component/list.go @@ -43,6 +43,11 @@ func init() { func listProcess(cmd *cobra.Command, args []string) (err error) { log := logger.Must(logger.FromContext(cmd.Context())).Child(cmd.Parent().Name(), "list") + log.Infof("Listing all components") + if !common.WhatIf(log.ToContext(cmd.Context()), cmd, "Showing components") { + return nil + } + repository, err := repository.GetRepository(cmd.Context(), cmd) if err != nil { return err @@ -53,11 +58,6 @@ func listProcess(cmd *cobra.Command, args []string) (err error) { uripath = fmt.Sprintf("%s?q=%s", uripath, url.QueryEscape(listOptions.Query)) } - log.Infof("Listing all components") - if !common.WhatIf(log.ToContext(cmd.Context()), cmd, "Showing components") { - return nil - } - components, err := profile.GetAll[Component](cmd.Context(), cmd, uripath) if err != nil { return err diff --git a/cmd/issue/attachment/attachment.go b/cmd/issue/attachment/attachment.go index 6db8d6e..8ba135c 100644 --- a/cmd/issue/attachment/attachment.go +++ b/cmd/issue/attachment/attachment.go @@ -7,6 +7,7 @@ import ( "github.com/gildas/bitbucket-cli/cmd/common" "github.com/gildas/bitbucket-cli/cmd/profile" + "github.com/gildas/bitbucket-cli/cmd/repository" "github.com/gildas/go-core" "github.com/gildas/go-errors" "github.com/gildas/go-logger" @@ -96,8 +97,13 @@ func GetIssueIDs(context context.Context, cmd *cobra.Command, args []string, toC ID int `json:"id" mapstructure:"id"` } + repository, err := repository.GetRepository(cmd.Context(), cmd) + if err != nil { + return []string{}, err + } + log.Infof("Getting all issues") - issues, err := profile.GetAll[Issue](context, cmd, "issues") + issues, err := profile.GetAll[Issue](context, cmd, repository.GetPath("issues")) if err != nil { log.Errorf("Failed to get issues", err) return []string{}, err @@ -111,8 +117,13 @@ func GetIssueIDs(context context.Context, cmd *cobra.Command, args []string, toC func GetAttachmentNames(context context.Context, cmd *cobra.Command, issueID string) (names []string, err error) { log := logger.Must(logger.FromContext(context)).Child("issue", "getids") + repository, err := repository.GetRepository(cmd.Context(), cmd) + if err != nil { + return []string{}, err + } + log.Infof("Getting all attachments") - attachments, err := profile.GetAll[Attachment](context, cmd, fmt.Sprintf("issues/%s/attachments", issueID)) + attachments, err := profile.GetAll[Attachment](context, cmd, repository.GetPath("issues", issueID, "attachments")) if err != nil { log.Errorf("Failed to get attachments", err) return []string{}, err diff --git a/cmd/issue/attachment/delete.go b/cmd/issue/attachment/delete.go index cbb4c08..bf85a79 100644 --- a/cmd/issue/attachment/delete.go +++ b/cmd/issue/attachment/delete.go @@ -29,7 +29,7 @@ var deleteOptions struct { func init() { Command.AddCommand(deleteCmd) - deleteOptions.IssueID = flags.NewEnumFlagWithFunc("", GetIssueIDs) + deleteOptions.IssueID = flags.NewEnumFlagWithFunc(deleteCmd, "", GetIssueIDs) deleteCmd.Flags().Var(deleteOptions.IssueID, "issue", "Issue to delete attachments from") _ = deleteCmd.MarkFlagRequired("issue") _ = deleteCmd.RegisterFlagCompletionFunc(deleteOptions.IssueID.CompletionFunc("issue")) diff --git a/cmd/issue/attachment/download.go b/cmd/issue/attachment/download.go index 6771da0..3478eab 100644 --- a/cmd/issue/attachment/download.go +++ b/cmd/issue/attachment/download.go @@ -30,7 +30,7 @@ var downloadOptions struct { func init() { Command.AddCommand(downloadCmd) - downloadOptions.IssueID = flags.NewEnumFlagWithFunc("", GetIssueIDs) + downloadOptions.IssueID = flags.NewEnumFlagWithFunc(downloadCmd, "", GetIssueIDs) downloadCmd.Flags().Var(downloadOptions.IssueID, "issue", "Issue to get attachments from") downloadCmd.Flags().StringVar(&downloadOptions.Destination, "destination", "", "Destination folder to download the attachment to. Defaults to the current folder") downloadCmd.Flags().BoolVar(&downloadOptions.Progress, "progress", false, "Show progress") diff --git a/cmd/issue/attachment/list.go b/cmd/issue/attachment/list.go index 3b163c9..29c6bc0 100644 --- a/cmd/issue/attachment/list.go +++ b/cmd/issue/attachment/list.go @@ -31,7 +31,7 @@ var listOptions struct { func init() { Command.AddCommand(listCmd) - listOptions.IssueID = flags.NewEnumFlagWithFunc("", GetIssueIDs) + listOptions.IssueID = flags.NewEnumFlagWithFunc(listCmd, "", GetIssueIDs) listOptions.Columns = flags.NewEnumSliceFlagWithAllAllowed(columns.Columns()...) listOptions.SortBy = flags.NewEnumFlag(columns.Sorters()...) listCmd.Flags().Var(listOptions.IssueID, "issue", "Issue to list attachments from") @@ -53,16 +53,16 @@ func listProcess(cmd *cobra.Command, args []string) (err error) { return err } - uripath := repository.GetPath("issues", listOptions.IssueID.Value, "attachments") - if len(listOptions.Query) > 0 { - uripath += "?q=" + url.QueryEscape(listOptions.Query) - } - log.Infof("Listing all attachments from repository %s", repository) if !common.WhatIf(log.ToContext(cmd.Context()), cmd, fmt.Sprintf("Showing attachments for issue %s in repository %s", listOptions.IssueID.Value, repository)) { return nil } + uripath := repository.GetPath("issues", listOptions.IssueID.Value, "attachments") + if len(listOptions.Query) > 0 { + uripath += "?q=" + url.QueryEscape(listOptions.Query) + } + attachments, err := profile.GetAll[Attachment](cmd.Context(), cmd, uripath) if err != nil { return err diff --git a/cmd/issue/attachment/upload.go b/cmd/issue/attachment/upload.go index 80a3e56..f4415c5 100644 --- a/cmd/issue/attachment/upload.go +++ b/cmd/issue/attachment/upload.go @@ -28,7 +28,7 @@ var uploadOptions struct { func init() { Command.AddCommand(uploadCmd) - uploadOptions.IssueID = flags.NewEnumFlagWithFunc("", GetIssueIDs) + uploadOptions.IssueID = flags.NewEnumFlagWithFunc(uploadCmd, "", GetIssueIDs) uploadCmd.Flags().Var(uploadOptions.IssueID, "issue", "Issue to upload attachments to") uploadCmd.Flags().BoolVar(&uploadOptions.Progress, "progress", false, "Show progress") _ = uploadCmd.MarkFlagRequired("issue") diff --git a/cmd/issue/comment/create.go b/cmd/issue/comment/create.go index 520efd1..abb4d61 100644 --- a/cmd/issue/comment/create.go +++ b/cmd/issue/comment/create.go @@ -32,7 +32,7 @@ var createOptions struct { func init() { Command.AddCommand(createCmd) - createOptions.IssueID = flags.NewEnumFlagWithFunc("", GetIssueIDs) + createOptions.IssueID = flags.NewEnumFlagWithFunc(createCmd, "", GetIssueIDs) createCmd.Flags().Var(createOptions.IssueID, "issue", "Issue to create comments to") createCmd.Flags().StringVar(&createOptions.Comment, "comment", "", "Comment of the issue") _ = createCmd.MarkFlagRequired("issue") diff --git a/cmd/issue/comment/delete.go b/cmd/issue/comment/delete.go index 1be1821..796b954 100644 --- a/cmd/issue/comment/delete.go +++ b/cmd/issue/comment/delete.go @@ -29,7 +29,7 @@ var deleteOptions struct { func init() { Command.AddCommand(deleteCmd) - deleteOptions.IssueID = flags.NewEnumFlagWithFunc("", GetIssueIDs) + deleteOptions.IssueID = flags.NewEnumFlagWithFunc(deleteCmd, "", GetIssueIDs) deleteCmd.Flags().Var(deleteOptions.IssueID, "issue", "Issue to delete comments from") _ = deleteCmd.MarkFlagRequired("issue") _ = deleteCmd.RegisterFlagCompletionFunc(deleteOptions.IssueID.CompletionFunc("issue")) @@ -40,9 +40,6 @@ func deleteValidArgs(cmd *cobra.Command, args []string, toComplete string) ([]st return nil, cobra.ShellCompDirectiveNoFileComp } - if profile.Current == nil { - return []string{}, cobra.ShellCompDirectiveNoFileComp - } commentIDs, err := GetIssueCommentIDs(cmd.Context(), cmd, profile.Current, deleteOptions.IssueID.Value) if err != nil { cobra.CompErrorln(err.Error()) diff --git a/cmd/issue/comment/get.go b/cmd/issue/comment/get.go index 83832fb..1b50d84 100644 --- a/cmd/issue/comment/get.go +++ b/cmd/issue/comment/get.go @@ -29,7 +29,7 @@ var getOptions struct { func init() { Command.AddCommand(getCmd) - getOptions.IssueID = flags.NewEnumFlagWithFunc("", GetIssueIDs) + getOptions.IssueID = flags.NewEnumFlagWithFunc(getCmd, "", GetIssueIDs) getOptions.Columns = flags.NewEnumSliceFlag(columns.Columns()...) getCmd.Flags().Var(getOptions.IssueID, "issue", "Issue to get comments from") getCmd.Flags().Var(getOptions.Columns, "columns", "Comma-separated list of columns to display") @@ -43,10 +43,7 @@ func getValidArgs(cmd *cobra.Command, args []string, toComplete string) ([]strin return nil, cobra.ShellCompDirectiveNoFileComp } - if profile.Current == nil { - return []string{}, cobra.ShellCompDirectiveNoFileComp - } - commentIDs, err := GetIssueCommentIDs(cmd.Context(), cmd, profile.Current, deleteOptions.IssueID.Value) + commentIDs, err := GetIssueCommentIDs(cmd.Context(), cmd, profile.Current, getOptions.IssueID.Value) if err != nil { cobra.CompErrorln(err.Error()) return []string{}, cobra.ShellCompDirectiveError diff --git a/cmd/issue/comment/list.go b/cmd/issue/comment/list.go index 8524f06..2f0bec0 100644 --- a/cmd/issue/comment/list.go +++ b/cmd/issue/comment/list.go @@ -31,7 +31,7 @@ var listOptions struct { func init() { Command.AddCommand(listCmd) - listOptions.IssueID = flags.NewEnumFlagWithFunc("", GetIssueIDs) + listOptions.IssueID = flags.NewEnumFlagWithFunc(listCmd, "", GetIssueIDs) listOptions.Columns = flags.NewEnumSliceFlagWithAllAllowed(columns.Columns()...) listOptions.SortBy = flags.NewEnumFlag(columns.Sorters()...) listCmd.Flags().Var(listOptions.IssueID, "issue", "Issue to list comments from") @@ -53,16 +53,16 @@ func listProcess(cmd *cobra.Command, args []string) (err error) { return err } - uripath := repository.GetPath("issues", listOptions.IssueID.Value, "comments") - if len(listOptions.Query) > 0 { - uripath += "?q=" + url.QueryEscape(listOptions.Query) - } - log.Infof("Listing all comments from repository %s", repository) if !common.WhatIf(log.ToContext(cmd.Context()), cmd, fmt.Sprintf("Showing comments for issue %s in repository %s", listOptions.IssueID.Value, repository)) { return nil } + uripath := repository.GetPath("issues", listOptions.IssueID.Value, "comments") + if len(listOptions.Query) > 0 { + uripath += "?q=" + url.QueryEscape(listOptions.Query) + } + comments, err := profile.GetAll[Comment](cmd.Context(), cmd, uripath) if err != nil { return err diff --git a/cmd/issue/comment/update.go b/cmd/issue/comment/update.go index a1d204e..7b63b6a 100644 --- a/cmd/issue/comment/update.go +++ b/cmd/issue/comment/update.go @@ -33,7 +33,7 @@ var updateOptions struct { func init() { Command.AddCommand(updateCmd) - updateOptions.IssueID = flags.NewEnumFlagWithFunc("", GetIssueIDs) + updateOptions.IssueID = flags.NewEnumFlagWithFunc(updateCmd, "", GetIssueIDs) updateCmd.Flags().Var(updateOptions.IssueID, "issue", "Issue to update comments to") updateCmd.Flags().StringVar(&updateOptions.Comment, "comment", "", "Updated comment of the issue") _ = updateCmd.MarkFlagRequired("issue") @@ -46,10 +46,7 @@ func updateValidArgs(cmd *cobra.Command, args []string, toComplete string) ([]st return nil, cobra.ShellCompDirectiveNoFileComp } - if profile.Current == nil { - return []string{}, cobra.ShellCompDirectiveNoFileComp - } - commentIDs, err := GetIssueCommentIDs(cmd.Context(), cmd, profile.Current, deleteOptions.IssueID.Value) + commentIDs, err := GetIssueCommentIDs(cmd.Context(), cmd, profile.Current, updateOptions.IssueID.Value) if err != nil { cobra.CompErrorln(err.Error()) return []string{}, cobra.ShellCompDirectiveError diff --git a/cmd/issue/list.go b/cmd/issue/list.go index 0eb88ed..d1ae922 100644 --- a/cmd/issue/list.go +++ b/cmd/issue/list.go @@ -46,6 +46,16 @@ func init() { func listProcess(cmd *cobra.Command, args []string) (err error) { log := logger.Must(logger.FromContext(cmd.Context())).Child(cmd.Parent().Name(), "list") + repository, err := repository.GetRepository(cmd.Context(), cmd) + if err != nil { + return err + } + + log.Infof("Listing all issues from repository %s", repository) + if !common.WhatIf(log.ToContext(cmd.Context()), cmd, "Showing issues") { + return nil + } + filter := "" if !core.Contains(listOptions.States.GetSlice(), "all") { if states := listOptions.States.GetSlice(); len(states) > 0 { @@ -68,16 +78,6 @@ func listProcess(cmd *cobra.Command, args []string) (err error) { filter += url.QueryEscape(listOptions.Query) } - repository, err := repository.GetRepository(cmd.Context(), cmd) - if err != nil { - return err - } - - log.Infof("Listing all issues from repository %s with profile %s", repository, profile.Current) - if !common.WhatIf(log.ToContext(cmd.Context()), cmd, "Showing issues") { - return nil - } - issues, err := profile.GetAll[Issue](cmd.Context(), cmd, repository.GetPath("issues")+filter) if err != nil { return err diff --git a/cmd/pipeline/common/getters.go b/cmd/pipeline/common/getters.go index ceed2f1..1adee6e 100644 --- a/cmd/pipeline/common/getters.go +++ b/cmd/pipeline/common/getters.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/gildas/bitbucket-cli/cmd/profile" + "github.com/gildas/bitbucket-cli/cmd/repository" "github.com/gildas/go-core" "github.com/gildas/go-logger" "github.com/spf13/cobra" @@ -19,11 +20,16 @@ type PipelineID struct { func GetPipelineIDs(context context.Context, cmd *cobra.Command, args []string, toComplete string) (ids []string, err error) { log := logger.Must(logger.FromContext(context)).Child(nil, "getpipelines") + repository, err := repository.GetRepository(cmd.Context(), cmd) + if err != nil { + return []string{}, err + } + log.Infof("Getting pipelines") pipelines, err := profile.GetAll[PipelineID]( log.ToContext(context), cmd, - "pipelines", + repository.GetPath("pipelines"), ) if err != nil { log.Errorf("Failed to get pipelines", err) diff --git a/cmd/pipeline/get.go b/cmd/pipeline/get.go index 07fa6a6..d6b2153 100644 --- a/cmd/pipeline/get.go +++ b/cmd/pipeline/get.go @@ -39,10 +39,6 @@ func getValidArgs(cmd *cobra.Command, args []string, toComplete string) ([]strin return nil, cobra.ShellCompDirectiveNoFileComp } - if profile.Current == nil { - return []string{}, cobra.ShellCompDirectiveNoFileComp - } - ids, err := plcommon.GetPipelineIDs(cmd.Context(), cmd, args, toComplete) if err != nil { cobra.CompErrorln(err.Error()) diff --git a/cmd/pipeline/list.go b/cmd/pipeline/list.go index 05c3a81..a601ed8 100644 --- a/cmd/pipeline/list.go +++ b/cmd/pipeline/list.go @@ -8,7 +8,6 @@ import ( "github.com/gildas/bitbucket-cli/cmd/profile" "github.com/gildas/bitbucket-cli/cmd/repository" "github.com/gildas/go-core" - "github.com/gildas/go-errors" "github.com/gildas/go-flags" "github.com/gildas/go-logger" "github.com/spf13/cobra" @@ -46,10 +45,6 @@ func init() { func listProcess(cmd *cobra.Command, args []string) (err error) { log := logger.Must(logger.FromContext(cmd.Context())).Child(cmd.Parent().Name(), "list") - if profile.Current == nil { - return errors.ArgumentMissing.With("profile") - } - repository, err := repository.GetRepository(cmd.Context(), cmd) if err != nil { return err diff --git a/cmd/pipeline/step/cases.go b/cmd/pipeline/step/cases.go index ef5d7e6..d12a634 100644 --- a/cmd/pipeline/step/cases.go +++ b/cmd/pipeline/step/cases.go @@ -30,7 +30,7 @@ var casesOptions struct { func init() { Command.AddCommand(casesCmd) - casesOptions.PipelineID = flags.NewEnumFlagWithFunc("", plcommon.GetPipelineIDs) + casesOptions.PipelineID = flags.NewEnumFlagWithFunc(casesCmd, "", plcommon.GetPipelineIDs) casesCmd.Flags().Var(casesOptions.PipelineID, "pipeline", "Pipeline to list steps from") _ = casesCmd.MarkFlagRequired("pipeline") _ = casesCmd.RegisterFlagCompletionFunc(casesOptions.PipelineID.CompletionFunc("pipeline")) @@ -41,10 +41,6 @@ func casesValidArgs(cmd *cobra.Command, args []string, toComplete string) ([]str return nil, cobra.ShellCompDirectiveNoFileComp } - if profile.Current == nil { - return []string{}, cobra.ShellCompDirectiveNoFileComp - } - stepIDs, err := GetPipelineStepIDs(cmd.Context(), cmd, casesOptions.PipelineID.Value) if err != nil { cobra.CompErrorln(err.Error()) diff --git a/cmd/pipeline/step/get.go b/cmd/pipeline/step/get.go index 44c4e5c..b6c8b6a 100644 --- a/cmd/pipeline/step/get.go +++ b/cmd/pipeline/step/get.go @@ -32,7 +32,7 @@ var getOptions struct { func init() { Command.AddCommand(getCmd) - getOptions.PipelineID = flags.NewEnumFlagWithFunc("", plcommon.GetPipelineIDs) + getOptions.PipelineID = flags.NewEnumFlagWithFunc(getCmd, "", plcommon.GetPipelineIDs) getOptions.Columns = flags.NewEnumSliceFlag(columns.Columns()...) getCmd.Flags().Var(getOptions.PipelineID, "pipeline", "Pipeline to list steps from") getCmd.Flags().Var(getOptions.Columns, "columns", "Comma-separated list of columns to display") @@ -47,10 +47,6 @@ func getValidArgs(cmd *cobra.Command, args []string, toComplete string) ([]strin return nil, cobra.ShellCompDirectiveNoFileComp } - if profile.Current == nil { - return []string{}, cobra.ShellCompDirectiveNoFileComp - } - stepIDs, err := GetPipelineStepIDs(cmd.Context(), cmd, getOptions.PipelineID.Value) if err != nil { cobra.CompErrorln(err.Error()) diff --git a/cmd/pipeline/step/list.go b/cmd/pipeline/step/list.go index 6ed156c..14bea2f 100644 --- a/cmd/pipeline/step/list.go +++ b/cmd/pipeline/step/list.go @@ -9,7 +9,6 @@ import ( "github.com/gildas/bitbucket-cli/cmd/profile" "github.com/gildas/bitbucket-cli/cmd/repository" "github.com/gildas/go-core" - "github.com/gildas/go-errors" "github.com/gildas/go-flags" "github.com/gildas/go-logger" "github.com/spf13/cobra" @@ -33,7 +32,7 @@ var listOptions struct { func init() { Command.AddCommand(listCmd) - listOptions.PipelineID = flags.NewEnumFlagWithFunc("", plcommon.GetPipelineIDs) + listOptions.PipelineID = flags.NewEnumFlagWithFunc(listCmd, "", plcommon.GetPipelineIDs) listOptions.Columns = flags.NewEnumSliceFlagWithAllAllowed(columns.Columns()...) listOptions.SortBy = flags.NewEnumFlag(columns.Sorters()...) listCmd.Flags().Var(listOptions.PipelineID, "pipeline", "Pipeline to list steps from") @@ -50,16 +49,12 @@ func init() { func listProcess(cmd *cobra.Command, args []string) (err error) { log := logger.Must(logger.FromContext(cmd.Context())).Child(cmd.Parent().Name(), "list") - if profile.Current == nil { - return errors.ArgumentMissing.With("profile") - } - repository, err := repository.GetRepository(cmd.Context(), cmd) if err != nil { return err } - log.Infof("Listing all comments from repository %s with profile %s", repository, profile.Current) + log.Infof("Listing all steps from repository %s", repository) if !common.WhatIf(log.ToContext(cmd.Context()), cmd, fmt.Sprintf("Showing steps for pipeline %s in repository %s with profile %s", listOptions.PipelineID.Value, repository, profile.Current)) { return nil } diff --git a/cmd/pipeline/step/logs.go b/cmd/pipeline/step/logs.go index efa111d..6f09677 100644 --- a/cmd/pipeline/step/logs.go +++ b/cmd/pipeline/step/logs.go @@ -30,7 +30,7 @@ var logOptions struct { func init() { Command.AddCommand(logCmd) - logOptions.PipelineID = flags.NewEnumFlagWithFunc("", plcommon.GetPipelineIDs) + logOptions.PipelineID = flags.NewEnumFlagWithFunc(logCmd, "", plcommon.GetPipelineIDs) logCmd.Flags().Var(logOptions.PipelineID, "pipeline", "Pipeline to list steps from") _ = logCmd.MarkFlagRequired("pipeline") _ = logCmd.RegisterFlagCompletionFunc(logOptions.PipelineID.CompletionFunc("pipeline")) @@ -41,10 +41,6 @@ func logValidArgs(cmd *cobra.Command, args []string, toComplete string) ([]strin return nil, cobra.ShellCompDirectiveNoFileComp } - if profile.Current == nil { - return []string{}, cobra.ShellCompDirectiveNoFileComp - } - stepIDs, err := GetPipelineStepIDs(cmd.Context(), cmd, logOptions.PipelineID.Value) if err != nil { cobra.CompErrorln(err.Error()) diff --git a/cmd/pipeline/step/report.go b/cmd/pipeline/step/report.go index 8ac7d32..060ed6c 100644 --- a/cmd/pipeline/step/report.go +++ b/cmd/pipeline/step/report.go @@ -30,7 +30,7 @@ var reportOptions struct { func init() { Command.AddCommand(reportCmd) - reportOptions.PipelineID = flags.NewEnumFlagWithFunc("", plcommon.GetPipelineIDs) + reportOptions.PipelineID = flags.NewEnumFlagWithFunc(reportCmd, "", plcommon.GetPipelineIDs) reportCmd.Flags().Var(reportOptions.PipelineID, "pipeline", "Pipeline to list steps from") _ = reportCmd.MarkFlagRequired("pipeline") _ = reportCmd.RegisterFlagCompletionFunc(reportOptions.PipelineID.CompletionFunc("pipeline")) @@ -41,10 +41,6 @@ func reportValidArgs(cmd *cobra.Command, args []string, toComplete string) ([]st return nil, cobra.ShellCompDirectiveNoFileComp } - if profile.Current == nil { - return []string{}, cobra.ShellCompDirectiveNoFileComp - } - stepIDs, err := GetPipelineStepIDs(cmd.Context(), cmd, reportOptions.PipelineID.Value) if err != nil { cobra.CompErrorln(err.Error()) diff --git a/cmd/pipeline/step/step.go b/cmd/pipeline/step/step.go index d98ee9d..bb8c312 100644 --- a/cmd/pipeline/step/step.go +++ b/cmd/pipeline/step/step.go @@ -10,6 +10,7 @@ import ( "github.com/gildas/bitbucket-cli/cmd/common" plcommon "github.com/gildas/bitbucket-cli/cmd/pipeline/common" "github.com/gildas/bitbucket-cli/cmd/profile" + "github.com/gildas/bitbucket-cli/cmd/repository" "github.com/gildas/go-core" "github.com/gildas/go-errors" "github.com/gildas/go-logger" @@ -174,12 +175,16 @@ func (step *Step) UnmarshalJSON(data []byte) error { } // GetPipelineStepIDs gets the IDs of the steps for a pipeline -func GetPipelineStepIDs(context context.Context, cmd *cobra.Command, PipelineID string) (ids []string, err error) { +func GetPipelineStepIDs(context context.Context, cmd *cobra.Command, pipelineID string) (ids []string, err error) { log := logger.Must(logger.FromContext(context)).Child("pipeline", "getids") - steps, err := profile.GetAll[Step](context, cmd, fmt.Sprintf("pipelines/%s/steps", PipelineID)) + repository, err := repository.GetRepository(cmd.Context(), cmd) if err != nil { - log.Errorf("Failed to get pipelines", err) + return []string{}, err + } + steps, err := profile.GetAll[Step](context, cmd, repository.GetPath("pipelines", pipelineID, "steps")) + if err != nil { + log.Errorf("Failed to get steps for pipeline %s: %v", pipelineID, err) return []string{}, err } return core.Map(steps, func(step Step) string { diff --git a/cmd/pipeline/trigger.go b/cmd/pipeline/trigger.go index c4acffd..918772f 100644 --- a/cmd/pipeline/trigger.go +++ b/cmd/pipeline/trigger.go @@ -40,9 +40,9 @@ var triggerOptions struct { func init() { Command.AddCommand(triggerCmd) - triggerOptions.Branch = flags.NewEnumFlagWithFunc("", branch.GetBranchNames) - triggerOptions.Commit = flags.NewEnumFlagWithFunc("", commit.GetCommitHashes) - triggerOptions.Tag = flags.NewEnumFlagWithFunc("", tag.GetTagNames) + triggerOptions.Branch = flags.NewEnumFlagWithFunc(triggerCmd, "", branch.GetBranchNames) + triggerOptions.Commit = flags.NewEnumFlagWithFunc(triggerCmd, "", commit.GetCommitHashes) + triggerOptions.Tag = flags.NewEnumFlagWithFunc(triggerCmd, "", tag.GetTagNames) triggerCmd.Flags().Var(triggerOptions.Branch, "branch", "Branch to run the pipeline on") triggerCmd.Flags().Var(triggerOptions.Tag, "tag", "Tag to run the pipeline on") triggerCmd.Flags().Var(triggerOptions.Commit, "commit", "Specific commit hash to run the pipeline on") diff --git a/cmd/profile/authorize.go b/cmd/profile/authorize.go index 2d4967d..0b6a977 100644 --- a/cmd/profile/authorize.go +++ b/cmd/profile/authorize.go @@ -35,6 +35,10 @@ func authorizeProcess(cmd *cobra.Command, args []string) error { return errors.ArgumentMissing.With("profile") } + if _, err := GetProfileFromCommand(cmd.Context(), cmd); err != nil { + return err + } + log.Infof("Authorizing profile %s (Valid names: %v)", args[0], Profiles.Names()) profile, found := Profiles.Find(args[0]) if !found { diff --git a/cmd/profile/create.go b/cmd/profile/create.go index 5e953c0..c5014ae 100644 --- a/cmd/profile/create.go +++ b/cmd/profile/create.go @@ -35,8 +35,8 @@ var createOptions struct { func init() { Command.AddCommand(createCmd) - createOptions.DefaultWorkspace = flags.NewEnumFlagWithFunc("", getWorkspaceSlugs) - createOptions.DefaultProject = flags.NewEnumFlagWithFunc("", getProjectKeys) + createOptions.DefaultWorkspace = flags.NewEnumFlagWithFunc(createCmd, "", getWorkspaceSlugs) + createOptions.DefaultProject = flags.NewEnumFlagWithFunc(createCmd, "", getProjectKeys) createOptions.OutputFormat = flags.NewEnumFlag("json", "yaml", "table") createOptions.CloneProtocol = flags.NewEnumFlag("+git", "https", "ssh") createCmd.Flags().StringVarP(&createOptions.Name, "name", "n", "", "Name of the profile") @@ -78,6 +78,10 @@ func init() { func createProcess(cmd *cobra.Command, args []string) error { log := logger.Must(logger.FromContext(cmd.Context())).Child(cmd.Parent().Name(), "create") + if _, err := GetProfileFromCommand(cmd.Context(), cmd); err != nil { + return err + } + if len(createOptions.DefaultWorkspace.String()) > 0 { createOptions.Profile.DefaultWorkspace = createOptions.DefaultWorkspace.String() } diff --git a/cmd/profile/delete.go b/cmd/profile/delete.go index f1b5cf5..ac25d1c 100644 --- a/cmd/profile/delete.go +++ b/cmd/profile/delete.go @@ -41,6 +41,10 @@ func deleteProcess(cmd *cobra.Command, args []string) (err error) { log := logger.Must(logger.FromContext(cmd.Context())).Child(cmd.Parent().Name(), "delete") var deleted int + if _, err := GetProfileFromCommand(cmd.Context(), cmd); err != nil { + return err + } + if deleteOptions.All { log.Infof("Deleting all profiles") if common.WhatIf(log.ToContext(cmd.Context()), cmd, "Deleting all profiles") { diff --git a/cmd/profile/get.go b/cmd/profile/get.go index f1ad352..b92b37c 100644 --- a/cmd/profile/get.go +++ b/cmd/profile/get.go @@ -38,6 +38,10 @@ func init() { func getProcess(cmd *cobra.Command, args []string) error { log := logger.Must(logger.FromContext(cmd.Context())).Child(cmd.Parent().Name(), "get") + if _, err := GetProfileFromCommand(cmd.Context(), cmd); err != nil { + return err + } + if getOptions.Current { log.Infof("Displaying current profile") if Current == nil { diff --git a/cmd/profile/list.go b/cmd/profile/list.go index 3bf6a59..3a0882f 100644 --- a/cmd/profile/list.go +++ b/cmd/profile/list.go @@ -41,6 +41,10 @@ func listProcess(cmd *cobra.Command, args []string) (err error) { return nil } + if _, err = GetProfileFromCommand(cmd.Context(), cmd); err != nil { + return err + } + if len(Profiles) == 0 { log.Infof("No profiles found") return diff --git a/cmd/profile/profile.go b/cmd/profile/profile.go index aaf1c9e..9e52db0 100644 --- a/cmd/profile/profile.go +++ b/cmd/profile/profile.go @@ -124,18 +124,19 @@ var columns = common.Columns[*Profile]{ // // If the profile is not given, it will use the current profile func GetProfileFromCommand(context context.Context, cmd *cobra.Command) (profile *Profile, err error) { + log := logger.Must(logger.FromContext(context)).Child("profile", "getProfileFromCommand") + + if err = Profiles.Load(context, cmd); err != nil { + return nil, err + } + if cmd.Flag("profile").Changed { var found bool + log.Debugf("Command line has profile flag set to %s", cmd.Flag("profile").Value.String()) if profile, found = Profiles.Find(cmd.Flag("profile").Value.String()); !found { return nil, errors.ArgumentInvalid.With("profile", cmd.Flag("profile").Value.String()) } } else if Current == nil { - if len(Profiles) == 0 { - err = Profiles.Load(context) - if err != nil { - return nil, err - } - } Current = Profiles.Current(context) if Current == nil { return nil, errors.ArgumentMissing.With("profile") diff --git a/cmd/profile/profiles.go b/cmd/profile/profiles.go index 6314c65..1ada56c 100644 --- a/cmd/profile/profiles.go +++ b/cmd/profile/profiles.go @@ -24,7 +24,7 @@ func (profiles profiles) Current(context context.Context) *Profile { if gitConfig, err := common.OpenGitConfig(context); err == nil { log.Debugf("Found a git config file") if section, err := common.GetGitSection(context, gitConfig, `bitbucket "cli"`); err == nil { - log.Debugf("Found a bitbucket \"cli\" section in git config") + log.Debugf("Found a bitbucket \"cli\" section in git config: name=%s", section.Name()) if profileName := section.Key("profile").String(); len(profileName) > 0 { log.Debugf("Found a profile in git config: %s", profileName) if profile, found := profiles.Find(profileName); found { @@ -38,14 +38,18 @@ func (profiles profiles) Current(context context.Context) *Profile { } } + log.Debugf("No profile found in git config, looking for default profile in %d profiles", len(profiles)) for _, profile := range profiles { if profile.Default { + log.Infof("Using default profile %s", profile.Name) return profile } } if len(profiles) > 0 { + log.Infof("Using first profile %s", profiles[0].Name) return profiles[0] } + log.Warnf("No profile found") return nil } @@ -132,8 +136,18 @@ func (profiles profiles) SetCurrent(name string) { } // Load loads the profiles from a viper key -func (profiles *profiles) Load(context context.Context) error { - log := logger.Must(logger.FromContext(context)).Child("profiles", "load") +func (profiles *profiles) Load(ctx context.Context, cmd *cobra.Command) error { + log := logger.Must(logger.FromContext(ctx)).Child("profiles", "load") + + if len(*profiles) > 0 { + return nil + } + + if len(viper.AllKeys()) == 0 { + if err := common.Initialize(cmd); err != nil { + return err + } + } log.Infof("Loading profiles from %s", viper.ConfigFileUsed()) if err := viper.UnmarshalKey("profiles", &profiles); err != nil { @@ -149,6 +163,10 @@ func ValidProfileNames(cmd *cobra.Command, args []string, toComplete string) ([] return nil, cobra.ShellCompDirectiveNoFileComp } + if _, err := GetProfileFromCommand(cmd.Context(), cmd); err != nil { + return []string{}, cobra.ShellCompDirectiveNoFileComp + } + names := Profiles.Names() return common.FilterValidArgs(names, args, toComplete), cobra.ShellCompDirectiveNoFileComp } diff --git a/cmd/profile/update.go b/cmd/profile/update.go index 794e663..c8e51a9 100644 --- a/cmd/profile/update.go +++ b/cmd/profile/update.go @@ -37,8 +37,8 @@ var updateOptions struct { func init() { Command.AddCommand(updateCmd) - updateOptions.DefaultWorkspace = flags.NewEnumFlagWithFunc("", getWorkspaceSlugs) - updateOptions.DefaultProject = flags.NewEnumFlagWithFunc("", getProjectKeys) + updateOptions.DefaultWorkspace = flags.NewEnumFlagWithFunc(updateCmd, "", getWorkspaceSlugs) + updateOptions.DefaultProject = flags.NewEnumFlagWithFunc(updateCmd, "", getProjectKeys) updateOptions.OutputFormat = flags.NewEnumFlag("json", "yaml", "table") updateOptions.CloneProtocol = flags.NewEnumFlag("+git", "https", "ssh") updateCmd.Flags().StringVarP(&updateOptions.Name, "name", "n", "", "Name of the profile") @@ -84,6 +84,10 @@ func init() { func updateProcess(cmd *cobra.Command, args []string) error { log := logger.Must(logger.FromContext(cmd.Context())).Child(cmd.Parent().Name(), "update") + if _, err := GetProfileFromCommand(cmd.Context(), cmd); err != nil { + return err + } + if len(updateOptions.DefaultWorkspace.String()) > 0 { updateOptions.Profile.DefaultWorkspace = updateOptions.DefaultWorkspace.String() } diff --git a/cmd/profile/use.go b/cmd/profile/use.go index 732d27f..e19ebdf 100644 --- a/cmd/profile/use.go +++ b/cmd/profile/use.go @@ -27,6 +27,10 @@ func init() { func useProcess(cmd *cobra.Command, args []string) error { log := logger.Must(logger.FromContext(cmd.Context())).Child(cmd.Parent().Name(), "get") + if _, err := GetProfileFromCommand(cmd.Context(), cmd); err != nil { + return err + } + log.Infof("Using profile %s (Valid names: %v)", args[0], Profiles.Names()) profile, found := Profiles.Find(args[0]) if !found { diff --git a/cmd/project/reviewer/add.go b/cmd/project/reviewer/add.go index 4c9c61f..79ae685 100644 --- a/cmd/project/reviewer/add.go +++ b/cmd/project/reviewer/add.go @@ -29,7 +29,7 @@ var addOptions struct { func init() { Command.AddCommand(addCmd) - addOptions.Project = flags.NewEnumFlagWithFunc("", GetProjectKeys) + addOptions.Project = flags.NewEnumFlagWithFunc(addCmd, "", GetProjectKeys) addCmd.Flags().Var(addOptions.Project, "project", "Project Key to add reviewers to") _ = addCmd.RegisterFlagCompletionFunc(addOptions.Project.CompletionFunc("project")) addCmd.SetHelpFunc(hideUnsupportedFlags) diff --git a/cmd/project/reviewer/delete.go b/cmd/project/reviewer/delete.go index 42dd5e7..da44d17 100644 --- a/cmd/project/reviewer/delete.go +++ b/cmd/project/reviewer/delete.go @@ -30,7 +30,7 @@ var deleteOptions struct { func init() { Command.AddCommand(deleteCmd) - deleteOptions.Project = flags.NewEnumFlagWithFunc("", GetProjectKeys) + deleteOptions.Project = flags.NewEnumFlagWithFunc(deleteCmd, "", GetProjectKeys) deleteCmd.Flags().Var(deleteOptions.Project, "project", "Project Key to delete reviewers from") _ = deleteCmd.RegisterFlagCompletionFunc(deleteOptions.Project.CompletionFunc("project")) deleteCmd.SetHelpFunc(hideUnsupportedFlags) diff --git a/cmd/project/reviewer/get.go b/cmd/project/reviewer/get.go index 192d72a..5ec1971 100644 --- a/cmd/project/reviewer/get.go +++ b/cmd/project/reviewer/get.go @@ -31,7 +31,7 @@ var getOptions struct { func init() { Command.AddCommand(getCmd) - getOptions.Project = flags.NewEnumFlagWithFunc("", GetProjectKeys) + getOptions.Project = flags.NewEnumFlagWithFunc(getCmd, "", GetProjectKeys) getOptions.Columns = flags.NewEnumSliceFlag(columns.Columns()...) getCmd.Flags().Var(getOptions.Project, "project", "Project Key to get reviewers from") getCmd.Flags().Var(getOptions.Columns, "columns", "Comma-separated list of columns to display") @@ -45,7 +45,7 @@ func getValidArgs(cmd *cobra.Command, args []string, toComplete string) ([]strin return nil, cobra.ShellCompDirectiveNoFileComp } - userIDs, err := GetReviewerUserIDs(cmd.Context(), cmd, deleteOptions.Project.Value) + userIDs, err := GetReviewerUserIDs(cmd.Context(), cmd, getOptions.Project.Value) if err != nil { return []string{}, cobra.ShellCompDirectiveNoFileComp } diff --git a/cmd/project/reviewer/list.go b/cmd/project/reviewer/list.go index 1e01cba..7df8789 100644 --- a/cmd/project/reviewer/list.go +++ b/cmd/project/reviewer/list.go @@ -28,7 +28,7 @@ var listOptions struct { func init() { Command.AddCommand(listCmd) - listOptions.Project = flags.NewEnumFlagWithFunc("", GetProjectKeys) + listOptions.Project = flags.NewEnumFlagWithFunc(listCmd, "", GetProjectKeys) listOptions.Columns = flags.NewEnumSliceFlagWithAllAllowed(columns.Columns()...) listOptions.SortBy = flags.NewEnumFlag(columns.Sorters()...) listCmd.Flags().Var(listOptions.Project, "project", "Project Key to list reviewers from") diff --git a/cmd/pullrequest/activities.go b/cmd/pullrequest/activities.go index 95c2c2b..53d1c8f 100644 --- a/cmd/pullrequest/activities.go +++ b/cmd/pullrequest/activities.go @@ -75,10 +75,6 @@ func activitiesValidArgs(cmd *cobra.Command, args []string, toComplete string) ( return nil, cobra.ShellCompDirectiveNoFileComp } - if profile.Current == nil { - return []string{}, cobra.ShellCompDirectiveNoFileComp - } - ids, err := prcommon.GetPullRequestIDsWithState(cmd.Context(), cmd, "OPEN") if err != nil { cobra.CompErrorln(err.Error()) diff --git a/cmd/pullrequest/activity/list.go b/cmd/pullrequest/activity/list.go index 2fa61aa..5bc1666 100644 --- a/cmd/pullrequest/activity/list.go +++ b/cmd/pullrequest/activity/list.go @@ -9,7 +9,6 @@ import ( "github.com/gildas/bitbucket-cli/cmd/pullrequest/common" "github.com/gildas/bitbucket-cli/cmd/repository" "github.com/gildas/go-core" - "github.com/gildas/go-errors" "github.com/gildas/go-flags" "github.com/gildas/go-logger" "github.com/spf13/cobra" @@ -34,7 +33,7 @@ var listOptions struct { func init() { Command.AddCommand(listCmd) - listOptions.PullRequestID = flags.NewEnumFlagWithFunc("", prcommon.GetPullRequestIDs) + listOptions.PullRequestID = flags.NewEnumFlagWithFunc(listCmd, "", prcommon.GetPullRequestIDs) listOptions.Columns = flags.NewEnumSliceFlagWithAllAllowed(columns.Columns()...) listOptions.SortBy = flags.NewEnumFlag(columns.Sorters()...) listCmd.Flags().Var(listOptions.PullRequestID, "pullrequest", "pullrequest to list activities from") @@ -51,10 +50,6 @@ func init() { func listProcess(cmd *cobra.Command, args []string) (err error) { log := logger.Must(logger.FromContext(cmd.Context())).Child(cmd.Parent().Name(), "list") - if profile.Current == nil { - return errors.ArgumentMissing.With("profile") - } - repository, err := repository.GetRepository(cmd.Context(), cmd) if err != nil { return err @@ -66,7 +61,7 @@ func listProcess(cmd *cobra.Command, args []string) (err error) { uripath = fmt.Sprintf("%s?q=%s", uripath, url.QueryEscape(listOptions.Query)) } - log.Infof("Listing all activities from repository %s with profile %s", repository, profile.Current) + log.Infof("Listing all activities from repository %s", repository) if !common.WhatIf(log.ToContext(cmd.Context()), cmd, fmt.Sprintf("Showing activities for pullrequest %s in repository %s with profile %s", listOptions.PullRequestID.Value, repository, profile.Current)) { return nil } diff --git a/cmd/pullrequest/approve.go b/cmd/pullrequest/approve.go index 1381a0a..e1fa930 100644 --- a/cmd/pullrequest/approve.go +++ b/cmd/pullrequest/approve.go @@ -29,10 +29,6 @@ func approveValidArgs(cmd *cobra.Command, args []string, toComplete string) ([]s return nil, cobra.ShellCompDirectiveNoFileComp } - if profile.Current == nil { - return []string{}, cobra.ShellCompDirectiveNoFileComp - } - ids, err := prcommon.GetPullRequestIDsWithState(cmd.Context(), cmd, "OPEN") if err != nil { cobra.CompErrorln(err.Error()) diff --git a/cmd/pullrequest/comment/create.go b/cmd/pullrequest/comment/create.go index 2b29f01..995dfbe 100644 --- a/cmd/pullrequest/comment/create.go +++ b/cmd/pullrequest/comment/create.go @@ -15,9 +15,10 @@ import ( ) type CommentCreator struct { - Content ContentCreator `json:"content" mapstructure:"content"` - Anchor *common.FileAnchor `json:"inline,omitempty" mapstructure:"inline"` - Parent *ParentReference `json:"parent,omitempty" mapstructure:"parent"` + Content ContentCreator `json:"content" mapstructure:"content"` + Anchor *common.FileAnchor `json:"inline,omitempty" mapstructure:"inline"` + Parent *ParentReference `json:"parent,omitempty" mapstructure:"parent"` + Pending *bool `json:"pending,omitempty" mapstructure:"pending"` } type ContentCreator struct { @@ -39,12 +40,13 @@ var createOptions struct { From int To int ParentID int64 + Pending bool } func init() { Command.AddCommand(createCmd) - createOptions.PullRequestID = flags.NewEnumFlagWithFunc("", prcommon.GetPullRequestIDs) + createOptions.PullRequestID = flags.NewEnumFlagWithFunc(createCmd, "", prcommon.GetPullRequestIDs) createCmd.Flags().Var(createOptions.PullRequestID, "pullrequest", "Pullrequest to create comments to") createCmd.Flags().StringVar(&createOptions.Comment, "comment", "", "Comment of the pullrequest") createCmd.Flags().StringVar(&createOptions.File, "file", "", "File to comment on") @@ -52,6 +54,7 @@ func init() { createCmd.Flags().IntVar(&createOptions.From, "from", 0, "From line to comment on. Cannot be used with --line") createCmd.Flags().IntVar(&createOptions.To, "to", 0, "To line to comment on. Cannot be used with --line") createCmd.Flags().Int64Var(&createOptions.ParentID, "parent", 0, "Parent comment ID to reply to") + createCmd.Flags().BoolVar(&createOptions.Pending, "pending", false, "Mark the comment as pending") createCmd.MarkFlagsMutuallyExclusive("line", "from") createCmd.MarkFlagsMutuallyExclusive("line", "to") _ = createCmd.MarkFlagRequired("pullrequest") @@ -93,9 +96,12 @@ func createProcess(cmd *cobra.Command, args []string) (err error) { } else if createOptions.From > 0 || createOptions.To > 0 { return errors.RuntimeError.With("Cannot specify from/to without a file") } + if cmd.Flag("pending").Changed { + payload.Pending = &createOptions.Pending + } log.Record("payload", payload).Infof("Creating pullrequest comment") - if !common.WhatIf(log.ToContext(cmd.Context()), cmd, "Creating comment for pullrequest %s", createOptions.PullRequestID) { + if !common.WhatIf(log.ToContext(cmd.Context()), cmd, "Creating comment for pullrequest %s", createOptions.PullRequestID.Value) { return nil } var comment Comment diff --git a/cmd/pullrequest/comment/create_test.go b/cmd/pullrequest/comment/create_test.go index a4d12ce..78f58f1 100644 --- a/cmd/pullrequest/comment/create_test.go +++ b/cmd/pullrequest/comment/create_test.go @@ -122,3 +122,28 @@ func (suite *CommentCreateSuite) TestCommentCreatorJSONMatchesBitbucketAPIFormat expected := `{"content":{"raw":"Done!"},"parent":{"id":759578390}}` suite.Assert().JSONEq(expected, string(data)) } + +func (suite *CommentCreateSuite) TestCanMarshalCommentCreatorWithPending() { + creator := comment.CommentCreator{ + Content: comment.ContentCreator{Raw: "This is a top-level comment"}, + Pending: new(true), + } + + data, err := json.Marshal(creator) + suite.Require().NoError(err) + + var result map[string]interface{} + err = json.Unmarshal(data, &result) + suite.Require().NoError(err) + + content, ok := result["content"].(map[string]interface{}) + suite.Require().True(ok, "content should be present") + suite.Assert().Equal("This is a top-level comment", content["raw"]) + + _, ok = result["parent"] + suite.Assert().False(ok, "parent should not be present when nil") + + pending, ok := result["pending"].(bool) + suite.Require().True(ok, "pending should be present") + suite.Assert().True(pending, "pending should be true") +} diff --git a/cmd/pullrequest/comment/delete.go b/cmd/pullrequest/comment/delete.go index 15a527f..00c2105 100644 --- a/cmd/pullrequest/comment/delete.go +++ b/cmd/pullrequest/comment/delete.go @@ -30,7 +30,7 @@ var deleteOptions struct { func init() { Command.AddCommand(deleteCmd) - deleteOptions.PullRequestID = flags.NewEnumFlagWithFunc("", prcommon.GetPullRequestIDs) + deleteOptions.PullRequestID = flags.NewEnumFlagWithFunc(deleteCmd, "", prcommon.GetPullRequestIDs) deleteCmd.Flags().Var(deleteOptions.PullRequestID, "pullrequest", "Pullrequest to delete comments from") _ = deleteCmd.MarkFlagRequired("pullrequest") _ = deleteCmd.RegisterFlagCompletionFunc(deleteOptions.PullRequestID.CompletionFunc("pullrequest")) @@ -63,7 +63,7 @@ func deleteProcess(cmd *cobra.Command, args []string) error { var merr errors.MultiError for _, commentID := range args { - if common.WhatIf(log.ToContext(cmd.Context()), cmd, "Deleting comment %s from pullrequest %s", commentID, deleteOptions.PullRequestID) { + if common.WhatIf(log.ToContext(cmd.Context()), cmd, "Deleting comment %s from pullrequest %s", commentID, deleteOptions.PullRequestID.Value) { err := profile.Delete( log.ToContext(cmd.Context()), cmd, diff --git a/cmd/pullrequest/comment/get.go b/cmd/pullrequest/comment/get.go index 333a41f..bc45399 100644 --- a/cmd/pullrequest/comment/get.go +++ b/cmd/pullrequest/comment/get.go @@ -30,7 +30,7 @@ var getOptions struct { func init() { Command.AddCommand(getCmd) - getOptions.PullRequestID = flags.NewEnumFlagWithFunc("", prcommon.GetPullRequestIDs) + getOptions.PullRequestID = flags.NewEnumFlagWithFunc(getCmd, "", prcommon.GetPullRequestIDs) getOptions.Columns = flags.NewEnumSliceFlag(columns.Columns()...) getCmd.Flags().Var(getOptions.PullRequestID, "pullrequest", "Pullrequest to get comments from") getCmd.Flags().Var(getOptions.Columns, "columns", "Comma-separated list of columns to display") diff --git a/cmd/pullrequest/comment/list.go b/cmd/pullrequest/comment/list.go index d72eb20..3e9f2f1 100644 --- a/cmd/pullrequest/comment/list.go +++ b/cmd/pullrequest/comment/list.go @@ -9,7 +9,6 @@ import ( prcommon "github.com/gildas/bitbucket-cli/cmd/pullrequest/common" "github.com/gildas/bitbucket-cli/cmd/repository" "github.com/gildas/go-core" - "github.com/gildas/go-errors" "github.com/gildas/go-flags" "github.com/gildas/go-logger" "github.com/spf13/cobra" @@ -33,7 +32,7 @@ var listOptions struct { func init() { Command.AddCommand(listCmd) - listOptions.PullRequestID = flags.NewEnumFlagWithFunc("", prcommon.GetPullRequestIDs) + listOptions.PullRequestID = flags.NewEnumFlagWithFunc(listCmd, "", prcommon.GetPullRequestIDs) listOptions.Columns = flags.NewEnumSliceFlagWithAllAllowed(columns.Columns()...) listOptions.SortBy = flags.NewEnumFlag(columns.Sorters()...) listCmd.Flags().Var(listOptions.PullRequestID, "pullrequest", "pullrequest to list comments from") @@ -50,10 +49,6 @@ func init() { func listProcess(cmd *cobra.Command, args []string) (err error) { log := logger.Must(logger.FromContext(cmd.Context())).Child(cmd.Parent().Name(), "list") - if profile.Current == nil { - return errors.ArgumentMissing.With("profile") - } - repository, err := repository.GetRepository(cmd.Context(), cmd) if err != nil { return err @@ -65,7 +60,7 @@ func listProcess(cmd *cobra.Command, args []string) (err error) { uripath = fmt.Sprintf("%s?q=%s", uripath, url.QueryEscape(listOptions.Query)) } - log.Infof("Listing all comments from repository %s with profile %s", repository, profile.Current) + log.Infof("Listing all comments from repository %s", repository) if !common.WhatIf(log.ToContext(cmd.Context()), cmd, fmt.Sprintf("Showing comments for pullrequest %s in repository %s with profile %s", listOptions.PullRequestID.Value, repository, profile.Current)) { return nil } diff --git a/cmd/pullrequest/comment/reopen.go b/cmd/pullrequest/comment/reopen.go index 38da18a..5fcce25 100644 --- a/cmd/pullrequest/comment/reopen.go +++ b/cmd/pullrequest/comment/reopen.go @@ -29,7 +29,7 @@ var reopenOptions struct { func init() { Command.AddCommand(reopenCmd) - reopenOptions.PullRequestID = flags.NewEnumFlagWithFunc("", prcommon.GetPullRequestIDs) + reopenOptions.PullRequestID = flags.NewEnumFlagWithFunc(reopenCmd, "", prcommon.GetPullRequestIDs) reopenCmd.Flags().Var(reopenOptions.PullRequestID, "pullrequest", "Pullrequest to reopen comments from") _ = reopenCmd.MarkFlagRequired("pullrequest") _ = reopenCmd.RegisterFlagCompletionFunc(reopenOptions.PullRequestID.CompletionFunc("pullrequest")) @@ -60,7 +60,7 @@ func reopenProcess(cmd *cobra.Command, args []string) (err error) { return err } - if !common.WhatIf(log.ToContext(cmd.Context()), cmd, "Resolving comment %s from pullrequest %s", args[0], reopenOptions.PullRequestID) { + if !common.WhatIf(log.ToContext(cmd.Context()), cmd, "Reopening comment %s from pullrequest %s", args[0], reopenOptions.PullRequestID.Value) { return nil } diff --git a/cmd/pullrequest/comment/resolve.go b/cmd/pullrequest/comment/resolve.go index b4cb06c..f5ec1c2 100644 --- a/cmd/pullrequest/comment/resolve.go +++ b/cmd/pullrequest/comment/resolve.go @@ -29,7 +29,7 @@ var resolveOptions struct { func init() { Command.AddCommand(resolveCmd) - resolveOptions.PullRequestID = flags.NewEnumFlagWithFunc("", prcommon.GetPullRequestIDs) + resolveOptions.PullRequestID = flags.NewEnumFlagWithFunc(resolveCmd, "", prcommon.GetPullRequestIDs) resolveCmd.Flags().Var(resolveOptions.PullRequestID, "pullrequest", "Pullrequest to resolve comments from") _ = resolveCmd.MarkFlagRequired("pullrequest") _ = resolveCmd.RegisterFlagCompletionFunc(resolveOptions.PullRequestID.CompletionFunc("pullrequest")) @@ -60,7 +60,7 @@ func resolveProcess(cmd *cobra.Command, args []string) (err error) { return err } - if !common.WhatIf(log.ToContext(cmd.Context()), cmd, "Resolving comment %s from pullrequest %s", args[0], resolveOptions.PullRequestID) { + if !common.WhatIf(log.ToContext(cmd.Context()), cmd, "Resolving comment %s from pullrequest %s", args[0], resolveOptions.PullRequestID.Value) { return nil } diff --git a/cmd/pullrequest/comment/update.go b/cmd/pullrequest/comment/update.go index 9208c46..08cce52 100644 --- a/cmd/pullrequest/comment/update.go +++ b/cmd/pullrequest/comment/update.go @@ -15,9 +15,10 @@ import ( ) type CommentUpdator struct { - Content ContentUpdator `json:"content" mapstructure:"content"` - Anchor *common.FileAnchor `json:"inline,omitempty" mapstructure:"inline"` - Parent *ParentReference `json:"parent,omitempty" mapstructure:"parent"` + Content ContentUpdator `json:"content" mapstructure:"content"` + Anchor *common.FileAnchor `json:"inline,omitempty" mapstructure:"inline"` + Parent *ParentReference `json:"parent,omitempty" mapstructure:"parent"` + Pending *bool `json:"pending,omitempty" mapstructure:"pending"` } type ContentUpdator struct { @@ -40,12 +41,13 @@ var updateOptions struct { From int To int ParentID int64 + Pending bool } func init() { Command.AddCommand(updateCmd) - updateOptions.PullRequestID = flags.NewEnumFlagWithFunc("", prcommon.GetPullRequestIDs) + updateOptions.PullRequestID = flags.NewEnumFlagWithFunc(updateCmd, "", prcommon.GetPullRequestIDs) updateCmd.Flags().Var(updateOptions.PullRequestID, "pullrequest", "Pullrequest to update comments to") updateCmd.Flags().StringVar(&updateOptions.Comment, "comment", "", "Updated comment of the pullrequest") updateCmd.Flags().StringVar(&updateOptions.File, "file", "", "File to comment on") @@ -53,6 +55,7 @@ func init() { updateCmd.Flags().IntVar(&updateOptions.From, "from", 0, "From line to comment on. Cannot be used with --line") updateCmd.Flags().IntVar(&updateOptions.To, "to", 0, "To line to comment on. Cannot be used with --line") updateCmd.Flags().Int64Var(&updateOptions.ParentID, "parent", 0, "Parent comment ID to reply to") + updateCmd.Flags().BoolVar(&updateOptions.Pending, "pending", false, "Mark the comment as pending") updateCmd.MarkFlagsMutuallyExclusive("line", "from") updateCmd.MarkFlagsMutuallyExclusive("line", "to") _ = updateCmd.MarkFlagRequired("pullrequest") @@ -103,12 +106,16 @@ func updateProcess(cmd *cobra.Command, args []string) (err error) { return errors.RuntimeError.With("Cannot specify from/to without a file") } + if cmd.Flag("pending").Changed { + payload.Pending = &updateOptions.Pending + } + if updateOptions.ParentID > 0 { payload.Parent = &ParentReference{ID: updateOptions.ParentID} } log.Record("payload", payload).Infof("Updating pullrequest comment") - if !common.WhatIf(log.ToContext(cmd.Context()), cmd, "Updating comment %s for pullrequest %s", updateOptions.Comment, updateOptions.PullRequestID) { + if !common.WhatIf(log.ToContext(cmd.Context()), cmd, "Updating comment %s for pullrequest %s", args[0], updateOptions.PullRequestID.Value) { return nil } var comment Comment diff --git a/cmd/pullrequest/commits.go b/cmd/pullrequest/commits.go index f1e26b6..e10d849 100644 --- a/cmd/pullrequest/commits.go +++ b/cmd/pullrequest/commits.go @@ -44,10 +44,6 @@ func commitsValidArgs(cmd *cobra.Command, args []string, toComplete string) ([]s return nil, cobra.ShellCompDirectiveNoFileComp } - if profile.Current == nil { - return []string{}, cobra.ShellCompDirectiveNoFileComp - } - ids, err := prcommon.GetPullRequestIDsWithState(cmd.Context(), cmd, "OPEN") if err != nil { cobra.CompErrorln(err.Error()) diff --git a/cmd/pullrequest/create.go b/cmd/pullrequest/create.go index 60f0a99..9739650 100644 --- a/cmd/pullrequest/create.go +++ b/cmd/pullrequest/create.go @@ -50,9 +50,9 @@ var createOptions struct { func init() { Command.AddCommand(createCmd) - createOptions.Source = flags.NewEnumFlagWithFunc("", branch.GetBranchNames) - createOptions.Destination = flags.NewEnumFlagWithFunc("", branch.GetBranchNames) - createOptions.Reviewers = flags.NewEnumSliceFlagWithAllAllowedAndFunc(GetReviewerNicknames) + createOptions.Source = flags.NewEnumFlagWithFunc(createCmd, "", branch.GetBranchNames) + createOptions.Destination = flags.NewEnumFlagWithFunc(createCmd, "", branch.GetBranchNames) + createOptions.Reviewers = flags.NewEnumSliceFlagWithAllAllowedAndFunc(createCmd, GetReviewerNicknames) createCmd.Flags().StringVar(&createOptions.Title, "title", "", "Title of the pullrequest") createCmd.Flags().StringVar(&createOptions.Description, "description", "", "Description of the pullrequest") diff --git a/cmd/pullrequest/decline.go b/cmd/pullrequest/decline.go index 9fe31d4..ca4485b 100644 --- a/cmd/pullrequest/decline.go +++ b/cmd/pullrequest/decline.go @@ -28,10 +28,6 @@ func declineValidArgs(cmd *cobra.Command, args []string, toComplete string) ([]s return nil, cobra.ShellCompDirectiveNoFileComp } - if profile.Current == nil { - return []string{}, cobra.ShellCompDirectiveNoFileComp - } - ids, err := prcommon.GetPullRequestIDsWithState(cmd.Context(), cmd, "OPEN") if err != nil { cobra.CompErrorln(err.Error()) diff --git a/cmd/pullrequest/diff.go b/cmd/pullrequest/diff.go index f6a0d92..216002b 100644 --- a/cmd/pullrequest/diff.go +++ b/cmd/pullrequest/diff.go @@ -37,10 +37,6 @@ func validDiffArgs(cmd *cobra.Command, args []string, toComplete string) ([]stri return nil, cobra.ShellCompDirectiveNoFileComp } - if profile.Current == nil { - return []string{}, cobra.ShellCompDirectiveNoFileComp - } - ids, err := prcommon.GetPullRequestIDsWithState(cmd.Context(), cmd, "OPEN") if err != nil { cobra.CompErrorln(err.Error()) diff --git a/cmd/pullrequest/get.go b/cmd/pullrequest/get.go index 8534bc5..35839db 100644 --- a/cmd/pullrequest/get.go +++ b/cmd/pullrequest/get.go @@ -39,10 +39,6 @@ func getValidArgs(cmd *cobra.Command, args []string, toComplete string) ([]strin return nil, cobra.ShellCompDirectiveNoFileComp } - if profile.Current == nil { - return []string{}, cobra.ShellCompDirectiveNoFileComp - } - ids, err := prcommon.GetPullRequestIDsWithState(cmd.Context(), cmd, "ALL") if err != nil { cobra.CompErrorln(err.Error()) diff --git a/cmd/pullrequest/merge.go b/cmd/pullrequest/merge.go index e999211..b3a82da 100644 --- a/cmd/pullrequest/merge.go +++ b/cmd/pullrequest/merge.go @@ -42,10 +42,6 @@ func mergeValidArgs(cmd *cobra.Command, args []string, toComplete string) ([]str return nil, cobra.ShellCompDirectiveNoFileComp } - if profile.Current == nil { - return []string{}, cobra.ShellCompDirectiveNoFileComp - } - ids, err := prcommon.GetPullRequestIDsWithState(cmd.Context(), cmd, "OPEN") if err != nil { cobra.CompErrorln(err.Error()) diff --git a/cmd/pullrequest/merge_status.go b/cmd/pullrequest/merge_status.go index 7348828..eb21f65 100644 --- a/cmd/pullrequest/merge_status.go +++ b/cmd/pullrequest/merge_status.go @@ -34,10 +34,6 @@ func mergeStatusValidArgs(cmd *cobra.Command, args []string, toComplete string) return nil, cobra.ShellCompDirectiveNoFileComp } - if profile.Current == nil { - return []string{}, cobra.ShellCompDirectiveNoFileComp - } - ids, err := prcommon.GetPullRequestIDsWithState(cmd.Context(), cmd, "OPEN") if err != nil { cobra.CompErrorln(err.Error()) diff --git a/cmd/pullrequest/patch.go b/cmd/pullrequest/patch.go index c316d54..8aa66b9 100644 --- a/cmd/pullrequest/patch.go +++ b/cmd/pullrequest/patch.go @@ -31,10 +31,6 @@ func validPatchArgs(cmd *cobra.Command, args []string, toComplete string) ([]str return nil, cobra.ShellCompDirectiveNoFileComp } - if profile.Current == nil { - return []string{}, cobra.ShellCompDirectiveNoFileComp - } - ids, err := prcommon.GetPullRequestIDsWithState(cmd.Context(), cmd, "OPEN") if err != nil { cobra.CompErrorln(err.Error()) diff --git a/cmd/pullrequest/remove-request-changes.go b/cmd/pullrequest/remove-request-changes.go index 3b38663..2986338 100644 --- a/cmd/pullrequest/remove-request-changes.go +++ b/cmd/pullrequest/remove-request-changes.go @@ -29,10 +29,6 @@ func removeRequestChangesValidArgs(cmd *cobra.Command, args []string, toComplete return nil, cobra.ShellCompDirectiveNoFileComp } - if profile.Current == nil { - return []string{}, cobra.ShellCompDirectiveNoFileComp - } - ids, err := prcommon.GetPullRequestIDsWithState(cmd.Context(), cmd, "OPEN") if err != nil { cobra.CompErrorln(err.Error()) diff --git a/cmd/pullrequest/request-changes.go b/cmd/pullrequest/request-changes.go index 287ed9d..a079454c 100644 --- a/cmd/pullrequest/request-changes.go +++ b/cmd/pullrequest/request-changes.go @@ -30,10 +30,6 @@ func requestChangesValidArgs(cmd *cobra.Command, args []string, toComplete strin return nil, cobra.ShellCompDirectiveNoFileComp } - if profile.Current == nil { - return []string{}, cobra.ShellCompDirectiveNoFileComp - } - ids, err := prcommon.GetPullRequestIDsWithState(cmd.Context(), cmd, "OPEN") if err != nil { cobra.CompErrorln(err.Error()) diff --git a/cmd/pullrequest/task/create.go b/cmd/pullrequest/task/create.go index 45407e5..f76f2ea 100644 --- a/cmd/pullrequest/task/create.go +++ b/cmd/pullrequest/task/create.go @@ -43,8 +43,8 @@ var createOptions struct { func init() { Command.AddCommand(createCmd) - createOptions.PullRequestID = flags.NewEnumFlagWithFunc("", prcommon.GetPullRequestIDs) - createOptions.CommentID = flags.NewEnumFlagWithFunc("", comment.GetPullRequestCommentIDs) + createOptions.PullRequestID = flags.NewEnumFlagWithFunc(createCmd, "", prcommon.GetPullRequestIDs) + createOptions.CommentID = flags.NewEnumFlagWithFunc(createCmd, "", comment.GetPullRequestCommentIDs) createCmd.Flags().Var(createOptions.PullRequestID, "pullrequest", "Pullrequest to create tasks to") createCmd.Flags().StringVar(&createOptions.Content, "content", "", "Content of the task") createCmd.Flags().Var(createOptions.CommentID, "comment", "Comment ID to create task on") diff --git a/cmd/pullrequest/task/delete.go b/cmd/pullrequest/task/delete.go index f5073c4..8c19897 100644 --- a/cmd/pullrequest/task/delete.go +++ b/cmd/pullrequest/task/delete.go @@ -30,7 +30,7 @@ var deleteOptions struct { func init() { Command.AddCommand(deleteCmd) - deleteOptions.PullRequestID = flags.NewEnumFlagWithFunc("", prcommon.GetPullRequestIDs) + deleteOptions.PullRequestID = flags.NewEnumFlagWithFunc(deleteCmd, "", prcommon.GetPullRequestIDs) deleteCmd.Flags().Var(deleteOptions.PullRequestID, "pullrequest", "Pullrequest to delete comments from") _ = deleteCmd.MarkFlagRequired("pullrequest") _ = deleteCmd.RegisterFlagCompletionFunc(deleteOptions.PullRequestID.CompletionFunc("pullrequest")) diff --git a/cmd/pullrequest/task/get.go b/cmd/pullrequest/task/get.go index 949511d..2fa219c 100644 --- a/cmd/pullrequest/task/get.go +++ b/cmd/pullrequest/task/get.go @@ -30,7 +30,7 @@ var getOptions struct { func init() { Command.AddCommand(getCmd) - getOptions.PullRequestID = flags.NewEnumFlagWithFunc("", prcommon.GetPullRequestIDs) + getOptions.PullRequestID = flags.NewEnumFlagWithFunc(getCmd, "", prcommon.GetPullRequestIDs) getOptions.Columns = flags.NewEnumSliceFlag(columns.Columns()...) getCmd.Flags().Var(getOptions.PullRequestID, "pullrequest", "Pullrequest to get tasks from") getCmd.Flags().Var(getOptions.Columns, "columns", "Comma-separated list of columns to display") diff --git a/cmd/pullrequest/task/list.go b/cmd/pullrequest/task/list.go index fd88aee..5d3689d 100644 --- a/cmd/pullrequest/task/list.go +++ b/cmd/pullrequest/task/list.go @@ -9,7 +9,6 @@ import ( prcommon "github.com/gildas/bitbucket-cli/cmd/pullrequest/common" "github.com/gildas/bitbucket-cli/cmd/repository" "github.com/gildas/go-core" - "github.com/gildas/go-errors" "github.com/gildas/go-flags" "github.com/gildas/go-logger" "github.com/spf13/cobra" @@ -33,7 +32,7 @@ var listOptions struct { func init() { Command.AddCommand(listCmd) - listOptions.PullRequestID = flags.NewEnumFlagWithFunc("", prcommon.GetPullRequestIDs) + listOptions.PullRequestID = flags.NewEnumFlagWithFunc(listCmd, "", prcommon.GetPullRequestIDs) listOptions.Columns = flags.NewEnumSliceFlagWithAllAllowed(columns.Columns()...) listOptions.SortBy = flags.NewEnumFlag(columns.Sorters()...) listCmd.Flags().Var(listOptions.PullRequestID, "pullrequest", "pullrequest to list tasks from") @@ -49,28 +48,25 @@ func init() { func listProcess(cmd *cobra.Command, args []string) (err error) { log := logger.Must(logger.FromContext(cmd.Context())).Child(cmd.Parent().Name(), "list") + ctx := log.ToContext(cmd.Context()) - if profile.Current == nil { - return errors.ArgumentMissing.With("profile") - } - - repository, err := repository.GetRepository(cmd.Context(), cmd) + repository, err := repository.GetRepository(ctx, cmd) if err != nil { return err } + log.Infof("Listing pullrequest tasks for pullrequest %s", listOptions.PullRequestID.Value) + if !common.WhatIf(ctx, cmd, fmt.Sprintf("Listing pullrequest tasks for pullrequest %s", listOptions.PullRequestID.Value)) { + return nil + } + uripath := repository.GetPath(fmt.Sprintf("pullrequests/%s/tasks", listOptions.PullRequestID.Value)) if len(listOptions.Query) > 0 { uripath = fmt.Sprintf("%s?q=%s", uripath, url.QueryEscape(listOptions.Query)) } - log.Infof("Listing pullrequest tasks for pullrequest %s", listOptions.PullRequestID.Value) - if !common.WhatIf(log.ToContext(cmd.Context()), cmd, fmt.Sprintf("Listing pullrequest tasks for pullrequest %s", listOptions.PullRequestID.Value)) { - return nil - } - - tasks, err := profile.GetAll[Task](cmd.Context(), cmd, uripath) + tasks, err := profile.GetAll[Task](ctx, cmd, uripath) if err != nil { return err } @@ -79,5 +75,5 @@ func listProcess(cmd *cobra.Command, args []string) (err error) { return nil } core.Sort(tasks, columns.SortBy(listOptions.SortBy.Value)) - return profile.Current.Print(cmd.Context(), cmd, Tasks(tasks)) + return profile.Current.Print(ctx, cmd, Tasks(tasks)) } diff --git a/cmd/pullrequest/task/update.go b/cmd/pullrequest/task/update.go index 7c5f878..820da42 100644 --- a/cmd/pullrequest/task/update.go +++ b/cmd/pullrequest/task/update.go @@ -40,11 +40,11 @@ var updateOptions struct { func init() { Command.AddCommand(updateCmd) - updateOptions.PullRequestID = flags.NewEnumFlagWithFunc("", prcommon.GetPullRequestIDs) - updateOptions.State = flags.NewEnumFlag("", "RESOLVED", "UNRESOLVED") + updateOptions.PullRequestID = flags.NewEnumFlagWithFunc(updateCmd, "", prcommon.GetPullRequestIDs) + updateOptions.State = flags.NewEnumFlag("RESOLVED", "UNRESOLVED") updateCmd.Flags().Var(updateOptions.PullRequestID, "pullrequest", "Pullrequest to update tasks to") updateCmd.Flags().StringVar(&updateOptions.Content, "content", "", "Updated content of the task") - updateCmd.Flags().Var(updateOptions.State, "state", "Updated state of the task. Can be one of needs_work, complete or pending") + updateCmd.Flags().Var(updateOptions.State, "state", "Updated state of the task. Can be one of RESOLVED or UNRESOLVED") _ = updateCmd.MarkFlagRequired("pullrequest") _ = updateCmd.RegisterFlagCompletionFunc(updateOptions.PullRequestID.CompletionFunc("pullrequest")) _ = updateCmd.RegisterFlagCompletionFunc(updateOptions.State.CompletionFunc("state")) @@ -83,7 +83,7 @@ func updateProcess(cmd *cobra.Command, args []string) error { Raw: updateOptions.Content, } } - if updateOptions.State != nil && len(updateOptions.State.Value) > 0 { + if cmd.Flags().Changed("state") && len(updateOptions.State.Value) > 0 { taskUpdator.State = updateOptions.State.Value } diff --git a/cmd/pullrequest/unapprove.go b/cmd/pullrequest/unapprove.go index 9f76cf1..bbe75f6 100644 --- a/cmd/pullrequest/unapprove.go +++ b/cmd/pullrequest/unapprove.go @@ -27,10 +27,6 @@ func unapproveValidArgs(cmd *cobra.Command, args []string, toComplete string) ([ return nil, cobra.ShellCompDirectiveNoFileComp } - if profile.Current == nil { - return []string{}, cobra.ShellCompDirectiveNoFileComp - } - ids, err := prcommon.GetPullRequestIDsWithState(cmd.Context(), cmd, "OPEN") if err != nil { cobra.CompErrorln(err.Error()) diff --git a/cmd/pullrequest/update.go b/cmd/pullrequest/update.go index d2286f4..7308b5d 100644 --- a/cmd/pullrequest/update.go +++ b/cmd/pullrequest/update.go @@ -41,9 +41,9 @@ var updateOptions struct { func init() { Command.AddCommand(updateCmd) - updateOptions.Destination = flags.NewEnumFlagWithFunc("", branch.GetBranchNames) - updateOptions.AddReviewers = flags.NewEnumSliceFlagWithAllAllowedAndFunc(GetReviewerNicknames) - updateOptions.RemoveReviewers = flags.NewEnumSliceFlagWithAllAllowedAndFunc(GetReviewerNicknames) + updateOptions.Destination = flags.NewEnumFlagWithFunc(updateCmd, "", branch.GetBranchNames) + updateOptions.AddReviewers = flags.NewEnumSliceFlagWithAllAllowedAndFunc(updateCmd, GetReviewerNicknames) + updateOptions.RemoveReviewers = flags.NewEnumSliceFlagWithAllAllowedAndFunc(updateCmd, GetReviewerNicknames) updateCmd.Flags().StringVar(&updateOptions.Title, "title", "", "Title of the pullrequest") updateCmd.Flags().StringVar(&updateOptions.Description, "description", "", "Description of the pullrequest") @@ -62,10 +62,6 @@ func updateValidArgs(cmd *cobra.Command, args []string, toComplete string) ([]st return nil, cobra.ShellCompDirectiveNoFileComp } - if profile.Current == nil { - return []string{}, cobra.ShellCompDirectiveNoFileComp - } - ids, err := prcommon.GetPullRequestIDsWithState(cmd.Context(), cmd, "ALL") if err != nil { cobra.CompErrorln(err.Error()) diff --git a/cmd/repository/create.go b/cmd/repository/create.go index 316840b..acfadf9 100644 --- a/cmd/repository/create.go +++ b/cmd/repository/create.go @@ -45,7 +45,7 @@ var createOptions struct { func init() { Command.AddCommand(createCmd) - createOptions.Project = flags.NewEnumFlagWithFunc("", project.GetProjectKeys) + createOptions.Project = flags.NewEnumFlagWithFunc(createCmd, "", project.GetProjectKeys) createOptions.ForkPolicy = flags.NewEnumFlag("allow_forks", "+no_public_forks", "no_forks") createCmd.Flags().Var(createOptions.Project, "project", "Project to create repositories from") createCmd.Flags().StringVar(&createOptions.Name, "name", "", "Name of the repository") diff --git a/cmd/repository/fork.go b/cmd/repository/fork.go index c26b0d0..96020f6 100644 --- a/cmd/repository/fork.go +++ b/cmd/repository/fork.go @@ -43,7 +43,7 @@ var forkOptions struct { func init() { Command.AddCommand(forkCmd) - forkOptions.Project = flags.NewEnumFlagWithFunc("", project.GetProjectKeys) + forkOptions.Project = flags.NewEnumFlagWithFunc(forkCmd, "", project.GetProjectKeys) forkOptions.ForkPolicy = flags.NewEnumFlag("allow_forks", "+no_public_forks", "no_forks") forkCmd.Flags().Var(forkOptions.Project, "project", "Project to fork repositories from") forkCmd.Flags().StringVar(&forkOptions.Name, "name", "", "Name of the repository") diff --git a/cmd/repository/repository.go b/cmd/repository/repository.go index 99a1b5c..e7e482b 100644 --- a/cmd/repository/repository.go +++ b/cmd/repository/repository.go @@ -31,9 +31,9 @@ type Repository struct { Owner user.User `json:"owner,omitempty" mapstructure:"owner"` Workspace *workspace.Workspace `json:"workspace,omitempty" mapstructure:"workspace"` Project project.Project `json:"project,omitempty" mapstructure:"project"` - HasIssues bool `json:"has_issues" mapstructure:"has_issues"` - HasWiki bool `json:"has_wiki" mapstructure:"has_wiki"` - IsPrivate bool `json:"is_private" mapstructure:"is_private"` + HasIssues bool `json:"has_issues,omitempty" mapstructure:"has_issues"` + HasWiki bool `json:"has_wiki,omitempty" mapstructure:"has_wiki"` + IsPrivate bool `json:"is_private,omitempty" mapstructure:"is_private"` ForkPolicy string `json:"fork_policy,omitempty" mapstructure:"fork_policy"` Size int64 `json:"size,omitempty" mapstructure:"size"` Language string `json:"language,omitempty" mapstructure:"language"` diff --git a/cmd/repository/update.go b/cmd/repository/update.go index f3c77c5..ec9c334 100644 --- a/cmd/repository/update.go +++ b/cmd/repository/update.go @@ -46,7 +46,7 @@ var updateOptions struct { func init() { Command.AddCommand(updateCmd) - updateOptions.Project = flags.NewEnumFlagWithFunc("", project.GetProjectKeys) + updateOptions.Project = flags.NewEnumFlagWithFunc(updateCmd, "", project.GetProjectKeys) updateOptions.ForkPolicy = flags.NewEnumFlag("allow_forks", "+no_public_forks", "no_forks") updateCmd.Flags().Var(updateOptions.Project, "project", "Project to update repositories from") updateCmd.Flags().StringVar(&updateOptions.Name, "name", "", "Name of the repository") @@ -59,7 +59,7 @@ func init() { updateCmd.MarkFlagsMutuallyExclusive("private", "public") _ = updateCmd.RegisterFlagCompletionFunc(updateOptions.Project.CompletionFunc("project")) updateCmd.Flags().MarkHidden("repository") - createCmd.SetHelpFunc(func(cmd *cobra.Command, args []string) { + updateCmd.SetHelpFunc(func(cmd *cobra.Command, args []string) { cmd.Flags().MarkHidden("repository") cmd.Parent().HelpFunc()(cmd, args) }) diff --git a/cmd/root.go b/cmd/root.go index 8ae32e8..ae4594b 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -5,13 +5,12 @@ import ( "fmt" "os" "path/filepath" - "runtime" - "strings" "github.com/gildas/bitbucket-cli/cmd/artifact" "github.com/gildas/bitbucket-cli/cmd/branch" "github.com/gildas/bitbucket-cli/cmd/cache" "github.com/gildas/bitbucket-cli/cmd/commit" + "github.com/gildas/bitbucket-cli/cmd/common" "github.com/gildas/bitbucket-cli/cmd/component" "github.com/gildas/bitbucket-cli/cmd/gpg-key" "github.com/gildas/bitbucket-cli/cmd/issue" @@ -26,9 +25,7 @@ import ( "github.com/gildas/bitbucket-cli/cmd/workspace" "github.com/gildas/go-core" "github.com/gildas/go-flags" - "github.com/gildas/go-logger" "github.com/spf13/cobra" - "github.com/spf13/viper" ) // RootOptions describes the options for the application @@ -74,7 +71,7 @@ func init() { cobra.CheckErr(err) // Global flags - CmdOptions.Workspace = flags.NewEnumFlagWithFunc("", workspace.GetWorkspaceAllowedSlugs) + CmdOptions.Workspace = flags.NewEnumFlagWithFunc(RootCmd, "", workspace.GetWorkspaceAllowedSlugs) CmdOptions.OutputFormat = flags.EnumFlag{Allowed: []string{"csv", "json", "yaml", "table", "tsv"}, Value: core.GetEnvAsString("BB_OUTPUT_FORMAT", "")} RootCmd.PersistentFlags().StringVar(&CmdOptions.ConfigFile, "config", core.GetEnvAsString("BB_CONFIG", ""), "config file (default is .env, "+filepath.Join(configDir, "bitbucket", "config-cli.yml")) RootCmd.PersistentFlags().StringVarP(&CmdOptions.ProfileName, "profile", "p", core.GetEnvAsString("BB_PROFILE", ""), "Profile to use. Overrides the default profile") @@ -115,70 +112,10 @@ func init() { RootCmd.AddCommand(cache.Command) RootCmd.SilenceUsage = true // Do not show usage when an error occurs - cobra.OnInitialize(initConfig) -} - -// initConfig reads in config file and ENV variables if set. -func initConfig() { - log := logger.Must(logger.FromContext(RootCmd.Context())) - - if len(CmdOptions.LogDestination) > 0 { - log.ResetDestinations(CmdOptions.LogDestination) - } - if CmdOptions.Debug { - log.SetFilterLevel(logger.DEBUG) - } - - log.Infof("%s", strings.Repeat("-", 80)) - log.Infof("Starting %s v%s (%s)", RootCmd.Name(), RootCmd.Version, runtime.GOARCH) - log.Infof("Log Destination: %s", log) - - viper.SetConfigType("yaml") - if len(CmdOptions.ConfigFile) > 0 { // Use config file from the flag. - viper.SetConfigFile(CmdOptions.ConfigFile) - } else if configDir, _ := os.UserConfigDir(); len(configDir) > 0 { - viper.AddConfigPath(filepath.Join(configDir, "bitbucket")) - viper.SetConfigName("config-cli.yml") - } else { // Old fashion configuration file in $HOME - homeDir, err := os.UserHomeDir() - cobra.CheckErr(err) - viper.AddConfigPath(homeDir) - viper.SetConfigName(".bitbucket-cli") - } - - // Read the config file - err := viper.ReadInConfig() - if verr, ok := err.(viper.ConfigFileNotFoundError); ok { - log.Warnf("Config file not found: %s", verr) - if len(CmdOptions.ProfileName) > 0 { - log.Fatalf("Profile %s not found (missing config file)", CmdOptions.ProfileName) - fmt.Fprintf(os.Stderr, "Profile %s not found (missing config file)\n", CmdOptions.ProfileName) + cobra.OnInitialize(func() { + if err := common.Initialize(RootCmd); err != nil { + fmt.Fprintf(os.Stderr, "Failed to initialize: %s\n", err) os.Exit(1) } - } else if err != nil { - log.Fatalf("Failed to read config file: %s", err) - fmt.Fprintf(os.Stderr, "Failed to read config file: %s\n", err) - os.Exit(1) - } else { - log.Infof("Config File: %s", viper.ConfigFileUsed()) - if err := profile.Profiles.Load(RootCmd.Context()); err != nil { - log.Fatalf("Failed to load profiles: %s", err) - fmt.Fprintf(os.Stderr, "Failed to load profiles: %s\n", err) - os.Exit(1) - } - if len(CmdOptions.ProfileName) > 0 { - var found bool - - if profile.Current, found = profile.Profiles.Find(CmdOptions.ProfileName); !found { - log.Fatalf("Profile %s not found", CmdOptions.ProfileName) - fmt.Fprintf(os.Stderr, "Profile %s not found in %s\n", CmdOptions.ProfileName, viper.ConfigFileUsed()) - os.Exit(1) - } - } else { - profile.Current = profile.Profiles.Current(RootCmd.Context()) - } - if profile.Current != nil { - log.Record("profile", profile.Current).Infof("Current Profile: %s", profile.Current) - } - } + }) } diff --git a/cmd/tag/create.go b/cmd/tag/create.go index e3b86d0..15e4af5 100644 --- a/cmd/tag/create.go +++ b/cmd/tag/create.go @@ -30,7 +30,7 @@ var createOptions struct { func init() { Command.AddCommand(createCmd) - createOptions.Commit = flags.NewEnumFlagWithFunc("latest", commit.GetCommitHashes) + createOptions.Commit = flags.NewEnumFlagWithFunc(createCmd, "latest", commit.GetCommitHashes) createCmd.Flags().StringVar(&createOptions.Name, "name", "", "Name of the tag") createCmd.Flags().StringVar(&createOptions.Message, "message", "", "Message of the tag") createCmd.Flags().Var(createOptions.Commit, "commit", "Target commit hash for the tag. Defaults to the latest commit") diff --git a/cmd/tag/list.go b/cmd/tag/list.go index 9fd1f04..b35783e 100644 --- a/cmd/tag/list.go +++ b/cmd/tag/list.go @@ -1,8 +1,6 @@ package tag import ( - "fmt" - "github.com/gildas/bitbucket-cli/cmd/common" "github.com/gildas/bitbucket-cli/cmd/profile" "github.com/gildas/bitbucket-cli/cmd/repository" @@ -46,7 +44,7 @@ func listProcess(cmd *cobra.Command, args []string) (err error) { } log.Infof("Listing all tags for repository: %s", repository) - if !common.WhatIf(log.ToContext(cmd.Context()), cmd, fmt.Sprintf("Showing tags")) { + if !common.WhatIf(log.ToContext(cmd.Context()), cmd, "Showing tags") { return nil } tags, err := GetTags(log.ToContext(cmd.Context()), cmd) diff --git a/cmd/workspace/permission/list.go b/cmd/workspace/permission/list.go index 81bf399..0418c83 100644 --- a/cmd/workspace/permission/list.go +++ b/cmd/workspace/permission/list.go @@ -8,7 +8,6 @@ import ( "github.com/gildas/bitbucket-cli/cmd/profile" wkcommon "github.com/gildas/bitbucket-cli/cmd/workspace/common" "github.com/gildas/go-core" - "github.com/gildas/go-errors" "github.com/gildas/go-flags" "github.com/gildas/go-logger" "github.com/spf13/cobra" @@ -61,8 +60,9 @@ func listValidArgs(cmd *cobra.Command, args []string, toComplete string) ([]stri func listProcess(cmd *cobra.Command, args []string) error { log := logger.Must(logger.FromContext(cmd.Context())).Child(cmd.Parent().Name(), "list") - if profile.Current == nil { - return errors.ArgumentMissing.With("profile") + log.Infof("Listing all permissions from workspace %s", args[0]) + if !common.WhatIf(log.ToContext(cmd.Context()), cmd, fmt.Sprintf("Showing permissions for workspace %s with profile %s", args[0], profile.Current)) { + return nil } var uripath string @@ -73,11 +73,6 @@ func listProcess(cmd *cobra.Command, args []string) error { uripath = fmt.Sprintf("/workspaces/%s/permissions", args[0]) } - log.Infof("Listing all permissions from workspace %s with profile %s", args[0], profile.Current) - if !common.WhatIf(log.ToContext(cmd.Context()), cmd, fmt.Sprintf("Showing permissions for workspace %s with profile %s", args[0], profile.Current)) { - return nil - } - permissions, err := profile.GetAll[Permission](cmd.Context(), cmd, uripath) if err != nil { return err diff --git a/go.mod b/go.mod index cda6898..04a5330 100644 --- a/go.mod +++ b/go.mod @@ -3,14 +3,16 @@ module github.com/gildas/bitbucket-cli go 1.26 require ( + github.com/AlekSi/gocov-xml v1.2.0 + github.com/axw/gocov v1.2.1 github.com/briandowns/spinner v1.23.2 github.com/gildas/go-cache v0.2.2 github.com/gildas/go-core v0.6.4 github.com/gildas/go-errors v0.4.0 - github.com/gildas/go-flags v0.4.2 - github.com/gildas/go-logger v1.9.3 + github.com/gildas/go-flags v0.5.0 + github.com/gildas/go-logger v1.9.5 github.com/gildas/go-request v0.9.19 - github.com/go-git/go-git/v5 v5.18.0 + github.com/go-git/go-git/v5 v5.19.1 github.com/google/uuid v1.6.0 github.com/joho/godotenv v1.5.1 github.com/kataras/tablewriter v0.0.0-20180708051242-e063d29b7c23 @@ -28,8 +30,8 @@ require ( cloud.google.com/go/auth v0.20.0 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/compute/metadata v0.9.0 // indirect - cloud.google.com/go/logging v1.17.0 // indirect - cloud.google.com/go/longrunning v0.12.0 // indirect + cloud.google.com/go/logging v1.18.0 // indirect + cloud.google.com/go/longrunning v1.0.0 // indirect dario.cat/mergo v1.0.2 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/ProtonMail/go-crypto v1.4.1 // indirect @@ -44,22 +46,22 @@ require ( github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.10.1 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-git/go-billy/v5 v5.8.0 // indirect + github.com/go-git/go-billy/v5 v5.9.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/godbus/dbus/v5 v5.2.2 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/google/s2a-go v0.1.9 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.15 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.16 // indirect github.com/googleapis/gax-go/v2 v2.22.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/kevinburke/ssh_config v1.6.0 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect - github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-colorable v0.1.15 // indirect github.com/mattn/go-isatty v0.0.22 // indirect - github.com/mattn/go-runewidth v0.0.23 // indirect + github.com/mattn/go-runewidth v0.0.24 // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/pelletier/go-toml/v2 v2.3.1 // indirect github.com/pjbgf/sha1cd v0.6.0 // indirect @@ -74,26 +76,26 @@ require ( github.com/subosito/gotenv v1.6.0 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect - go.opentelemetry.io/otel v1.43.0 // indirect - go.opentelemetry.io/otel/metric v1.43.0 // indirect - go.opentelemetry.io/otel/trace v1.43.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 // indirect + go.opentelemetry.io/otel v1.44.0 // indirect + go.opentelemetry.io/otel/metric v1.44.0 // indirect + go.opentelemetry.io/otel/trace v1.44.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.50.0 // indirect - golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f // indirect - golang.org/x/net v0.53.0 // indirect + golang.org/x/crypto v0.52.0 // indirect + golang.org/x/exp v0.0.0-20260603202125-055de637280b // indirect + golang.org/x/net v0.55.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.43.0 // indirect - golang.org/x/term v0.42.0 // indirect - golang.org/x/text v0.36.0 // indirect + golang.org/x/sys v0.45.0 // indirect + golang.org/x/term v0.43.0 // indirect + golang.org/x/text v0.37.0 // indirect golang.org/x/time v0.15.0 // indirect - google.golang.org/api v0.278.0 // indirect - google.golang.org/genproto v0.0.0-20260504160031-60b97b32f348 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260504160031-60b97b32f348 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260504160031-60b97b32f348 // indirect - google.golang.org/grpc v1.81.0 // indirect + google.golang.org/api v0.283.0 // indirect + google.golang.org/genproto v0.0.0-20260526163538-3dc84a4a5aaa // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect + google.golang.org/grpc v1.81.1 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect ) diff --git a/go.sum b/go.sum index b34fb76..b0ef052 100644 --- a/go.sum +++ b/go.sum @@ -6,14 +6,16 @@ cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIi cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= -cloud.google.com/go/iam v1.10.0 h1:cWWt8u8jXv3MzpvBmQgNClvvbVCRukruCJAnoK3fIJY= -cloud.google.com/go/iam v1.10.0/go.mod h1:KP+nKGugNJW4LcLx1uEZcq1ok5sQHFaQehQNl4QDgV4= -cloud.google.com/go/logging v1.17.0 h1:rUFekZYwHiKElXCyz3zYBGz4BOeIqzgCKxVLdgrZ5mY= -cloud.google.com/go/logging v1.17.0/go.mod h1:ZGKnpBaURITh+g/uom2VhbiFoFWvejcrHPDhxFtU/gI= -cloud.google.com/go/longrunning v0.12.0 h1:wLv2hXvID9zHejLtcPo1B0JBjErnwZCYAPKSTa65xpY= -cloud.google.com/go/longrunning v0.12.0/go.mod h1:8nqFBPOO1U/XkhWl0I19AMZEphrHi73VNABIpKYaTwM= +cloud.google.com/go/iam v1.11.0 h1:KieQ9Pb+LLPak1O3Rv3GgCxhnmkYf7Xyh0P5HfF1jFM= +cloud.google.com/go/iam v1.11.0/go.mod h1:KP+nKGugNJW4LcLx1uEZcq1ok5sQHFaQehQNl4QDgV4= +cloud.google.com/go/logging v1.18.0 h1:KhzZq+1cSkPH9YUaKLLhLtQxIHitVayBmk0sGfoM9+k= +cloud.google.com/go/logging v1.18.0/go.mod h1:ZGKnpBaURITh+g/uom2VhbiFoFWvejcrHPDhxFtU/gI= +cloud.google.com/go/longrunning v1.0.0 h1:lwzWEYD8+NkYV7dhexOz6kmlvajZA70+bW/xMhRVVdY= +cloud.google.com/go/longrunning v1.0.0/go.mod h1:8nqFBPOO1U/XkhWl0I19AMZEphrHi73VNABIpKYaTwM= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= +github.com/AlekSi/gocov-xml v1.2.0 h1:TGx+qVSgBn655Ejv8b2mgPIGvIa8ZxGFSPXuWf0J3Vw= +github.com/AlekSi/gocov-xml v1.2.0/go.mod h1:g1dRVOCHjKkMtlPfW6BokJ/qxoeZ1uPNAK7A/ii3CUo= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= @@ -23,6 +25,9 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/axw/gocov v1.1.0/go.mod h1:H9G4tivgdN3pYSSVrTFBr6kGDCmAkgbJhtxFzAvgcdw= +github.com/axw/gocov v1.2.1 h1:bqtQDBC2tQWcPzTYIVxK0EDCfNRLwsk4NZ0+GB4hX8Q= +github.com/axw/gocov v1.2.1/go.mod h1:l11/vZBBKfQEE+42jF47myjDrRZHM+hR+XgGjI6FopU= github.com/briandowns/spinner v1.23.2 h1:Zc6ecUnI+YzLmJniCfDNaMbW0Wid1d5+qcTq4L2FW8w= github.com/briandowns/spinner v1.23.2/go.mod h1:LaZeM4wm2Ywy6vO571mvhQNRcWfRUnXOs0RcKV0wYKM= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= @@ -67,22 +72,22 @@ github.com/gildas/go-core v0.6.4 h1:l+4zBiQCu1gKG+PYL0HqV4UcD1x5XgSUxnsOOrfT+n0= github.com/gildas/go-core v0.6.4/go.mod h1:m654RMk7tOAXG+MCkhguNrptmWa9qwNRUxZRh7fcLu0= github.com/gildas/go-errors v0.4.0 h1:pJ5km8sKrOm5MQd/0g+y4pSKI38YBwPs5NkwSsU8bkM= github.com/gildas/go-errors v0.4.0/go.mod h1:a05AfO2MLgb8OTPj5l/HZRrBhfjxnwWyEKXgyeoKjtg= -github.com/gildas/go-flags v0.4.2 h1:0DtShGeoBkXQCJkj1l4Ej0kkHmQ+WGaRQ4MH9Hc+s10= -github.com/gildas/go-flags v0.4.2/go.mod h1:W41rY6doPgywnRENJ1PtEDy4uf1z80zehz/FkxtAnpQ= -github.com/gildas/go-logger v1.9.3 h1:WlO7qoOfvb1yNfTkN/ZJ0iMB/mqXzBxwdOx810L+Mlk= -github.com/gildas/go-logger v1.9.3/go.mod h1:t2MDXBJ0nqAMDHS9JafqLWm2OKBmWl/qMx3xDFTlOu4= +github.com/gildas/go-flags v0.5.0 h1:Y4e2ql6wGFsSr+o4wdUC1vr5Rk+FIyJyiJG32IpXOrU= +github.com/gildas/go-flags v0.5.0/go.mod h1:qyFaL181I8KvBvDUqj6b8kZ299u9LLY2bGcfqsvoy1E= +github.com/gildas/go-logger v1.9.5 h1:5MDK8R3PG+OGylu4sHdJi+zV+W6TJHwVMpHzwOqzamk= +github.com/gildas/go-logger v1.9.5/go.mod h1:k4zAm14pVkxhGgcxdpE41UyTRjCph2gI5ZX2x6Psp4A= github.com/gildas/go-request v0.9.19 h1:pA3vZcsl6o/E7XO4RVW76LZ11ldwYR0WyzukD4sXXhQ= github.com/gildas/go-request v0.9.19/go.mod h1:9o4Y3y1g5zLT4+EyOdw69Ke52ftWwG1mrfNNrT5YjKY= github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.8.0 h1:I8hjc3LbBlXTtVuFNJuwYuMiHvQJDq1AT6u4DwDzZG0= -github.com/go-git/go-billy/v5 v5.8.0/go.mod h1:RpvI/rw4Vr5QA+Z60c6d6LXH0rYJo0uD5SqfmrrheCY= +github.com/go-git/go-billy/v5 v5.9.0 h1:jItGXszUDRtR/AlferWPTMN4j38BQ88XnXKbilmmBPA= +github.com/go-git/go-billy/v5 v5.9.0/go.mod h1:jCnQMLj9eUgGU7+ludSTYoZL/GGmii14RxKFj7ROgHw= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.18.0 h1:O831KI+0PR51hM2kep6T8k+w0/LIAD490gvqMCvL5hM= -github.com/go-git/go-git/v5 v5.18.0/go.mod h1:pW/VmeqkanRFqR6AljLcs7EA7FbZaN5MQqO7oZADXpo= +github.com/go-git/go-git/v5 v5.19.1 h1:nX27AnaU43/K5bKktKwgBmR9lawoYVe1Ckg0rgzzN00= +github.com/go-git/go-git/v5 v5.19.1/go.mod h1:Pb1v0c7/g8aGQJwx9Us09W85yGoyvSwuhEGMH7zjDKQ= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -102,8 +107,8 @@ github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.15 h1:xolVQTEXusUcAA5UgtyRLjelpFFHWlPQ4XfWGc7MBas= -github.com/googleapis/enterprise-certificate-proxy v0.3.15/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg= +github.com/googleapis/enterprise-certificate-proxy v0.3.16 h1:F/VPrx0YPBdksZJQdCAp0WUsqnNmZpUZszzfYt0M5Dw= +github.com/googleapis/enterprise-certificate-proxy v0.3.16/go.mod h1:9Yb0eAkH/Xqhvv3zbeKf/+wMJqCeocWc6KIhDvEAuYE= github.com/googleapis/gax-go/v2 v2.22.0 h1:PjIWBpgGIVKGoCXuiCoP64altEJCj3/Ei+kSU5vlZD4= github.com/googleapis/gax-go/v2 v2.22.0/go.mod h1:irWBbALSr0Sk3qlqb9SyJ1h68WjgeFuiOzI4Rqw5+aY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -125,12 +130,12 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= -github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY= +github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4= github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= -github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw= -github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +github.com/mattn/go-runewidth v0.0.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/ay2DU= +github.com/mattn/go-runewidth v0.0.24/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= @@ -193,63 +198,69 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 h1:0Qx7VGBacMm9ZENQ7TnNObTYI4ShC+lHI16seduaxZo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0/go.mod h1:Sje3i3MjSPKTSPvVWCaL8ugBzJwik3u4smCjUeuupqg= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo= -go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= -go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= -go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= -go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= -go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= -go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= -go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= -go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= -go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= -go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0 h1:2yEATaop1/a1I4psnSLgWVPLWwCzkqWakgJy7xTDVy0= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0/go.mod h1:D7J12YRapIekYyPWgGPlA/23pRmpSEZC5xJC/TTLI9U= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 h1:8tvICD4vSTOOsNrsI4Ljf6C+6UKvpTEH5XY3JMoyPoo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0/go.mod h1:z9+yiacE0IHRqM4qFfkbt/JYlmYXgss8GY/jXoNuPJI= +go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= +go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= +go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= +go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= +go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= +go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0= +go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI= +go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= +go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= +go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= -golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= -golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM= -golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= +golang.org/x/exp v0.0.0-20260603202125-055de637280b h1:v1uXiEBHo8QA0LiGCo7UgHMzHT4Kdfpl2zmtH5vaP1Q= +golang.org/x/exp v0.0.0-20260603202125-055de637280b/go.mod h1:d2fgXJLVs4dYDHUk5lwMIfzRzSrWCfGZb0ZqeLa/Vcw= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= -golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= -golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY= -golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= -golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= -google.golang.org/api v0.278.0 h1:W7jiRvRi53VYFfZ/HoZjQBtJk7gOFbHD8ot1RzVZU6E= -google.golang.org/api v0.278.0/go.mod h1:B9TqLBwJqVjp1mtt7WeoQwWRwvu/400y5lETOql+giQ= -google.golang.org/genproto v0.0.0-20260504160031-60b97b32f348 h1:JjVGDZYWkJWZcxveJGzfkXC5myDVWAd4dZdgbzrDUv8= -google.golang.org/genproto v0.0.0-20260504160031-60b97b32f348/go.mod h1:95PqD4xM+AdOcBGsmgfaofXsiA37uXDtDufVbntT3TU= -google.golang.org/genproto/googleapis/api v0.0.0-20260504160031-60b97b32f348 h1:U8orV30l6KpDsi9dxU0CoJZGbjS8EEpw+6ba+XwGPQA= -google.golang.org/genproto/googleapis/api v0.0.0-20260504160031-60b97b32f348/go.mod h1:Yzdzr5OOZFgSsEV2D/Xi9NL3bszpXFAg0hFJiRohcD8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260504160031-60b97b32f348 h1:pfIbyB44sWzHiCpRqIen67ZQnVXSfIxWrqUMk1qwODE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260504160031-60b97b32f348/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= -google.golang.org/grpc v1.81.0 h1:W3G9N3KQf3BU+YuCtGKJk0CmxQNbAISICD/9AORxLIw= -google.golang.org/grpc v1.81.0/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= +google.golang.org/api v0.283.0 h1:0lkp8u0MPwJVHqRL+nJlMAoZVVzbmiXmFHXMOTmSPik= +google.golang.org/api v0.283.0/go.mod h1:6Wssta4c5n9qHq5CBhmlai5h/PUa1djdDAIhYEHyvcM= +google.golang.org/genproto v0.0.0-20260526163538-3dc84a4a5aaa h1:mfj8IS4EA4VAR9a6QDVxTQkLY64iBybb5QI1B4pXrpE= +google.golang.org/genproto v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:fuT7yonGw1Iq2oa+YC0fyqPPQJkgo/54gPNC6VitOkI= +google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8= +google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ= +google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/packaging/chocolatey/bitbucket-cli.nuspec b/packaging/chocolatey/bitbucket-cli.nuspec index 8805086..663a078 100644 --- a/packaging/chocolatey/bitbucket-cli.nuspec +++ b/packaging/chocolatey/bitbucket-cli.nuspec @@ -3,7 +3,7 @@ bitbucket-cli - 0.18.1 + 0.18.2 Gildas Cherruel bitbucket-cli (Install) Gildas Cherruel @@ -18,7 +18,8 @@ Command line interface for Bitbucket The Bitbucket Command Line Interface brings the power of the Bitbucket platform to your command line. Creating and merging Pull Requests, cloning repositories, and more are now just a few keystrokes away. -- Bug fixes +- Added pending flag for pullrequest comment create/update +- flags with enums now enforce valid values diff --git a/packaging/chocolatey/tools/VERIFICATION.txt b/packaging/chocolatey/tools/VERIFICATION.txt index 8102ce6..092d1a3 100644 --- a/packaging/chocolatey/tools/VERIFICATION.txt +++ b/packaging/chocolatey/tools/VERIFICATION.txt @@ -8,13 +8,13 @@ I, Gildas Cherruel, am the author of this package that lives on https://github.c The Package can be verified by checking the following: 1. Go to: - https://github.com/gildas/bitbucket-cli/releases/download/v0.18.1/bitbucket-cli-0.18.1-windows-amd64.7z + https://github.com/gildas/bitbucket-cli/releases/download/v0.18.2/bitbucket-cli-0.18.2-windows-amd64.7z to download the application binary. 2. You can verify the integrity of the downloaded file by checking its SHA256 hash: - checksum: b3740789859dfd721ca473feafcf8b945558328b6703f05061a4f6f1adf46760 + checksum: 5a3e4d7a8ba6bf9f6fc77148fce46d6d7accab1d7369a448ce87588d7668d65f with the PowerShell function `Get-FileHash` or the Chocolatey `checksum.exe` utility. diff --git a/packaging/chocolatey/tools/chocolateyinstall.ps1 b/packaging/chocolatey/tools/chocolateyinstall.ps1 index 75abc34..b386157 100644 --- a/packaging/chocolatey/tools/chocolateyinstall.ps1 +++ b/packaging/chocolatey/tools/chocolateyinstall.ps1 @@ -5,9 +5,9 @@ $packageArgs = @{ packageName = $env:ChocolateyPackageName unzipLocation = $toolsDir fileType = 'exe' - file64 = "$toolsDir\bitbucket-cli-0.18.1-windows-amd64.7z" + file64 = "$toolsDir\bitbucket-cli-0.18.2-windows-amd64.7z" softwareName = 'bitbucket-cli*' - checksum64 = 'b3740789859dfd721ca473feafcf8b945558328b6703f05061a4f6f1adf46760' + checksum64 = '5a3e4d7a8ba6bf9f6fc77148fce46d6d7accab1d7369a448ce87588d7668d65f' checksumType64= 'sha256' } diff --git a/packaging/package-description.xml b/packaging/package-description.xml index e83a33b..99109dd 100644 --- a/packaging/package-description.xml +++ b/packaging/package-description.xml @@ -11,6 +11,7 @@ Note: This snapcraft is not affiliated with Atlassian. + diff --git a/packaging/snap/snapcraft.yaml b/packaging/snap/snapcraft.yaml index b48d6e1..3d38f1c 100644 --- a/packaging/snap/snapcraft.yaml +++ b/packaging/snap/snapcraft.yaml @@ -13,7 +13,7 @@ issues: https://github.com/gildas/bitbucket-cli/issues contact: - https://github.com/gildas/bitbucket-cli license: MIT -version: 0.18.1 +version: 0.18.2 base: core24 grade: stable confinement: strict diff --git a/tools/tools.go b/tools/tools.go new file mode 100644 index 0000000..cc156f2 --- /dev/null +++ b/tools/tools.go @@ -0,0 +1,12 @@ +//go:build tools +// +build tools + +package tools + +import ( + // github.com/AlekSi/gocov-xml is package main (no importable API). + // This blank import is syntactically valid for go mod tidy pinning only — + // do not build with -tags tools. + _ "github.com/AlekSi/gocov-xml" + _ "github.com/axw/gocov" +) diff --git a/version.go b/version.go index b21e352..b8df014 100644 --- a/version.go +++ b/version.go @@ -12,7 +12,7 @@ var branch string var stamp string // VERSION is the version of this application -var VERSION = "0.18.1" +var VERSION = "0.18.2" // APP is the name of the application const APP = "bb" From 6eee71a1b932381a361c6f4ba7c8bd65b16dfd59 Mon Sep 17 00:00:00 2001 From: Brian Summa Date: Sat, 6 Jun 2026 14:31:14 -0500 Subject: [PATCH 4/5] Fix Author.Raw omission in MarshalJSON and add guard to coverage-report Use Author.IsEmpty() instead of an ad-hoc Type/UserID check so authors with only a Raw field (non-Bitbucket-registered users) are not silently dropped from commit JSON output. Add the same [ -x ] existence guard to coverage-report that the test target already uses, so a failed tool installation fails with a clear message rather than a cryptic silent error. Co-Authored-By: Claude Sonnet 4.6 --- Makefile | 4 +++- cmd/commit/commit.go | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index a3d3567..5ed9fa7 100644 --- a/Makefile +++ b/Makefile @@ -205,7 +205,9 @@ test tests: ; $(info $(M) Running $(NAME:%=% )tests...) @ ## Run the Unit Tests fi coverage-report: $(COVERAGE_OUT) | coverage-tools; @ ## Generate XML coverage report (requires gocov/gocov-xml) - $Q $(GOCOV) convert $(COVERAGE_OUT) | $(GOCOVXML) > $(COVERAGE_XML) + $Q if [ -x "$(GOCOV)" ] && [ -x "$(GOCOVXML)" ]; then \ + $(GOCOV) convert $(COVERAGE_OUT) | $(GOCOVXML) > $(COVERAGE_XML); \ + fi test-ci:; @ ## Run the unit tests continuously $Q $(MAKE) --no-print-directory watch run="make test" diff --git a/cmd/commit/commit.go b/cmd/commit/commit.go index efacd89..7fd76fc 100644 --- a/cmd/commit/commit.go +++ b/cmd/commit/commit.go @@ -180,7 +180,7 @@ func (commit Commit) MarshalJSON() (data []byte, err error) { var repo *repository.Repository var date string - if commit.Author.Type != "" || !commit.Author.User.ID.IsNil() { + if !commit.Author.IsEmpty() { author = &commit.Author } if !commit.Repository.ID.IsNil() || len(commit.Repository.FullName) > 0 { From 55bf10335cfae5b5260419787252bb895f13f52c Mon Sep 17 00:00:00 2001 From: Brian Summa Date: Sat, 6 Jun 2026 14:37:25 -0500 Subject: [PATCH 5/5] Add test for Raw-only author serialization and improve coverage-report feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add TestCanMarshalRawOnlyAuthor to verify that commits from non-Bitbucket- registered users (Author.Raw set, Type and User empty) include the author in marshaled JSON output — the exact regression fixed in the prior commit. Add an else branch to the coverage-report Makefile target so missing coverage tools produce a clear message instead of a silent no-op exit. Co-Authored-By: Claude Sonnet 4.6 --- Makefile | 2 ++ cmd/commit/commit_test.go | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/Makefile b/Makefile index 5ed9fa7..b05a65b 100644 --- a/Makefile +++ b/Makefile @@ -207,6 +207,8 @@ test tests: ; $(info $(M) Running $(NAME:%=% )tests...) @ ## Run the Unit Tests coverage-report: $(COVERAGE_OUT) | coverage-tools; @ ## Generate XML coverage report (requires gocov/gocov-xml) $Q if [ -x "$(GOCOV)" ] && [ -x "$(GOCOVXML)" ]; then \ $(GOCOV) convert $(COVERAGE_OUT) | $(GOCOVXML) > $(COVERAGE_XML); \ + else \ + printf "$(M) coverage tools not installed; run: make coverage-tools\n"; \ fi test-ci:; @ ## Run the unit tests continuously diff --git a/cmd/commit/commit_test.go b/cmd/commit/commit_test.go index 9bb69d7..e11868c 100644 --- a/cmd/commit/commit_test.go +++ b/cmd/commit/commit_test.go @@ -94,6 +94,25 @@ func (suite *CommitSuite) TestCanUnmarshal() { suite.Assert().JSONEq(string(payload), string(data)) } +func (suite *CommitSuite) TestCanMarshalRawOnlyAuthor() { + c := commit.Commit{ + Hash: "abc123def456abc123def456abc123def456abc1", + Message: "Commit from an external contributor", + } + c.Author.Raw = "John Doe " + + data, err := json.Marshal(c) + suite.Require().NoError(err) + + var result map[string]any + err = json.Unmarshal(data, &result) + suite.Require().NoError(err) + + author, ok := result["author"].(map[string]any) + suite.Assert().True(ok, "author key must be present when only Raw is set") + suite.Assert().Equal("John Doe ", author["raw"]) +} + func (suite *CommitSuite) TestCanMarshalCommitReference() { expected := `{"type": "commit", "hash": "026560720168aa12820a01e8262f6bb60f0639d1"}` reference := commit.CommitReference{Hash: "026560720168aa12820a01e8262f6bb60f0639d1"}