├── .codecov.yml ├── .github ├── release.yml └── workflows │ ├── ci.yaml │ ├── reviewdog.yml │ └── tagpr.yml ├── .gitignore ├── .goreleaser.yml ├── .tagpr ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bumper.go ├── bumper_test.go ├── cmd └── gh-bump │ └── main.go ├── enum.go ├── enum_test.go ├── export_test.go ├── gh.go ├── go.mod ├── go.sum ├── mock └── mock_bump.go ├── prompt.go ├── renovate.json ├── root.go └── root_test.go /.codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | threshold: 1% 6 | informational: true 7 | patch: off 8 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | labels: 4 | - tagpr 5 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: test 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - main 7 | jobs: 8 | test: 9 | strategy: 10 | matrix: 11 | os: [ubuntu-latest, macOS-latest, windows-latest] 12 | runs-on: ${{ matrix.os }} 13 | name: build & test 14 | steps: 15 | - uses: actions/checkout@v4 16 | - uses: actions/setup-go@v5 17 | with: 18 | go-version-file: 'go.mod' 19 | - uses: actions/cache@v4 20 | with: 21 | path: | 22 | ~/go/pkg/mod 23 | ~/.cache/go-build 24 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 25 | restore-keys: | 26 | ${{ runner.os }}-go- 27 | - name: build 28 | run: go build . 29 | - name: test 30 | run: go test -v -race -coverprofile="coverage.txt" -covermode=atomic ./... 31 | - name: upload coverage to codecov 32 | uses: codecov/codecov-action@v4 33 | -------------------------------------------------------------------------------- /.github/workflows/reviewdog.yml: -------------------------------------------------------------------------------- 1 | name: reviewdog 2 | on: pull_request 3 | jobs: 4 | staticcheck: 5 | name: staticcheck 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v4 9 | - uses: reviewdog/action-staticcheck@v1 10 | with: 11 | reporter: github-pr-review 12 | fail_on_error: true 13 | -------------------------------------------------------------------------------- /.github/workflows/tagpr.yml: -------------------------------------------------------------------------------- 1 | name: tagpr 2 | on: 3 | push: 4 | branches: ["main"] 5 | jobs: 6 | tagpr: 7 | runs-on: ubuntu-latest 8 | outputs: 9 | tagpr-tag: ${{ steps.run-tagpr.outputs.tag }} 10 | env: 11 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 12 | steps: 13 | - uses: actions/checkout@v4 14 | - id: run-tagpr 15 | uses: Songmu/tagpr@v1 16 | release: 17 | needs: tagpr 18 | if: needs.tagpr.outputs.tagpr-tag != '' 19 | runs-on: ubuntu-latest 20 | env: 21 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 22 | steps: 23 | - uses: actions/checkout@v4 24 | with: 25 | fetch-depth: 0 26 | - uses: actions/setup-go@v5 27 | with: 28 | go-version-file: 'go.mod' 29 | - name: release by goreleaser 30 | uses: goreleaser/goreleaser-action@v5 31 | with: 32 | args: release --rm-dist 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | 17 | dist/ 18 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | before: 2 | hooks: 3 | - go mod tidy 4 | builds: 5 | - env: 6 | - CGO_ENABLED=0 7 | goos: 8 | - linux 9 | - windows 10 | - darwin 11 | goarch: 12 | - 386 13 | - amd64 14 | - arm64 15 | ignore: 16 | - goos: darwin 17 | goarch: 386 18 | - goos: windows 19 | goarch: arm64 20 | main: ./cmd/gh-bump 21 | archives: 22 | - name_template: '{{ .Os }}-{{ .Arch }}' 23 | format: binary 24 | snapshot: 25 | name_template: "{{ .Tag }}-next" 26 | changelog: 27 | skip: true 28 | -------------------------------------------------------------------------------- /.tagpr: -------------------------------------------------------------------------------- 1 | # config file for the tagpr in git config format 2 | # The tagpr generates the initial configuration, which you can rewrite to suit your environment. 3 | # CONFIGURATIONS: 4 | # tagpr.releaseBranch 5 | # Generally, it is "main." It is the branch for releases. The pcpr tracks this branch, 6 | # creates or updates a pull request as a release candidate, or tags when they are merged. 7 | # 8 | # tagpr.versionFile 9 | # Versioning file containing the semantic version needed to be updated at release. 10 | # It will be synchronized with the "git tag". 11 | # Often this is a meta-information file such as gemspec, setup.cfg, package.json, etc. 12 | # Sometimes the source code file, such as version.go or Bar.pm, is used. 13 | # If you do not want to use versioning files but only git tags, specify the "-" string here. 14 | # You can specify multiple version files by comma separated strings. 15 | # 16 | # tagpr.vPrefix 17 | # Flag whether or not v-prefix is added to semver when git tagging. (e.g. v1.2.3 if true) 18 | # This is only a tagging convention, not how it is described in the version file. 19 | # 20 | # tagpr.changelog (Optional) 21 | # Flag whether or not changelog is added or changed during the release. 22 | # 23 | # tagpr.command (Optional) 24 | # Command to change files just before release. 25 | # 26 | # tagpr.tmplate (Optional) 27 | # Pull request template in go template format 28 | # 29 | # tagpr.release (Optional) 30 | # GitHub Release creation behavior after tagging [true, draft, false] 31 | # If this value is not set, the release is to be created. 32 | [tagpr] 33 | vPrefix = true 34 | releaseBranch = main 35 | versionFile = - 36 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [v0.10.0](https://github.com/johnmanjiro13/gh-bump/compare/v0.9.2...v0.10.0) - 2025-02-22 4 | - fix(deps): update module github.com/spf13/cobra to v1.8.1 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/115 5 | - fix(deps): update module github.com/masterminds/semver/v3 to v3.3.0 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/117 6 | - fix(deps): update module github.com/masterminds/semver/v3 to v3.3.1 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/120 7 | - fix(deps): update module github.com/stretchr/testify to v1.10.0 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/121 8 | - fix(deps): update module github.com/spf13/cobra to v1.9.0 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/123 9 | - fix(deps): update module github.com/spf13/cobra to v1.9.1 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/124 10 | - add --suffix option by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/125 11 | 12 | ## [v0.9.2](https://github.com/johnmanjiro13/gh-bump/compare/v0.9.1...v0.9.2) - 2024-03-02 13 | - fix(deps): update module github.com/stretchr/testify to v1.9.0 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/112 14 | 15 | ## [v0.9.1](https://github.com/johnmanjiro13/gh-bump/compare/v0.9.0...v0.9.1) - 2024-02-24 16 | - fix(deps): update module github.com/spf13/cobra to v1.8.0 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/107 17 | - chore(deps): update codecov/codecov-action action to v4 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/111 18 | - chore(deps): update actions/cache action to v4 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/110 19 | - chore(deps): update actions/setup-go action to v5 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/109 20 | 21 | ## [v0.9.0](https://github.com/johnmanjiro13/gh-bump/compare/v0.8.2...v0.9.0) - 2023-10-09 22 | - Support to upload asset files by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/105 23 | 24 | ## [v0.8.2](https://github.com/johnmanjiro13/gh-bump/compare/v0.8.1...v0.8.2) - 2023-10-09 25 | - chore(deps): update actions/checkout action to v4 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/100 26 | - chore(deps): update goreleaser/goreleaser-action action to v5 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/101 27 | - refactor: Use `Var` function for flags which don't have shorthand by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/104 28 | 29 | ## [v0.8.1](https://github.com/johnmanjiro13/gh-bump/compare/v0.8.0...v0.8.1) - 2023-06-16 30 | - fix(deps): update module github.com/alecaivazis/survey/v2 to v2.3.7 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/98 31 | 32 | ## [v0.8.0](https://github.com/johnmanjiro13/gh-bump/compare/v0.7.3...v0.8.0) - 2023-06-12 33 | - feat: Bump go version to 1.20 by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/90 34 | - fix: Use new error message by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/93 35 | - fix(deps): update module github.com/stretchr/testify to v1.8.3 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/95 36 | - fix(deps): update module github.com/stretchr/testify to v1.8.4 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/96 37 | - feat: Use survey for prompt module by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/94 38 | 39 | ## [v0.7.3](https://github.com/johnmanjiro13/gh-bump/compare/v0.7.2...v0.7.3) - 2023-04-14 40 | - fix(deps): update module github.com/spf13/cobra to v1.7.0 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/87 41 | - fix(deps): update module github.com/masterminds/semver/v3 to v3.2.1 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/89 42 | 43 | ## [v0.7.2](https://github.com/johnmanjiro13/gh-bump/compare/v0.7.1...v0.7.2) - 2023-04-05 44 | - fix(deps): update module github.com/cli/go-gh to v1.2.0 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/79 45 | - fix(deps): update module github.com/stretchr/testify to v1.8.2 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/81 46 | - fix(deps): update module github.com/cli/go-gh to v1.2.1 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/82 47 | - feat: Use safeexec instead of go-gh by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/85 48 | - chore(deps): update actions/setup-go action to v4 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/84 49 | - chore: Set go version of the ci with go.mod by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/86 50 | 51 | ## [v0.7.1](https://github.com/johnmanjiro13/gh-bump/compare/v0.7.0...v0.7.1) - 2023-02-11 52 | - chore(deps): update goreleaser/goreleaser-action action to v4 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/76 53 | - fix(deps): update module github.com/cli/go-gh to v1.1.0 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/78 54 | 55 | ## [v0.7.0](https://github.com/johnmanjiro13/gh-bump/compare/v0.6.6...v0.7.0) - 2022-12-10 56 | - chore: Disable dependabot by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/66 57 | - Configure Renovate by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/68 58 | - chore: Setup renovate by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/72 59 | - chore(deps): update codecov/codecov-action action to v3 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/70 60 | - remove: CODEOWNERS by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/74 61 | - fix(deps): update module github.com/cli/go-gh to v1 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/73 62 | - fix(deps): update module github.com/masterminds/semver/v3 to v3.2.0 by @renovate in https://github.com/johnmanjiro13/gh-bump/pull/69 63 | - refactor: reviewdog yaml by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/75 64 | 65 | ## [v0.6.6](https://github.com/johnmanjiro13/gh-bump/compare/v0.6.5...v0.6.6) - 2022-11-13 66 | - build(deps): bump github.com/stretchr/testify from 1.8.0 to 1.8.1 by @dependabot in https://github.com/johnmanjiro13/gh-bump/pull/61 67 | - build(deps): bump github.com/spf13/cobra from 1.5.0 to 1.6.1 by @dependabot in https://github.com/johnmanjiro13/gh-bump/pull/63 68 | - build(deps): bump github.com/cli/go-gh from 0.1.1 to 0.1.2 by @dependabot in https://github.com/johnmanjiro13/gh-bump/pull/60 69 | 70 | ## [v0.6.5](https://github.com/johnmanjiro13/gh-bump/compare/v0.6.4...v0.6.5) - 2022-10-05 71 | - build(deps): bump github.com/cli/go-gh from 0.1.0 to 0.1.1 by @dependabot in https://github.com/johnmanjiro13/gh-bump/pull/55 72 | - ci: Introduce tagpr by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/56 73 | - Release with one job by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/58 74 | 75 | ## [v0.6.4](https://github.com/johnmanjiro13/gh-bump/compare/v0.6.3...v0.6.4) - 2022-09-18 76 | - refactor: Use go generate to generate mock by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/52 77 | - refactor: Use flat package by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/53 78 | - fix: goreleaser main path by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/54 79 | 80 | ## [v0.6.3](https://github.com/johnmanjiro13/gh-bump/compare/v0.6.2...v0.6.3) - 2022-08-27 81 | - Use go-gh instead of safeexec by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/50 82 | - Introduce staticcheck via reviewdog by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/51 83 | 84 | ## [v0.6.2](https://github.com/johnmanjiro13/gh-bump/compare/v0.6.1...v0.6.2) - 2022-08-21 85 | - build(deps): bump github.com/stretchr/testify from 1.7.5 to 1.8.0 by @dependabot in https://github.com/johnmanjiro13/gh-bump/pull/47 86 | - Bump goreleaser/goreleaser-action from 2 to 3 by @dependabot in https://github.com/johnmanjiro13/gh-bump/pull/33 87 | - upgrade: Go version 1.19 by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/48 88 | 89 | ## [v0.6.1](https://github.com/johnmanjiro13/gh-bump/compare/v0.6.0...v0.6.1) - 2022-06-27 90 | - build(deps): bump github.com/stretchr/testify from 1.7.1 to 1.7.2 by @dependabot in https://github.com/johnmanjiro13/gh-bump/pull/40 91 | - refactor: Define mock package for test by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/41 92 | - build(deps): bump github.com/stretchr/testify from 1.7.2 to 1.7.3 by @dependabot in https://github.com/johnmanjiro13/gh-bump/pull/42 93 | - build(deps): bump github.com/stretchr/testify from 1.7.3 to 1.7.4 by @dependabot in https://github.com/johnmanjiro13/gh-bump/pull/44 94 | - feat: Define exit status code with iota by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/46 95 | - build(deps): bump github.com/stretchr/testify from 1.7.4 to 1.7.5 by @dependabot in https://github.com/johnmanjiro13/gh-bump/pull/45 96 | - build(deps): bump github.com/spf13/cobra from 1.4.0 to 1.5.0 by @dependabot in https://github.com/johnmanjiro13/gh-bump/pull/43 97 | 98 | ## [v0.6.0](https://github.com/johnmanjiro13/gh-bump/compare/v0.5.2...v0.6.0) - 2022-06-05 99 | - test: improve test coverage by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/38 100 | - Support bump-type option by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/39 101 | 102 | ## [v0.5.2](https://github.com/johnmanjiro13/gh-bump/compare/v0.5.1...v0.5.2) - 2022-06-04 103 | - chore: fix goreleaser names by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/36 104 | - chore: remove emoji from release name by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/37 105 | 106 | ## [v0.5.1](https://github.com/johnmanjiro13/gh-bump/compare/v0.5.0...v0.5.1) - 2022-06-04 107 | - chore: Add codecov workflow by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/31 108 | - chore: use Go binary by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/35 109 | 110 | ## [v0.5.0](https://github.com/johnmanjiro13/gh-bump/compare/v0.4.0...v0.5.0) - 2022-05-01 111 | - test: fix assertion of generate notes in test by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/18 112 | - upgrade: Go 1.18 by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/19 113 | - chore: use release-drafter for create release by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/21 114 | - fix: run release-drafter workflow after build succeeded by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/22 115 | - add: dependabot.yml by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/20 116 | - Bump github.com/stretchr/testify from 1.7.0 to 1.7.1 by @dependabot in https://github.com/johnmanjiro13/gh-bump/pull/28 117 | - Bump actions/cache from 2 to 3 by @dependabot in https://github.com/johnmanjiro13/gh-bump/pull/25 118 | - chore: set Maintenance to dependencies by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/29 119 | - Bump actions/checkout from 2 to 3 by @dependabot in https://github.com/johnmanjiro13/gh-bump/pull/24 120 | - Bump github.com/spf13/cobra from 1.2.1 to 1.4.0 by @dependabot in https://github.com/johnmanjiro13/gh-bump/pull/26 121 | - Bump actions/setup-go from 2 to 3 by @dependabot in https://github.com/johnmanjiro13/gh-bump/pull/23 122 | - Bump github.com/manifoldco/promptui from 0.8.0 to 0.9.0 by @dependabot in https://github.com/johnmanjiro13/gh-bump/pull/27 123 | - Release v0.5.0 by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/30 124 | 125 | ## [v0.4.0](https://github.com/johnmanjiro13/gh-bump/compare/v0.3.0...v0.4.0) - 2022-02-28 126 | - add CODEOWNERS by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/16 127 | - Add support for `--generate-notes` option for `gh release create` by @yarlson in https://github.com/johnmanjiro13/gh-bump/pull/15 128 | - release v0.4.0 by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/17 129 | 130 | ## [v0.3.0](https://github.com/johnmanjiro13/gh-bump/compare/v0.2.1...v0.3.0) - 2021-12-12 131 | - move codes to some packages by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/5 132 | - add title option by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/6 133 | - remove is initial field from bumper by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/7 134 | - add target option by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/8 135 | - add a prerelease option by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/9 136 | - add a draft option by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/10 137 | - add notes option by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/11 138 | - add notes file option by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/12 139 | - add discussion category option by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/13 140 | - Release v0.3.0 by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/14 141 | 142 | ## [v0.2.1](https://github.com/johnmanjiro13/gh-bump/compare/v0.2.0...v0.2.1) - 2021-12-08 143 | - upgrade to v0.2.1 by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/3 144 | 145 | ## [v0.2.0](https://github.com/johnmanjiro13/gh-bump/compare/v0.1.0...v0.2.0) - 2021-12-08 146 | - Support to run in the current repository by @johnmanjiro13 in https://github.com/johnmanjiro13/gh-bump/pull/2 147 | 148 | ## [v0.1.0](https://github.com/johnmanjiro13/gh-bump/compare/v0.0.1...v0.1.0) - 2021-08-31 149 | 150 | ## [v0.0.1](https://github.com/johnmanjiro13/gh-bump/commits/v0.0.1) - 2021-08-31 151 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 johnmanjiro13 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gh bump 2 | 3 | a [gh](https://github.com/cli/cli) extension for bumping version of a repository. 4 | 5 | ![image](https://user-images.githubusercontent.com/28798279/131488677-50c275e2-4823-493b-b431-50994d6e6970.png) 6 | 7 | 8 | # Usage 9 | ``` 10 | gh bump 11 | ``` 12 | 13 | with another repository. 14 | 15 | ``` 16 | gh bump -R 17 | ``` 18 | 19 | If you want to see more detail about this extension, you can run below. 20 | 21 | ``` 22 | gh bump -h 23 | ``` 24 | 25 | # Installation 26 | ``` 27 | gh extension install johnmanjiro13/gh-bump 28 | ``` 29 | -------------------------------------------------------------------------------- /bumper.go: -------------------------------------------------------------------------------- 1 | package bump 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "strings" 7 | 8 | "github.com/AlecAivazis/survey/v2" 9 | "github.com/Masterminds/semver/v3" 10 | ) 11 | 12 | //go:generate mockgen -source=$GOFILE -package=mock -destination=./mock/mock_${GOPACKAGE}.go 13 | type Gh interface { 14 | ViewRepository() (sout, eout *bytes.Buffer, err error) 15 | ListRelease(repo string, isCurrent bool) (sout, eout *bytes.Buffer, err error) 16 | ViewRelease(repo string, isCurrent bool) (sout, eout *bytes.Buffer, err error) 17 | CreateRelease(version string, repo string, isCurrent bool, option *ReleaseOption) (sout, eout *bytes.Buffer, err error) 18 | } 19 | 20 | //go:generate mockgen -source=$GOFILE -package=mock -destination=./mock/mock_${GOPACKAGE}.go 21 | type Prompter interface { 22 | Input(question string, validator survey.Validator) (string, error) 23 | Select(question string, options []string) (string, error) 24 | Confirm(question string) (bool, error) 25 | } 26 | 27 | type ReleaseOption struct { 28 | IsDraft bool 29 | IsPrerelease bool 30 | DiscussionCategory string 31 | GenerateNotes bool 32 | Notes string 33 | NotesFilename string 34 | Target string 35 | Title string 36 | AssetFiles []string 37 | } 38 | 39 | type bumper struct { 40 | gh Gh 41 | prompter Prompter 42 | repository string 43 | isCurrent bool 44 | isDraft bool 45 | isPrerelease bool 46 | discussionCategory string 47 | generateNotes bool 48 | notes string 49 | notesFilename string 50 | target string 51 | title string 52 | assetFiles []string 53 | bumpType BumpType 54 | suffix string 55 | yes bool 56 | } 57 | 58 | func NewBumper(gh Gh) *bumper { 59 | return &bumper{ 60 | gh: gh, 61 | prompter: newPrompter(), 62 | } 63 | } 64 | 65 | func (b *bumper) WithRepository(repository string) error { 66 | if repository != "" { 67 | b.repository = repository 68 | return nil 69 | } 70 | 71 | repo, err := b.resolveRepository() 72 | if err != nil { 73 | return err 74 | } 75 | b.repository = repo 76 | b.isCurrent = true 77 | return nil 78 | } 79 | 80 | func (b *bumper) WithDraft() { 81 | b.isDraft = true 82 | } 83 | 84 | func (b *bumper) WithPrerelease() { 85 | b.isPrerelease = true 86 | } 87 | 88 | func (b *bumper) WithDiscussionCategory(category string) { 89 | b.discussionCategory = category 90 | } 91 | 92 | func (b *bumper) WithGenerateNotes() { 93 | b.generateNotes = true 94 | } 95 | 96 | func (b *bumper) WithNotes(notes string) { 97 | b.notes = notes 98 | } 99 | 100 | func (b *bumper) WithNotesFile(filename string) { 101 | b.notesFilename = filename 102 | } 103 | 104 | func (b *bumper) WithTarget(target string) { 105 | b.target = target 106 | } 107 | 108 | func (b *bumper) WithTitle(title string) { 109 | b.title = title 110 | } 111 | 112 | func (b *bumper) WithAssetFiles(files []string) { 113 | b.assetFiles = files 114 | } 115 | 116 | func (b *bumper) WithBumpType(s string) error { 117 | bumpType, err := ParseBumpType(s) 118 | if err != nil { 119 | return err 120 | } 121 | b.bumpType = bumpType 122 | return nil 123 | } 124 | 125 | func (b *bumper) WithSuffix(suffix string) { 126 | b.suffix = suffix 127 | } 128 | 129 | func (b *bumper) WithYes() { 130 | b.yes = true 131 | } 132 | 133 | func (b *bumper) Bump() error { 134 | releases, err := b.listReleases() 135 | if err != nil { 136 | return err 137 | } 138 | fmt.Println(releases) 139 | 140 | if len(b.assetFiles) > 0 { 141 | fmt.Println("Asset files:") 142 | fmt.Println(strings.Join(b.assetFiles, "\n")) 143 | fmt.Println() 144 | } 145 | 146 | current, isInitial, err := b.currentVersion() 147 | if err != nil { 148 | return err 149 | } 150 | var nextVer *semver.Version 151 | if isInitial { 152 | nextVer = current 153 | } else if b.bumpType.Valid() == nil && !b.bumpType.IsBlank() { 154 | nextVer, err = incrementVersion(current, b.bumpType.String()) 155 | if err != nil { 156 | return err 157 | } 158 | } else { 159 | nextVer, err = b.nextVersion(current) 160 | if err != nil { 161 | return err 162 | } 163 | } 164 | nextVerString := nextVer.Original() 165 | if b.suffix != "" { 166 | nextVerString = fmt.Sprintf("%s-%s", nextVerString, b.suffix) 167 | } 168 | 169 | // Skip approval if --yes is set 170 | if !b.yes { 171 | ok, err := b.approve(nextVerString) 172 | if err != nil { 173 | return err 174 | } 175 | if !ok { 176 | fmt.Println("Bump was canceled.") 177 | return nil 178 | } 179 | } 180 | 181 | result, err := b.createRelease(nextVerString) 182 | if err != nil { 183 | return err 184 | } 185 | fmt.Println("Release was created.") 186 | fmt.Println(result) 187 | return nil 188 | } 189 | 190 | func (b *bumper) resolveRepository() (string, error) { 191 | sout, _, err := b.gh.ViewRepository() 192 | if err != nil { 193 | return "", err 194 | } 195 | viewOut := strings.Split(sout.String(), "\n")[0] 196 | repo := strings.TrimSpace(strings.Split(viewOut, ":")[1]) 197 | return repo, nil 198 | } 199 | 200 | func (b *bumper) listReleases() (string, error) { 201 | sout, _, err := b.gh.ListRelease(b.repository, b.isCurrent) 202 | if err != nil { 203 | return "", err 204 | } 205 | return fmt.Sprintf("Tags:\n%s", sout.String()), nil 206 | } 207 | 208 | func (b *bumper) currentVersion() (current *semver.Version, isInitial bool, err error) { 209 | sout, eout, err := b.gh.ViewRelease(b.repository, b.isCurrent) 210 | if err != nil { 211 | if strings.Contains(eout.String(), "release not found") { 212 | current, err = b.newVersion() 213 | if err != nil { 214 | return nil, false, err 215 | } 216 | return current, true, nil 217 | } 218 | return nil, false, err 219 | } 220 | viewOut := strings.Split(sout.String(), "\n")[1] 221 | tag := strings.TrimSpace(strings.Split(viewOut, ":")[1]) 222 | current, err = semver.NewVersion(tag) 223 | if err != nil { 224 | return nil, false, fmt.Errorf("invalid version. err: %w", err) 225 | } 226 | return current, false, nil 227 | } 228 | 229 | func (b *bumper) newVersion() (*semver.Version, error) { 230 | validate := func(v interface{}) error { 231 | input, ok := v.(string) 232 | if !ok { 233 | return fmt.Errorf("invalid input type. input: %v", v) 234 | } 235 | _, err := semver.NewVersion(input) 236 | if err != nil { 237 | return fmt.Errorf("invalid version. err: %w", err) 238 | } 239 | return nil 240 | } 241 | 242 | version, err := b.prompter.Input("New version", validate) 243 | if err != nil { 244 | return nil, fmt.Errorf("failed to prompt. err: %w", err) 245 | } 246 | return semver.NewVersion(version) 247 | } 248 | 249 | func (b *bumper) nextVersion(current *semver.Version) (*semver.Version, error) { 250 | question := fmt.Sprintf("Select next version. current: %s", current.Original()) 251 | options := []string{"patch", "minor", "major"} 252 | bumpType, err := b.prompter.Select(question, options) 253 | if err != nil { 254 | return nil, fmt.Errorf("failed to prompt. err: %w", err) 255 | } 256 | return incrementVersion(current, bumpType) 257 | } 258 | 259 | func incrementVersion(current *semver.Version, bumpType string) (*semver.Version, error) { 260 | var next semver.Version 261 | switch bumpType { 262 | case "major": 263 | next = current.IncMajor() 264 | case "minor": 265 | next = current.IncMinor() 266 | case "patch": 267 | next = current.IncPatch() 268 | default: 269 | return nil, fmt.Errorf("invalid type") 270 | } 271 | return &next, nil 272 | } 273 | 274 | func (b *bumper) approve(next string) (bool, error) { 275 | question := fmt.Sprintf("Create release %s ?", next) 276 | isApproved, err := b.prompter.Confirm(question) 277 | if err != nil { 278 | return false, fmt.Errorf("failed to prompt. err: %w", err) 279 | } 280 | return isApproved, nil 281 | } 282 | 283 | func (b *bumper) createRelease(version string) (string, error) { 284 | option := &ReleaseOption{ 285 | IsDraft: b.isDraft, 286 | IsPrerelease: b.isPrerelease, 287 | DiscussionCategory: b.discussionCategory, 288 | GenerateNotes: b.generateNotes, 289 | Notes: b.notes, 290 | NotesFilename: b.notesFilename, 291 | Target: b.target, 292 | Title: b.title, 293 | AssetFiles: b.assetFiles, 294 | } 295 | sout, _, err := b.gh.CreateRelease(version, b.repository, b.isCurrent, option) 296 | if err != nil { 297 | return "", err 298 | } 299 | return sout.String(), nil 300 | } 301 | -------------------------------------------------------------------------------- /bumper_test.go: -------------------------------------------------------------------------------- 1 | package bump_test 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "testing" 7 | 8 | "github.com/Masterminds/semver/v3" 9 | "github.com/golang/mock/gomock" 10 | "github.com/stretchr/testify/assert" 11 | 12 | bump "github.com/johnmanjiro13/gh-bump" 13 | "github.com/johnmanjiro13/gh-bump/mock" 14 | ) 15 | 16 | const ( 17 | repoDocs = `name: johnmanjiro13/gh-bump 18 | description: gh extension for bumping version of a repository` 19 | tagList = `v0.1.0 Latest v0.1.0 2021-12-08T04:19:16Z` 20 | releaseView = `title: v0.1.0 21 | tag: v0.1.0` 22 | ) 23 | 24 | func TestBumper_Bump(t *testing.T) { 25 | ctrl := gomock.NewController(t) 26 | defer ctrl.Finish() 27 | 28 | gh := mock.NewMockGh(ctrl) 29 | prompter := mock.NewMockPrompter(ctrl) 30 | 31 | t.Run("bump semver", func(t *testing.T) { 32 | tests := map[string]struct { 33 | bumpType string 34 | next string 35 | }{ 36 | "patch": {bumpType: "patch", next: "v0.1.1"}, 37 | "minor": {bumpType: "minor", next: "v0.2.0"}, 38 | "major": {bumpType: "major", next: "v1.0.0"}, 39 | } 40 | 41 | for name, tt := range tests { 42 | t.Run(name, func(t *testing.T) { 43 | bumper := bump.NewBumper(gh) 44 | bumper.SetPrompter(prompter) 45 | gh.EXPECT().ListRelease(bumper.Repository(), bumper.IsCurrent()).Return(bytes.NewBufferString(tagList), nil, nil) 46 | gh.EXPECT().ViewRelease(bumper.Repository(), bumper.IsCurrent()).Return(bytes.NewBufferString(releaseView), nil, nil) 47 | 48 | prompter.EXPECT().Select("Select next version. current: v0.1.0", []string{"patch", "minor", "major"}).Return(tt.bumpType, nil) 49 | prompter.EXPECT().Confirm(fmt.Sprintf("Create release %s ?", tt.next)).Return(true, nil) 50 | gh.EXPECT().CreateRelease(tt.next, bumper.Repository(), bumper.IsCurrent(), &bump.ReleaseOption{}).Return(nil, nil, nil) 51 | assert.NoError(t, bumper.Bump()) 52 | }) 53 | } 54 | }) 55 | 56 | t.Run("bump semver with option", func(t *testing.T) { 57 | tests := map[string]struct { 58 | bumpType string 59 | next string 60 | hasError bool 61 | }{ 62 | "patch": {bumpType: "patch", next: "v0.1.1", hasError: false}, 63 | "minor": {bumpType: "minor", next: "v0.2.0", hasError: false}, 64 | "major": {bumpType: "major", next: "v1.0.0", hasError: false}, 65 | "invalid": {bumpType: "invalid", next: "", hasError: true}, 66 | } 67 | 68 | for name, tt := range tests { 69 | t.Run(name, func(t *testing.T) { 70 | bumper := bump.NewBumper(gh) 71 | bumper.SetPrompter(prompter) 72 | if tt.hasError { 73 | assert.ErrorIsf(t, bumper.WithBumpType(tt.bumpType), bump.ErrInvalidBumpType, "got %s", tt.bumpType) 74 | return 75 | } 76 | 77 | assert.NoError(t, bumper.WithBumpType(tt.bumpType)) 78 | gh.EXPECT().ListRelease(bumper.Repository(), bumper.IsCurrent()).Return(bytes.NewBufferString(tagList), nil, nil) 79 | gh.EXPECT().ViewRelease(bumper.Repository(), bumper.IsCurrent()).Return(bytes.NewBufferString(releaseView), nil, nil) 80 | prompter.EXPECT().Confirm(fmt.Sprintf("Create release %s ?", tt.next)).Return(true, nil) 81 | gh.EXPECT().CreateRelease(tt.next, bumper.Repository(), bumper.IsCurrent(), &bump.ReleaseOption{}).Return(nil, nil, nil) 82 | assert.NoError(t, bumper.Bump()) 83 | }) 84 | } 85 | }) 86 | 87 | t.Run("cancel bump", func(t *testing.T) { 88 | bumper := bump.NewBumper(gh) 89 | bumper.SetPrompter(prompter) 90 | gh.EXPECT().ListRelease(bumper.Repository(), bumper.IsCurrent()).Return(bytes.NewBufferString(tagList), nil, nil) 91 | gh.EXPECT().ViewRelease(bumper.Repository(), bumper.IsCurrent()).Return(bytes.NewBufferString(releaseView), nil, nil) 92 | 93 | prompter.EXPECT().Select("Select next version. current: v0.1.0", []string{"patch", "minor", "major"}).Return("patch", nil) 94 | prompter.EXPECT().Confirm("Create release v0.1.1 ?").Return(false, nil) 95 | assert.NoError(t, bumper.Bump()) 96 | }) 97 | 98 | t.Run("bump another repository", func(t *testing.T) { 99 | bumper := bump.NewBumper(gh) 100 | bumper.SetPrompter(prompter) 101 | 102 | const repo = "johnmanjiro13/gh-bump" 103 | assert.NoError(t, bumper.WithRepository("johnmanjiro13/gh-bump")) 104 | 105 | gh.EXPECT().ListRelease(bumper.Repository(), bumper.IsCurrent()).Return(bytes.NewBufferString(tagList), nil, nil) 106 | gh.EXPECT().ViewRelease(bumper.Repository(), bumper.IsCurrent()).Return(bytes.NewBufferString(releaseView), nil, nil) 107 | 108 | prompter.EXPECT().Select("Select next version. current: v0.1.0", []string{"patch", "minor", "major"}).Return("patch", nil) 109 | prompter.EXPECT().Confirm("Create release v0.1.1 ?").Return(true, nil) 110 | gh.EXPECT().CreateRelease("v0.1.1", repo, false, &bump.ReleaseOption{}).Return(nil, nil, nil) 111 | assert.NoError(t, bumper.Bump()) 112 | }) 113 | 114 | t.Run("bump with -y option", func(t *testing.T) { 115 | bumper := bump.NewBumper(gh) 116 | bumper.SetPrompter(prompter) 117 | 118 | bumper.WithYes() 119 | gh.EXPECT().ListRelease(bumper.Repository(), bumper.IsCurrent()).Return(bytes.NewBufferString(tagList), nil, nil) 120 | gh.EXPECT().ViewRelease(bumper.Repository(), bumper.IsCurrent()).Return(bytes.NewBufferString(releaseView), nil, nil) 121 | 122 | prompter.EXPECT().Select("Select next version. current: v0.1.0", []string{"patch", "minor", "major"}).Return("patch", nil) 123 | gh.EXPECT().CreateRelease("v0.1.1", bumper.Repository(), bumper.IsCurrent(), &bump.ReleaseOption{}).Return(nil, nil, nil) 124 | assert.NoError(t, bumper.Bump()) 125 | }) 126 | } 127 | 128 | func TestBumper_WithRepository(t *testing.T) { 129 | tests := map[string]struct { 130 | repository string 131 | wantRepository string 132 | wantIsCurrent bool 133 | }{ 134 | "repository was given": { 135 | repository: "johnmanjiro13/gh-bump", 136 | wantRepository: "johnmanjiro13/gh-bump", 137 | wantIsCurrent: false, 138 | }, 139 | "repository was not given": { 140 | repository: "", 141 | wantRepository: "johnmanjiro13/gh-bump", 142 | wantIsCurrent: true, 143 | }, 144 | } 145 | 146 | ctrl := gomock.NewController(t) 147 | defer ctrl.Finish() 148 | 149 | gh := mock.NewMockGh(ctrl) 150 | gh.EXPECT().ViewRepository().Return(bytes.NewBufferString(repoDocs), nil, nil) 151 | 152 | for name, tt := range tests { 153 | t.Run(name, func(t *testing.T) { 154 | b := bump.NewBumper(gh) 155 | assert.NoError(t, b.WithRepository(tt.repository)) 156 | 157 | assert.Equal(t, tt.wantRepository, b.Repository()) 158 | assert.Equal(t, tt.wantIsCurrent, b.IsCurrent()) 159 | }) 160 | } 161 | } 162 | 163 | func TestBumper_WithDraft(t *testing.T) { 164 | ctrl := gomock.NewController(t) 165 | defer ctrl.Finish() 166 | 167 | gh := mock.NewMockGh(ctrl) 168 | b := bump.NewBumper(gh) 169 | b.WithDraft() 170 | 171 | assert.True(t, b.IsDraft()) 172 | } 173 | 174 | func TestBumper_WithPrerelease(t *testing.T) { 175 | ctrl := gomock.NewController(t) 176 | defer ctrl.Finish() 177 | 178 | gh := mock.NewMockGh(ctrl) 179 | b := bump.NewBumper(gh) 180 | b.WithPrerelease() 181 | 182 | assert.True(t, b.IsPrerelease()) 183 | } 184 | 185 | func TestBumper_WithDiscussionCategory(t *testing.T) { 186 | ctrl := gomock.NewController(t) 187 | defer ctrl.Finish() 188 | 189 | gh := mock.NewMockGh(ctrl) 190 | b := bump.NewBumper(gh) 191 | b.WithDiscussionCategory("test") 192 | 193 | assert.Equal(t, "test", b.DiscussionCategory()) 194 | } 195 | 196 | func TestBumper_WithGenerateNotes(t *testing.T) { 197 | ctrl := gomock.NewController(t) 198 | defer ctrl.Finish() 199 | 200 | gh := mock.NewMockGh(ctrl) 201 | b := bump.NewBumper(gh) 202 | b.WithGenerateNotes() 203 | 204 | assert.True(t, b.GenerateNotes()) 205 | } 206 | 207 | func TestBumper_WithNotes(t *testing.T) { 208 | ctrl := gomock.NewController(t) 209 | defer ctrl.Finish() 210 | 211 | gh := mock.NewMockGh(ctrl) 212 | b := bump.NewBumper(gh) 213 | b.WithNotes("note") 214 | 215 | assert.Equal(t, "note", b.Notes()) 216 | } 217 | 218 | func TestBumper_WithNotesFile(t *testing.T) { 219 | ctrl := gomock.NewController(t) 220 | defer ctrl.Finish() 221 | 222 | gh := mock.NewMockGh(ctrl) 223 | b := bump.NewBumper(gh) 224 | b.WithNotesFile("filename") 225 | 226 | assert.Equal(t, "filename", b.NotesFilename()) 227 | } 228 | 229 | func TestBumper_WithTarget(t *testing.T) { 230 | ctrl := gomock.NewController(t) 231 | defer ctrl.Finish() 232 | 233 | gh := mock.NewMockGh(ctrl) 234 | b := bump.NewBumper(gh) 235 | b.WithTarget("target") 236 | 237 | assert.Equal(t, "target", b.Target()) 238 | } 239 | 240 | func TestBumper_WithTitle(t *testing.T) { 241 | ctrl := gomock.NewController(t) 242 | defer ctrl.Finish() 243 | 244 | gh := mock.NewMockGh(ctrl) 245 | b := bump.NewBumper(gh) 246 | b.WithTitle("title") 247 | 248 | assert.Equal(t, "title", b.Title()) 249 | } 250 | 251 | func TestBumper_WithAssetFiles(t *testing.T) { 252 | ctrl := gomock.NewController(t) 253 | defer ctrl.Finish() 254 | 255 | gh := mock.NewMockGh(ctrl) 256 | b := bump.NewBumper(gh) 257 | b.WithAssetFiles([]string{"file1", "file2"}) 258 | 259 | assert.Equal(t, []string{"file1", "file2"}, b.AssetFiles()) 260 | } 261 | 262 | func TestBumper_WithBumpType(t *testing.T) { 263 | ctrl := gomock.NewController(t) 264 | defer ctrl.Finish() 265 | 266 | gh := mock.NewMockGh(ctrl) 267 | tests := map[string]struct { 268 | s string 269 | want bump.BumpType 270 | wantErr error 271 | }{ 272 | "major": { 273 | s: "major", 274 | want: bump.Major, 275 | wantErr: nil, 276 | }, 277 | "invalid": { 278 | s: "invalid", 279 | want: "", 280 | wantErr: fmt.Errorf("%w: got invalid", bump.ErrInvalidBumpType), 281 | }, 282 | } 283 | 284 | for name, tt := range tests { 285 | t.Run(name, func(t *testing.T) { 286 | b := bump.NewBumper(gh) 287 | err := b.WithBumpType(tt.s) 288 | assert.Equal(t, tt.want, b.BumpType()) 289 | assert.Equal(t, tt.wantErr, err) 290 | }) 291 | } 292 | } 293 | 294 | func TestBumper_WithYes(t *testing.T) { 295 | ctrl := gomock.NewController(t) 296 | defer ctrl.Finish() 297 | 298 | gh := mock.NewMockGh(ctrl) 299 | b := bump.NewBumper(gh) 300 | b.WithYes() 301 | assert.True(t, b.Yes()) 302 | } 303 | 304 | func TestBumper_ResolveRepository(t *testing.T) { 305 | ctrl := gomock.NewController(t) 306 | defer ctrl.Finish() 307 | 308 | gh := mock.NewMockGh(ctrl) 309 | gh.EXPECT().ViewRepository().Return(bytes.NewBufferString(repoDocs), nil, nil) 310 | 311 | b := bump.NewBumper(gh) 312 | got, err := bump.ResolveRepository(b) 313 | assert.NoError(t, err) 314 | assert.Equal(t, "johnmanjiro13/gh-bump", got) 315 | } 316 | 317 | func TestBumper_listReleases(t *testing.T) { 318 | ctrl := gomock.NewController(t) 319 | defer ctrl.Finish() 320 | 321 | gh := mock.NewMockGh(ctrl) 322 | b := bump.NewBumper(gh) 323 | gh.EXPECT().ListRelease(b.Repository(), b.IsCurrent()). 324 | Return(bytes.NewBufferString(tagList), nil, nil) 325 | 326 | got, err := bump.ListReleases(b) 327 | assert.NoError(t, err) 328 | assert.Equal(t, fmt.Sprintf("Tags:\n%s", tagList), got) 329 | } 330 | 331 | func TestBumper_currentVersion(t *testing.T) { 332 | ctrl := gomock.NewController(t) 333 | defer ctrl.Finish() 334 | 335 | gh := mock.NewMockGh(ctrl) 336 | b := bump.NewBumper(gh) 337 | 338 | t.Run("new version", func(t *testing.T) { 339 | gh.EXPECT().ViewRelease(b.Repository(), b.IsCurrent()). 340 | Return(bytes.NewBufferString(releaseView), nil, nil) 341 | 342 | got, isInitial, err := bump.CurrentVersion(b) 343 | assert.NoError(t, err) 344 | assert.False(t, isInitial) 345 | want := semver.MustParse("v0.1.0") 346 | assert.Equal(t, want, got) 347 | }) 348 | } 349 | 350 | func TestIncrementVersion(t *testing.T) { 351 | current := semver.MustParse("v0.1.0") 352 | 353 | tests := map[string]struct { 354 | bumpType string 355 | want *semver.Version 356 | wantErr error 357 | }{ 358 | "major": { 359 | bumpType: "major", 360 | want: semver.MustParse("v1.0.0"), 361 | }, 362 | "minor": { 363 | bumpType: "minor", 364 | want: semver.MustParse("v0.2.0"), 365 | }, 366 | "patch": { 367 | bumpType: "patch", 368 | want: semver.MustParse("v0.1.1"), 369 | }, 370 | "invalid": { 371 | bumpType: "invalid", 372 | wantErr: fmt.Errorf("invalid type"), 373 | }, 374 | } 375 | 376 | for name, tt := range tests { 377 | t.Run(name, func(t *testing.T) { 378 | got, err := bump.IncrementVersion(current, tt.bumpType) 379 | assert.Equal(t, tt.wantErr, err) 380 | assert.Equal(t, tt.want, got) 381 | }) 382 | } 383 | } 384 | 385 | func TestBumper_createRelease(t *testing.T) { 386 | ctrl := gomock.NewController(t) 387 | defer ctrl.Finish() 388 | 389 | gh := mock.NewMockGh(ctrl) 390 | b := bump.NewBumper(gh) 391 | 392 | const version = "v1.0.0" 393 | gh.EXPECT().CreateRelease(version, b.Repository(), b.IsCurrent(), &bump.ReleaseOption{}). 394 | Return(bytes.NewBufferString(version), nil, nil) 395 | 396 | got, err := bump.CreateRelease(b, version) 397 | assert.NoError(t, err) 398 | assert.Equal(t, "v1.0.0", got) 399 | } 400 | -------------------------------------------------------------------------------- /cmd/gh-bump/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | bump "github.com/johnmanjiro13/gh-bump" 7 | ) 8 | 9 | type exitCode int 10 | 11 | const ( 12 | // exitStatusOK is status code zero 13 | exitStatusOK exitCode = iota 14 | // exitStatusError is status code non-zero 15 | exitStatusError 16 | ) 17 | 18 | func main() { 19 | os.Exit(int(run())) 20 | } 21 | 22 | func run() exitCode { 23 | gh := bump.NewGh() 24 | bumper := bump.NewBumper(gh) 25 | rootCmd := bump.NewRootCmd(bumper) 26 | if err := rootCmd.Execute(); err != nil { 27 | return exitStatusError 28 | } 29 | return exitStatusOK 30 | } 31 | -------------------------------------------------------------------------------- /enum.go: -------------------------------------------------------------------------------- 1 | package bump 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | type BumpType string 9 | 10 | const ( 11 | Major BumpType = "major" 12 | Minor BumpType = "minor" 13 | Patch BumpType = "patch" 14 | Blank BumpType = "" 15 | ) 16 | 17 | var ErrInvalidBumpType = errors.New("invalid bump type") 18 | 19 | func (b BumpType) String() string { 20 | return string(b) 21 | } 22 | 23 | func (b BumpType) IsBlank() bool { 24 | return b == Blank 25 | } 26 | 27 | func (b BumpType) Valid() error { 28 | switch b { 29 | case Major, Minor, Patch: 30 | return nil 31 | default: 32 | return fmt.Errorf("%w: got %s", ErrInvalidBumpType, b) 33 | } 34 | } 35 | 36 | func ParseBumpType(s string) (BumpType, error) { 37 | b := BumpType(s) 38 | if err := b.Valid(); err != nil { 39 | return "", err 40 | } 41 | return b, nil 42 | } 43 | -------------------------------------------------------------------------------- /enum_test.go: -------------------------------------------------------------------------------- 1 | package bump_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | 9 | bump "github.com/johnmanjiro13/gh-bump" 10 | ) 11 | 12 | func TestBumpType_String(t *testing.T) { 13 | bumpType := bump.BumpType("major") 14 | assert.Equal(t, "major", bumpType.String()) 15 | } 16 | 17 | func TestBumpType_IsBlank(t *testing.T) { 18 | tests := map[string]struct { 19 | bumpType bump.BumpType 20 | want bool 21 | }{ 22 | "major": { 23 | bumpType: bump.Major, 24 | want: false, 25 | }, 26 | "blank": { 27 | bumpType: bump.Blank, 28 | want: true, 29 | }, 30 | } 31 | 32 | for name, tt := range tests { 33 | t.Run(name, func(t *testing.T) { 34 | assert.Equal(t, tt.want, tt.bumpType.IsBlank()) 35 | }) 36 | } 37 | } 38 | 39 | func TestBumpType_Valid(t *testing.T) { 40 | tests := map[string]struct { 41 | bumpType string 42 | want error 43 | }{ 44 | "major": { 45 | bumpType: "major", 46 | want: nil, 47 | }, 48 | "minor": { 49 | bumpType: "minor", 50 | want: nil, 51 | }, 52 | "patch": { 53 | bumpType: "patch", 54 | want: nil, 55 | }, 56 | "invalid": { 57 | bumpType: "invalid", 58 | want: fmt.Errorf("%w: got invalid", bump.ErrInvalidBumpType), 59 | }, 60 | } 61 | 62 | for name, tt := range tests { 63 | t.Run(name, func(t *testing.T) { 64 | bumpType := bump.BumpType(tt.bumpType) 65 | assert.Equal(t, tt.want, bumpType.Valid()) 66 | }) 67 | } 68 | } 69 | 70 | func TestParseBumpType(t *testing.T) { 71 | tests := map[string]struct { 72 | s string 73 | want bump.BumpType 74 | wantErr error 75 | }{ 76 | "major": { 77 | s: "major", 78 | want: bump.BumpType("major"), 79 | wantErr: nil, 80 | }, 81 | "invalid": { 82 | s: "invalid", 83 | want: "", 84 | wantErr: fmt.Errorf("%w: got invalid", bump.ErrInvalidBumpType), 85 | }, 86 | } 87 | 88 | for name, tt := range tests { 89 | t.Run(name, func(t *testing.T) { 90 | got, err := bump.ParseBumpType(tt.s) 91 | assert.Equal(t, tt.want, got) 92 | assert.Equal(t, tt.wantErr, err) 93 | }) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /export_test.go: -------------------------------------------------------------------------------- 1 | package bump 2 | 3 | var ( 4 | ResolveRepository = (*bumper).resolveRepository 5 | ListReleases = (*bumper).listReleases 6 | CreateRelease = (*bumper).createRelease 7 | CurrentVersion = (*bumper).currentVersion 8 | IncrementVersion = incrementVersion 9 | ) 10 | 11 | func (b *bumper) SetPrompter(prompter Prompter) { 12 | b.prompter = prompter 13 | } 14 | 15 | func (b *bumper) Repository() string { 16 | return b.repository 17 | } 18 | 19 | func (b *bumper) IsCurrent() bool { 20 | return b.isCurrent 21 | } 22 | 23 | func (b *bumper) IsDraft() bool { 24 | return b.isDraft 25 | } 26 | 27 | func (b *bumper) IsPrerelease() bool { 28 | return b.isPrerelease 29 | } 30 | 31 | func (b *bumper) DiscussionCategory() string { 32 | return b.discussionCategory 33 | } 34 | 35 | func (b *bumper) GenerateNotes() bool { 36 | return b.generateNotes 37 | } 38 | 39 | func (b *bumper) Notes() string { 40 | return b.notes 41 | } 42 | 43 | func (b *bumper) NotesFilename() string { 44 | return b.notesFilename 45 | } 46 | 47 | func (b *bumper) Target() string { 48 | return b.target 49 | } 50 | 51 | func (b *bumper) Title() string { 52 | return b.title 53 | } 54 | 55 | func (b *bumper) AssetFiles() []string { 56 | return b.assetFiles 57 | } 58 | 59 | func (b *bumper) BumpType() BumpType { 60 | return b.bumpType 61 | } 62 | 63 | func (b *bumper) Yes() bool { 64 | return b.yes 65 | } 66 | -------------------------------------------------------------------------------- /gh.go: -------------------------------------------------------------------------------- 1 | package bump 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "os/exec" 7 | 8 | "github.com/cli/safeexec" 9 | ) 10 | 11 | type ghCLI struct{} 12 | 13 | func NewGh() *ghCLI { 14 | return &ghCLI{} 15 | } 16 | 17 | func (g *ghCLI) ViewRepository() (sout, eout *bytes.Buffer, err error) { 18 | return gh("repo", "view") 19 | } 20 | 21 | func (g *ghCLI) ListRelease(repo string, isCurrent bool) (sout, eout *bytes.Buffer, err error) { 22 | if isCurrent { 23 | sout, eout, err = gh("release", "list") 24 | } else { 25 | sout, eout, err = gh("release", "list", "-R", repo) 26 | } 27 | return 28 | } 29 | 30 | func (g *ghCLI) ViewRelease(repo string, isCurrent bool) (sout, eout *bytes.Buffer, err error) { 31 | if isCurrent { 32 | sout, eout, err = gh("release", "view") 33 | } else { 34 | sout, eout, err = gh("release", "view", "-R", repo) 35 | } 36 | return 37 | } 38 | 39 | func (g *ghCLI) CreateRelease(version string, repo string, isCurrent bool, option *ReleaseOption) (sout, eout *bytes.Buffer, err error) { 40 | args := []string{"release", "create", version} 41 | if len(option.AssetFiles) > 0 { 42 | args = append(args, option.AssetFiles...) 43 | } 44 | if !isCurrent { 45 | args = append(args, []string{"-R", repo}...) 46 | } 47 | if option.IsDraft { 48 | args = append(args, "--draft") 49 | } 50 | if option.IsPrerelease { 51 | args = append(args, "--prerelease") 52 | } 53 | if option.DiscussionCategory != "" { 54 | args = append(args, []string{"--discussion-category", option.DiscussionCategory}...) 55 | } 56 | if option.GenerateNotes { 57 | args = append(args, "--generate-notes") 58 | } 59 | if option.Notes != "" { 60 | args = append(args, []string{"--notes", option.Notes}...) 61 | } 62 | if option.NotesFilename != "" { 63 | args = append(args, []string{"--notes-file", option.NotesFilename}...) 64 | } 65 | if option.Target != "" { 66 | args = append(args, []string{"--target", option.Target}...) 67 | } 68 | if option.Title != "" { 69 | args = append(args, []string{"-t", option.Title}...) 70 | } 71 | sout, eout, err = gh(args...) 72 | return 73 | } 74 | 75 | func gh(args ...string) (sout, eout *bytes.Buffer, err error) { 76 | sout = new(bytes.Buffer) 77 | eout = new(bytes.Buffer) 78 | bin, err := safeexec.LookPath("gh") 79 | if err != nil { 80 | err = fmt.Errorf("could not find gh. err: %w", err) 81 | return 82 | } 83 | 84 | cmd := exec.Command(bin, args...) 85 | cmd.Stdout = sout 86 | cmd.Stderr = eout 87 | 88 | err = cmd.Run() 89 | if err != nil { 90 | err = fmt.Errorf("failed to run gh. err: %w, eout: %s", err, eout.String()) 91 | return 92 | } 93 | return 94 | } 95 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/johnmanjiro13/gh-bump 2 | 3 | go 1.21 4 | 5 | toolchain go1.23.2 6 | 7 | require ( 8 | github.com/AlecAivazis/survey/v2 v2.3.7 9 | github.com/Masterminds/semver/v3 v3.3.1 10 | github.com/cli/safeexec v1.0.1 11 | github.com/golang/mock v1.6.0 12 | github.com/spf13/cobra v1.9.1 13 | github.com/stretchr/testify v1.10.0 14 | ) 15 | 16 | require ( 17 | github.com/davecgh/go-spew v1.1.1 // indirect 18 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 19 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect 20 | github.com/kr/text v0.2.0 // indirect 21 | github.com/mattn/go-colorable v0.1.2 // indirect 22 | github.com/mattn/go-isatty v0.0.8 // indirect 23 | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect 24 | github.com/pmezard/go-difflib v1.0.0 // indirect 25 | github.com/spf13/pflag v1.0.6 // indirect 26 | golang.org/x/sys v0.5.0 // indirect 27 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect 28 | golang.org/x/text v0.4.0 // indirect 29 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect 30 | gopkg.in/yaml.v3 v3.0.1 // indirect 31 | ) 32 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= 2 | github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= 3 | github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= 4 | github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= 5 | github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= 6 | github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= 7 | github.com/cli/safeexec v1.0.1 h1:e/C79PbXF4yYTN/wauC4tviMxEV13BwljGj0N9j+N00= 8 | github.com/cli/safeexec v1.0.1/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= 9 | github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 10 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 11 | github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= 12 | github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= 13 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 14 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 15 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 16 | github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= 17 | github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= 18 | github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= 19 | github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= 20 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 21 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 22 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= 23 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= 24 | github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= 25 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 26 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 27 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 28 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 29 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 30 | github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= 31 | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 32 | github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= 33 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 34 | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= 35 | github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= 36 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 37 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 38 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 39 | github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= 40 | github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= 41 | github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= 42 | github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 43 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 44 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 45 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 46 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 47 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 48 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 49 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 50 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 51 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 52 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 53 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 54 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 55 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 56 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 57 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 58 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 59 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 60 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 61 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 62 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 63 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 64 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 65 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 66 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 67 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 68 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 69 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 70 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 71 | golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= 72 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 73 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 74 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= 75 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 76 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 77 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 78 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 79 | golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= 80 | golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 81 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 82 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 83 | golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 84 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 85 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 86 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 87 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 88 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 89 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 90 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 91 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 92 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 93 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 94 | -------------------------------------------------------------------------------- /mock/mock_bump.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: bumper.go 3 | 4 | // Package mock is a generated GoMock package. 5 | package mock 6 | 7 | import ( 8 | bytes "bytes" 9 | reflect "reflect" 10 | 11 | survey "github.com/AlecAivazis/survey/v2" 12 | gomock "github.com/golang/mock/gomock" 13 | bump "github.com/johnmanjiro13/gh-bump" 14 | ) 15 | 16 | // MockGh is a mock of Gh interface. 17 | type MockGh struct { 18 | ctrl *gomock.Controller 19 | recorder *MockGhMockRecorder 20 | } 21 | 22 | // MockGhMockRecorder is the mock recorder for MockGh. 23 | type MockGhMockRecorder struct { 24 | mock *MockGh 25 | } 26 | 27 | // NewMockGh creates a new mock instance. 28 | func NewMockGh(ctrl *gomock.Controller) *MockGh { 29 | mock := &MockGh{ctrl: ctrl} 30 | mock.recorder = &MockGhMockRecorder{mock} 31 | return mock 32 | } 33 | 34 | // EXPECT returns an object that allows the caller to indicate expected use. 35 | func (m *MockGh) EXPECT() *MockGhMockRecorder { 36 | return m.recorder 37 | } 38 | 39 | // CreateRelease mocks base method. 40 | func (m *MockGh) CreateRelease(version, repo string, isCurrent bool, option *bump.ReleaseOption) (*bytes.Buffer, *bytes.Buffer, error) { 41 | m.ctrl.T.Helper() 42 | ret := m.ctrl.Call(m, "CreateRelease", version, repo, isCurrent, option) 43 | ret0, _ := ret[0].(*bytes.Buffer) 44 | ret1, _ := ret[1].(*bytes.Buffer) 45 | ret2, _ := ret[2].(error) 46 | return ret0, ret1, ret2 47 | } 48 | 49 | // CreateRelease indicates an expected call of CreateRelease. 50 | func (mr *MockGhMockRecorder) CreateRelease(version, repo, isCurrent, option interface{}) *gomock.Call { 51 | mr.mock.ctrl.T.Helper() 52 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateRelease", reflect.TypeOf((*MockGh)(nil).CreateRelease), version, repo, isCurrent, option) 53 | } 54 | 55 | // ListRelease mocks base method. 56 | func (m *MockGh) ListRelease(repo string, isCurrent bool) (*bytes.Buffer, *bytes.Buffer, error) { 57 | m.ctrl.T.Helper() 58 | ret := m.ctrl.Call(m, "ListRelease", repo, isCurrent) 59 | ret0, _ := ret[0].(*bytes.Buffer) 60 | ret1, _ := ret[1].(*bytes.Buffer) 61 | ret2, _ := ret[2].(error) 62 | return ret0, ret1, ret2 63 | } 64 | 65 | // ListRelease indicates an expected call of ListRelease. 66 | func (mr *MockGhMockRecorder) ListRelease(repo, isCurrent interface{}) *gomock.Call { 67 | mr.mock.ctrl.T.Helper() 68 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListRelease", reflect.TypeOf((*MockGh)(nil).ListRelease), repo, isCurrent) 69 | } 70 | 71 | // ViewRelease mocks base method. 72 | func (m *MockGh) ViewRelease(repo string, isCurrent bool) (*bytes.Buffer, *bytes.Buffer, error) { 73 | m.ctrl.T.Helper() 74 | ret := m.ctrl.Call(m, "ViewRelease", repo, isCurrent) 75 | ret0, _ := ret[0].(*bytes.Buffer) 76 | ret1, _ := ret[1].(*bytes.Buffer) 77 | ret2, _ := ret[2].(error) 78 | return ret0, ret1, ret2 79 | } 80 | 81 | // ViewRelease indicates an expected call of ViewRelease. 82 | func (mr *MockGhMockRecorder) ViewRelease(repo, isCurrent interface{}) *gomock.Call { 83 | mr.mock.ctrl.T.Helper() 84 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ViewRelease", reflect.TypeOf((*MockGh)(nil).ViewRelease), repo, isCurrent) 85 | } 86 | 87 | // ViewRepository mocks base method. 88 | func (m *MockGh) ViewRepository() (*bytes.Buffer, *bytes.Buffer, error) { 89 | m.ctrl.T.Helper() 90 | ret := m.ctrl.Call(m, "ViewRepository") 91 | ret0, _ := ret[0].(*bytes.Buffer) 92 | ret1, _ := ret[1].(*bytes.Buffer) 93 | ret2, _ := ret[2].(error) 94 | return ret0, ret1, ret2 95 | } 96 | 97 | // ViewRepository indicates an expected call of ViewRepository. 98 | func (mr *MockGhMockRecorder) ViewRepository() *gomock.Call { 99 | mr.mock.ctrl.T.Helper() 100 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ViewRepository", reflect.TypeOf((*MockGh)(nil).ViewRepository)) 101 | } 102 | 103 | // MockPrompter is a mock of Prompter interface. 104 | type MockPrompter struct { 105 | ctrl *gomock.Controller 106 | recorder *MockPrompterMockRecorder 107 | } 108 | 109 | // MockPrompterMockRecorder is the mock recorder for MockPrompter. 110 | type MockPrompterMockRecorder struct { 111 | mock *MockPrompter 112 | } 113 | 114 | // NewMockPrompter creates a new mock instance. 115 | func NewMockPrompter(ctrl *gomock.Controller) *MockPrompter { 116 | mock := &MockPrompter{ctrl: ctrl} 117 | mock.recorder = &MockPrompterMockRecorder{mock} 118 | return mock 119 | } 120 | 121 | // EXPECT returns an object that allows the caller to indicate expected use. 122 | func (m *MockPrompter) EXPECT() *MockPrompterMockRecorder { 123 | return m.recorder 124 | } 125 | 126 | // Confirm mocks base method. 127 | func (m *MockPrompter) Confirm(question string) (bool, error) { 128 | m.ctrl.T.Helper() 129 | ret := m.ctrl.Call(m, "Confirm", question) 130 | ret0, _ := ret[0].(bool) 131 | ret1, _ := ret[1].(error) 132 | return ret0, ret1 133 | } 134 | 135 | // Confirm indicates an expected call of Confirm. 136 | func (mr *MockPrompterMockRecorder) Confirm(question interface{}) *gomock.Call { 137 | mr.mock.ctrl.T.Helper() 138 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Confirm", reflect.TypeOf((*MockPrompter)(nil).Confirm), question) 139 | } 140 | 141 | // Input mocks base method. 142 | func (m *MockPrompter) Input(question string, validator survey.Validator) (string, error) { 143 | m.ctrl.T.Helper() 144 | ret := m.ctrl.Call(m, "Input", question, validator) 145 | ret0, _ := ret[0].(string) 146 | ret1, _ := ret[1].(error) 147 | return ret0, ret1 148 | } 149 | 150 | // Input indicates an expected call of Input. 151 | func (mr *MockPrompterMockRecorder) Input(question, validator interface{}) *gomock.Call { 152 | mr.mock.ctrl.T.Helper() 153 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Input", reflect.TypeOf((*MockPrompter)(nil).Input), question, validator) 154 | } 155 | 156 | // Select mocks base method. 157 | func (m *MockPrompter) Select(question string, options []string) (string, error) { 158 | m.ctrl.T.Helper() 159 | ret := m.ctrl.Call(m, "Select", question, options) 160 | ret0, _ := ret[0].(string) 161 | ret1, _ := ret[1].(error) 162 | return ret0, ret1 163 | } 164 | 165 | // Select indicates an expected call of Select. 166 | func (mr *MockPrompterMockRecorder) Select(question, options interface{}) *gomock.Call { 167 | mr.mock.ctrl.T.Helper() 168 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Select", reflect.TypeOf((*MockPrompter)(nil).Select), question, options) 169 | } 170 | -------------------------------------------------------------------------------- /prompt.go: -------------------------------------------------------------------------------- 1 | package bump 2 | 3 | import "github.com/AlecAivazis/survey/v2" 4 | 5 | type prompter struct{} 6 | 7 | func newPrompter() Prompter { 8 | return &prompter{} 9 | } 10 | 11 | func (p *prompter) Input(question string, validator survey.Validator) (string, error) { 12 | var result string 13 | if err := survey.AskOne(&survey.Input{Message: question}, &result, survey.WithValidator(validator)); err != nil { 14 | return "", err 15 | } 16 | return result, nil 17 | } 18 | 19 | func (p *prompter) Select(question string, options []string) (string, error) { 20 | var result string 21 | if err := survey.AskOne(&survey.Select{Message: question, Options: options}, &result); err != nil { 22 | return "", err 23 | } 24 | return result, nil 25 | } 26 | 27 | func (p *prompter) Confirm(question string) (bool, error) { 28 | var result bool 29 | if err := survey.AskOne(&survey.Confirm{Message: question}, &result); err != nil { 30 | return false, err 31 | } 32 | return result, nil 33 | } 34 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base", 5 | ":preserveSemverRanges", 6 | ":timezone(Asia/Tokyo)" 7 | ], 8 | "schedule": [ 9 | "every weekend" 10 | ], 11 | "labels": [ 12 | "dependencies" 13 | ], 14 | "constraints": { 15 | "go": "1.20" 16 | }, 17 | "packageRules": [ 18 | { 19 | "matchUpdateTypes": ["minor", "patch", "pin", "digest"], 20 | "automerge": true 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /root.go: -------------------------------------------------------------------------------- 1 | package bump 2 | 3 | import "github.com/spf13/cobra" 4 | 5 | type Bumper interface { 6 | Bump() error 7 | WithRepository(repository string) error 8 | WithDraft() 9 | WithPrerelease() 10 | WithDiscussionCategory(category string) 11 | WithGenerateNotes() 12 | WithNotes(notes string) 13 | WithNotesFile(filename string) 14 | WithTitle(title string) 15 | WithTarget(target string) 16 | WithAssetFiles(files []string) 17 | WithBumpType(bumpType string) error 18 | WithSuffix(suffix string) 19 | WithYes() 20 | } 21 | 22 | func NewRootCmd(bumper Bumper) *cobra.Command { 23 | var ( 24 | repository string 25 | isDraft bool 26 | isPrerelease bool 27 | discussionCategory string 28 | generateNotes bool 29 | notes string 30 | notesFile string 31 | target string 32 | title string 33 | assetFiles []string 34 | bumpType string 35 | suffix string 36 | yes bool 37 | ) 38 | cmd := &cobra.Command{ 39 | Use: "bump", 40 | Short: "bump version of a given repository", 41 | RunE: func(cmd *cobra.Command, args []string) error { 42 | if err := bumper.WithRepository(repository); err != nil { 43 | return err 44 | } 45 | if isDraft { 46 | bumper.WithDraft() 47 | } 48 | if isPrerelease { 49 | bumper.WithPrerelease() 50 | } 51 | if discussionCategory != "" { 52 | bumper.WithDiscussionCategory(discussionCategory) 53 | } 54 | if generateNotes { 55 | bumper.WithGenerateNotes() 56 | } 57 | if notes != "" { 58 | bumper.WithNotes(notes) 59 | } 60 | if notesFile != "" { 61 | bumper.WithNotesFile(notesFile) 62 | } 63 | if target != "" { 64 | bumper.WithTarget(target) 65 | } 66 | if title != "" { 67 | bumper.WithTitle(title) 68 | } 69 | if len(assetFiles) > 0 { 70 | bumper.WithAssetFiles(assetFiles) 71 | } 72 | if bumpType != "" { 73 | err := bumper.WithBumpType(bumpType) 74 | if err != nil { 75 | return err 76 | } 77 | } 78 | if suffix != "" { 79 | bumper.WithSuffix(suffix) 80 | } 81 | if yes { 82 | bumper.WithYes() 83 | } 84 | return bumper.Bump() 85 | }, 86 | } 87 | 88 | cmd.Flags().StringVarP(&repository, "repo", "R", "", "Select another repository using the [HOST/]OWNER/REPO format") 89 | cmd.Flags().BoolVarP(&isDraft, "draft", "d", false, "Save the release as a draft instead of publishing it") 90 | cmd.Flags().BoolVarP(&isPrerelease, "prerelease", "p", false, "Mark the release as a prerelease") 91 | cmd.Flags().StringVar(&discussionCategory, "discussion-category", "", "Start a discussion of the specified category") 92 | cmd.Flags().BoolVarP(&generateNotes, "generate-notes", "g", false, "Automatically generate title and notes for the release") 93 | cmd.Flags().StringVarP(¬es, "notes", "n", "", "Release notes") 94 | cmd.Flags().StringVarP(¬esFile, "notes-file", "F", "", "Read release notes from file") 95 | cmd.Flags().StringVar(&target, "target", "", "Target branch or full commit SHA (default: main branch)") 96 | cmd.Flags().StringVarP(&title, "title", "t", "", "Release title") 97 | cmd.Flags().StringSliceVar(&assetFiles, "asset-files", []string{}, "Asset files to upload") 98 | cmd.Flags().StringVar(&bumpType, "bump-type", "", "Bump type (major, minor or patch)") 99 | cmd.Flags().StringVar(&suffix, "suffix", "", "Suffix for the version") 100 | cmd.Flags().BoolVarP(&yes, "yes", "y", false, "Answer 'yes' to all questions") 101 | return cmd 102 | } 103 | -------------------------------------------------------------------------------- /root_test.go: -------------------------------------------------------------------------------- 1 | package bump_test 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | 9 | bump "github.com/johnmanjiro13/gh-bump" 10 | ) 11 | 12 | type mockBumper struct { 13 | repository string 14 | isDraft bool 15 | isPrerelease bool 16 | discussionCategory string 17 | generateNotes bool 18 | notes string 19 | notesFilename string 20 | target string 21 | title string 22 | assetFiles []string 23 | bumpType bump.BumpType 24 | suffix string 25 | yes bool 26 | } 27 | 28 | func (b *mockBumper) Bump() error { 29 | return nil 30 | } 31 | 32 | func (b *mockBumper) WithRepository(repository string) error { 33 | b.repository = repository 34 | return nil 35 | } 36 | 37 | func (b *mockBumper) WithDraft() { 38 | b.isDraft = true 39 | } 40 | 41 | func (b *mockBumper) WithPrerelease() { 42 | b.isPrerelease = true 43 | } 44 | 45 | func (b *mockBumper) WithDiscussionCategory(category string) { 46 | b.discussionCategory = category 47 | } 48 | 49 | func (b *mockBumper) WithGenerateNotes() { 50 | b.generateNotes = true 51 | } 52 | 53 | func (b *mockBumper) WithNotes(notes string) { 54 | b.notes = notes 55 | } 56 | 57 | func (b *mockBumper) WithNotesFile(filename string) { 58 | b.notesFilename = filename 59 | } 60 | 61 | func (b *mockBumper) WithTitle(title string) { 62 | b.title = title 63 | } 64 | 65 | func (b *mockBumper) WithTarget(target string) { 66 | b.target = target 67 | } 68 | 69 | func (b *mockBumper) WithAssetFiles(files []string) { 70 | b.assetFiles = files 71 | } 72 | 73 | func (b *mockBumper) WithBumpType(s string) error { 74 | bumpType, err := bump.ParseBumpType(s) 75 | if err != nil { 76 | return err 77 | } 78 | b.bumpType = bumpType 79 | return nil 80 | } 81 | 82 | func (b *mockBumper) WithSuffix(suffix string) { 83 | b.suffix = suffix 84 | } 85 | 86 | func (b *mockBumper) WithYes() { 87 | b.yes = true 88 | } 89 | 90 | func TestNew(t *testing.T) { 91 | tests := map[string]struct { 92 | command string 93 | wantRepo string 94 | wantDraft bool 95 | wantPrerelease bool 96 | wantDiscussionCategory string 97 | wantGenerateNotes bool 98 | wantNotes string 99 | wantNotesFilename string 100 | wantTarget string 101 | wantTitle string 102 | wantAssetFiles []string 103 | wantBumpType bump.BumpType 104 | wantSuffix string 105 | wantYes bool 106 | }{ 107 | "repository given": { 108 | command: "bump -R johnmanjiro13/gh-bump", 109 | wantRepo: "johnmanjiro13/gh-bump", 110 | }, 111 | "current repository": { 112 | command: "bump", 113 | wantRepo: "", 114 | }, 115 | "with draft": { 116 | command: "bump --draft", 117 | wantDraft: true, 118 | }, 119 | "with prerelease": { 120 | command: "bump --prerelease", 121 | wantPrerelease: true, 122 | }, 123 | "with discussion category": { 124 | command: "bump --discussion-category category!", 125 | wantDiscussionCategory: "category!", 126 | }, 127 | "with generate-notes": { 128 | command: "bump --generate-notes", 129 | wantGenerateNotes: true, 130 | }, 131 | "with notes": { 132 | command: "bump --notes release", 133 | wantNotes: "release", 134 | }, 135 | "with notes file": { 136 | command: "bump --notes-file filename", 137 | wantNotesFilename: "filename", 138 | }, 139 | "with target": { 140 | command: "bump --target feature", 141 | wantTarget: "feature", 142 | }, 143 | "with title": { 144 | command: "bump -t test_title", 145 | wantTitle: "test_title", 146 | }, 147 | "with asset files with comma": { 148 | command: "bump --asset-files file1,file2", 149 | wantAssetFiles: []string{"file1", "file2"}, 150 | }, 151 | "with asset files with multiple flags": { 152 | command: "bump --asset-files file1 --asset-files file2", 153 | wantAssetFiles: []string{"file1", "file2"}, 154 | }, 155 | "with bump type": { 156 | command: "bump --bump-type major", 157 | wantBumpType: bump.Major, 158 | }, 159 | "with suffix": { 160 | command: "bump --suffix test", 161 | wantSuffix: "test", 162 | }, 163 | "with yes": { 164 | command: "bump --yes", 165 | wantYes: true, 166 | }, 167 | } 168 | 169 | for name, tt := range tests { 170 | t.Run(name, func(t *testing.T) { 171 | bumper := &mockBumper{} 172 | cmd := bump.NewRootCmd(bumper) 173 | cmd.SetArgs(strings.Split(tt.command, " ")[1:]) 174 | 175 | assert.NoError(t, cmd.Execute()) 176 | assert.Equal(t, tt.wantRepo, bumper.repository) 177 | assert.Equal(t, tt.wantDraft, bumper.isDraft) 178 | assert.Equal(t, tt.wantPrerelease, bumper.isPrerelease) 179 | assert.Equal(t, tt.wantDiscussionCategory, bumper.discussionCategory) 180 | assert.Equal(t, tt.wantGenerateNotes, bumper.generateNotes) 181 | assert.Equal(t, tt.wantNotes, bumper.notes) 182 | assert.Equal(t, tt.wantNotesFilename, bumper.notesFilename) 183 | assert.Equal(t, tt.wantTarget, bumper.target) 184 | assert.Equal(t, tt.wantTitle, bumper.title) 185 | assert.Equal(t, tt.wantAssetFiles, bumper.assetFiles) 186 | assert.Equal(t, tt.wantBumpType, bumper.bumpType) 187 | assert.Equal(t, tt.wantSuffix, bumper.suffix) 188 | assert.Equal(t, tt.wantYes, bumper.yes) 189 | }) 190 | } 191 | } 192 | --------------------------------------------------------------------------------