├── .github └── workflows │ ├── ci.yml │ ├── sync-to-gitlab-cleanup.yml │ └── sync-to-gitlab.yml ├── .gitignore ├── .gitlab-ci.yml ├── .golangci.yaml ├── .goreleaser.yaml ├── .npm-binary-releaser.yaml ├── CODE_OF_CONDUCT.md ├── Dockerfile ├── LICENSE ├── README.md ├── cmd └── semantic-release │ └── main.go ├── docker └── entrypoint.sh ├── docs └── upgrade-from-v1-to-v2.md ├── go.mod ├── go.sum ├── pkg ├── analyzer │ ├── commit_analyzer.go │ ├── commit_analyzer.pb.go │ ├── commit_analyzer.proto │ ├── commit_analyzer_grpc.pb.go │ └── commit_analyzer_wrapper.go ├── condition │ ├── ci_condition.go │ ├── ci_condition.pb.go │ ├── ci_condition.proto │ ├── ci_condition_grpc.pb.go │ └── ci_condition_wrapper.go ├── config │ ├── config.go │ ├── config.pb.go │ └── config.proto ├── generator │ ├── changelog_generator.go │ ├── changelog_generator.pb.go │ ├── changelog_generator.proto │ ├── changelog_generator_grpc.pb.go │ └── changelog_generator_wrapper.go ├── hooks │ ├── hooks.go │ ├── hooks.pb.go │ ├── hooks.proto │ ├── hooks_grpc.pb.go │ └── hooks_wrapper.go ├── plugin │ ├── client.go │ ├── discovery │ │ ├── discovery.go │ │ ├── download.go │ │ ├── local.go │ │ └── resolver │ │ │ ├── github │ │ │ ├── github.go │ │ │ └── known_plugins.go │ │ │ ├── registry │ │ │ └── resolver.go │ │ │ └── resolver.go │ ├── grpc.go │ ├── manager │ │ └── manager.go │ ├── plugin.go │ ├── plugin_test.go │ ├── serve.go │ ├── sysprocattr.go │ └── sysprocattr_linux.go ├── provider │ ├── provider.go │ ├── provider.pb.go │ ├── provider.proto │ ├── provider_grpc.pb.go │ └── wrapper.go ├── semrel │ ├── releases.go │ ├── releases_test.go │ ├── semrel.go │ ├── semrel_test.go │ ├── structs.pb.go │ └── structs.proto └── updater │ ├── updater.go │ ├── updater.pb.go │ ├── updater.proto │ ├── updater_grpc.pb.go │ ├── updater_test.go │ └── updater_wrapper.go └── scripts └── generate.sh /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - '**' 6 | pull_request: 7 | branches: 8 | - '**' 9 | 10 | jobs: 11 | lint: 12 | name: Lint 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - uses: actions/setup-go@v5 17 | with: 18 | go-version: '1.23' 19 | - uses: golangci/golangci-lint-action@v6 20 | with: 21 | args: --timeout=5m 22 | 23 | build: 24 | name: Build and Run 25 | runs-on: ${{ matrix.os }} 26 | needs: lint 27 | strategy: 28 | fail-fast: true 29 | matrix: 30 | os: [macos-latest, windows-latest, ubuntu-latest] 31 | steps: 32 | - uses: actions/checkout@v4 33 | - uses: actions/setup-go@v5 34 | with: 35 | go-version: '1.23' 36 | - run: go build ./cmd/semantic-release/ 37 | env: 38 | CGO_ENABLED: 0 39 | - run: go test -v ./... 40 | - run: ./semantic-release --dry --allow-no-changes --no-ci 41 | env: 42 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 43 | shell: bash 44 | 45 | release: 46 | name: Release 47 | runs-on: ubuntu-latest 48 | needs: build 49 | steps: 50 | - uses: actions/checkout@v4 51 | - uses: actions/setup-go@v5 52 | with: 53 | go-version: '1.23' 54 | - run: go build ./cmd/semantic-release/ 55 | - uses: docker/login-action@v3 56 | with: 57 | registry: ghcr.io 58 | username: ${{ github.actor }} 59 | password: ${{ secrets.GITHUB_TOKEN }} 60 | - uses: docker/login-action@v3 61 | with: 62 | registry: registry.gitlab.com 63 | username: ${{ secrets.GITLAB_REGISTRY_USER }} 64 | password: ${{ secrets.GITLAB_REGISTRY_TOKEN }} 65 | - uses: go-semantic-release/action@v1 66 | with: 67 | bin: ./semantic-release 68 | hooks: goreleaser,npm-binary-releaser 69 | prerelease: true 70 | env: 71 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 72 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 73 | -------------------------------------------------------------------------------- /.github/workflows/sync-to-gitlab-cleanup.yml: -------------------------------------------------------------------------------- 1 | name: Sync to GitLab Cleanup 2 | on: 3 | delete 4 | 5 | jobs: 6 | cleanup: 7 | if: github.event.ref_type == 'branch' 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Cleanup 11 | run: | 12 | git init 13 | git remote add gitlab https://oauth2:${GITLAB_TOKEN}@gitlab.com/go-semantic-release/semantic-release.git 14 | git push gitlab :${{ github.event.ref }} 15 | env: 16 | GITLAB_TOKEN: ${{ secrets.GITLAB_PUSH_TOKEN }} 17 | -------------------------------------------------------------------------------- /.github/workflows/sync-to-gitlab.yml: -------------------------------------------------------------------------------- 1 | name: Sync to GitLab 2 | on: 3 | push: 4 | branches: 5 | - '**' 6 | 7 | jobs: 8 | sync: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | with: 13 | fetch-depth: 0 14 | - name: Sync to GitLab 15 | run: | 16 | git remote add gitlab https://oauth2:${GITLAB_TOKEN}@gitlab.com/go-semantic-release/semantic-release.git 17 | git push gitlab ${{ github.ref_name }} --force 18 | env: 19 | GITLAB_TOKEN: ${{ secrets.GITLAB_PUSH_TOKEN }} 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .envrc 2 | test/.npmrc 3 | .idea/ 4 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: golang:1.23 2 | 3 | stages: 4 | - test 5 | - build 6 | 7 | test: 8 | stage: test 9 | except: 10 | - tags 11 | script: 12 | - go test -v ./... 13 | 14 | build: 15 | stage: build 16 | except: 17 | - tags 18 | script: 19 | - go build ./cmd/semantic-release/ 20 | - ./semantic-release --dry --allow-no-changes --no-ci 21 | -------------------------------------------------------------------------------- /.golangci.yaml: -------------------------------------------------------------------------------- 1 | linters: 2 | enable: 3 | - errorlint 4 | - forbidigo 5 | - gochecknoinits 6 | - gocritic 7 | - goconst 8 | - gocyclo 9 | - gofumpt 10 | - goimports 11 | - misspell 12 | - revive 13 | - unconvert 14 | - unparam 15 | - wastedassign 16 | 17 | linters-settings: 18 | gocyclo: 19 | min-complexity: 12 20 | gofumpt: 21 | extra-rules: true 22 | govet: 23 | enable-all: true 24 | disable: 25 | - fieldalignment 26 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | builds: 2 | - env: 3 | - CGO_ENABLED=0 4 | targets: 5 | - linux_amd64 6 | - linux_arm64 7 | - darwin_amd64 8 | - darwin_arm64 9 | - linux_arm 10 | - windows_amd64 11 | main: ./cmd/semantic-release/ 12 | ldflags: 13 | - -extldflags '-static' 14 | - -s -w -X main.SRVERSION={{.Version}} 15 | 16 | archives: 17 | - format: binary 18 | name_template: '{{ .Binary }}_v{{ .Version }}_{{ .Os }}_{{ .Arch }}' 19 | 20 | checksum: 21 | name_template: '{{ .ProjectName }}_v{{ .Version }}_checksums.txt' 22 | 23 | dockers: 24 | - image_templates: 25 | - "ghcr.io/go-semantic-release/semantic-release:latest" 26 | - "ghcr.io/go-semantic-release/semantic-release:{{.Version}}" 27 | - "registry.gitlab.com/go-semantic-release/semantic-release:latest" 28 | - "registry.gitlab.com/go-semantic-release/semantic-release:{{.Version}}" 29 | build_flag_templates: 30 | - "--label=org.opencontainers.image.created={{.Date}}" 31 | - "--label=org.opencontainers.image.title={{.ProjectName}}" 32 | - "--label=org.opencontainers.image.revision={{.FullCommit}}" 33 | - "--label=org.opencontainers.image.version={{.Version}}" 34 | - "--label=org.opencontainers.image.license=MIT" 35 | - "--label=org.opencontainers.image.source=https://github.com/go-semantic-release/semantic-release.git" 36 | extra_files: 37 | - ./docker/entrypoint.sh 38 | -------------------------------------------------------------------------------- /.npm-binary-releaser.yaml: -------------------------------------------------------------------------------- 1 | name: go-semantic-release 2 | description: "fully automated package/module/image publishing" 3 | license: MIT 4 | packageNamePrefix: "@go-semantic-release/" 5 | noPrefixForMainPackage: true 6 | publish: true 7 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at github+coc@christophwitzko.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | 3 | ADD ./docker/entrypoint.sh /usr/local/bin/docker-entrypoint 4 | RUN apk update && apk add --no-cache git ca-certificates && update-ca-certificates 5 | COPY "./semantic-release" /usr/local/bin/semantic-release 6 | 7 | ENTRYPOINT ["docker-entrypoint"] 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 Christoph Witzko 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # :package::rocket: semantic-release 2 | [![CI](https://github.com/go-semantic-release/semantic-release/workflows/CI/badge.svg?branch=master)](https://github.com/go-semantic-release/semantic-release/actions?query=workflow%3ACI+branch%3Amaster) 3 | [![pipeline status](https://gitlab.com/go-semantic-release/semantic-release/badges/master/pipeline.svg)](https://gitlab.com/go-semantic-release/semantic-release/pipelines) 4 | [![Go Report Card](https://goreportcard.com/badge/github.com/go-semantic-release/semantic-release)](https://goreportcard.com/report/github.com/go-semantic-release/semantic-release) 5 | [![PkgGoDev](https://pkg.go.dev/badge/github.com/go-semantic-release/semantic-release/v2)](https://pkg.go.dev/github.com/go-semantic-release/semantic-release/v2) 6 | 7 | > fully automated package/module/image publishing 8 | 9 | This project aims to be an alternative to the original [semantic-release](https://github.com/semantic-release/semantic-release) implementation. Using Go, `semantic-release` can be installed by downloading a single binary and is, therefore, easier to install and does not require Node.js and npm. Furthermore, `semantic-release` has a built-in plugin system that allows to extend and customize its functionality. 10 | 11 | ### Features 12 | 13 | - Automated version and release management 14 | - No external dependencies required 15 | - Runs on Linux, macOS and Windows 16 | - Fully extensible via plugins 17 | - Automated changelog generation 18 | - Supports GitHub, GitLab and git 19 | - Support for maintaining multiple major version releases 20 | 21 | ## How does it work? 22 | Instead of writing [meaningless commit messages](http://whatthecommit.com/), we can take our time to think about the changes in the codebase and write them down. Following the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification it is then possible to generate a helpful changelog and to derive the next semantic version number from them. 23 | 24 | When `semantic-release` is setup it will do that after every successful continuous integration build of your default branch and publish the new version for you. This way no human is directly involved in the release process and your releases are guaranteed to be unromantic and unsentimental. 25 | 26 | _Source: [semantic-release/semantic-release#how-does-it-work](https://github.com/semantic-release/semantic-release#how-does-it-work)_ 27 | 28 | You can enforce semantic commit messages using [a git hook](https://github.com/hazcod/semantic-commit-hook). 29 | 30 | ## Installation 31 | 32 | 33 | ### Option 1: Use the go-semantic-release GitHub Action ([go-semantic-release/action](https://github.com/go-semantic-release/action)) 34 | 35 | ### Option 2: Install manually 36 | 37 | ```bash 38 | curl -SL https://get-release.xyz/semantic-release/linux/amd64 -o ./semantic-release && chmod +x ./semantic-release 39 | ``` 40 | 41 | ### Option 3: Install via npm 42 | 43 | ```bash 44 | npm install -g go-semantic-release 45 | ``` 46 | 47 | ## Examples 48 | 49 | ### Releasing a Go application with GitHub Actions 50 | Full example can be found at [go-semantic-release/example-go-application](https://github.com/go-semantic-release/example-go-application). 51 | 52 | Example [.github/workflows/ci.yml](https://github.com/go-semantic-release/example-go-application/blob/main/.github/workflows/ci.yml) config: 53 | ```yaml 54 | name: CI 55 | on: 56 | push: 57 | branches: 58 | - '**' 59 | pull_request: 60 | branches: 61 | - '**' 62 | jobs: 63 | lint: 64 | runs-on: ubuntu-latest 65 | steps: 66 | - uses: actions/checkout@v3 67 | - uses: actions/setup-go@v3 68 | with: 69 | go-version: 1.19 70 | - uses: golangci/golangci-lint-action@v3 71 | test: 72 | runs-on: ubuntu-latest 73 | needs: lint 74 | steps: 75 | - uses: actions/checkout@v3 76 | - uses: actions/setup-go@v3 77 | with: 78 | go-version: 1.19 79 | - run: go test -v ./... 80 | release: 81 | runs-on: ubuntu-latest 82 | needs: test 83 | permissions: 84 | contents: write 85 | steps: 86 | - uses: actions/checkout@v3 87 | - uses: actions/setup-go@v3 88 | with: 89 | go-version: 1.19 90 | - uses: go-semantic-release/action@v1 91 | with: 92 | hooks: goreleaser 93 | env: 94 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 95 | ``` 96 | 97 | ### Example GitLab CI Config 98 | 99 | #### GitLab token 100 | It is necessary to create a new Gitlab personal access token with the `api` scope [here](https://gitlab.com/profile/personal_access_tokens). 101 | Ensure the CI variable is protected and masked as the `GITLAB_TOKEN` has a lot of rights. There is an open issue for project specific [tokens](https://gitlab.com/gitlab-org/gitlab/issues/756) 102 | You can set the GitLab token via the `GITLAB_TOKEN` environment variable or the `-token` flag. 103 | 104 | .gitlab-ci.yml 105 | ```yml 106 | stages: 107 | # other stages 108 | - release 109 | 110 | release: 111 | image: 112 | name: registry.gitlab.com/go-semantic-release/semantic-release:latest 113 | entrypoint: [""] 114 | stage: release 115 | # when: manual # Add this if you want to manually create releases 116 | only: 117 | - master 118 | script: 119 | - semantic-release # Add --allow-no-changes if you want to create a release for each push 120 | ``` 121 | 122 | #### Job Token 123 | If you do not provide a PAT the [job token](https://docs.gitlab.com/ee/ci/jobs/ci_job_token.html) will be used. 124 | This restricted token can create releases but not read commits. The [git strategy](https://docs.gitlab.com/ee/ci/runners/configure_runners.html#git-strategy) 125 | must be set to clone so that we can read the commits from the repository. See example below 126 | 127 | .gitlab-ci.yml 128 | ```yml 129 | variables: 130 | GIT_STRATEGY: clone 131 | 132 | stages: 133 | # other stages 134 | - release 135 | 136 | release: 137 | image: 138 | name: registry.gitlab.com/go-semantic-release/semantic-release:latest 139 | entrypoint: [""] 140 | stage: release 141 | # when: manual # Add this if you want to manually create releases 142 | only: 143 | - master 144 | script: 145 | - semantic-release 146 | # - semantic-release --allow-no-changes # create a release for each push 147 | # - semantic-release --provider gitlab --provider-opt log_order=ctime # traverse commits by committer time (commits in merge requests will affect the calculated version) 148 | ``` 149 | 150 | ### Releasing a Go application with GitLab CI 151 | The full example can be found at https://gitlab.com/go-semantic-release/example-go-application. 152 | 153 | Example [.gitlab-ci.yml](https://gitlab.com/go-semantic-release/example-go-application/-/blob/main/.gitlab-ci.yml) config: 154 | ```yaml 155 | image: golang:1.19 156 | 157 | stages: 158 | - test 159 | - release 160 | 161 | test: 162 | stage: test 163 | except: 164 | - tags 165 | script: 166 | - go test -v ./... 167 | - go build ./ 168 | - ./example-go-application 169 | 170 | release: 171 | stage: release 172 | only: 173 | - main 174 | script: 175 | - curl -SL https://get-release.xyz/semantic-release/linux/amd64 -o ./semantic-release && chmod +x ./semantic-release 176 | - ./semantic-release --hooks goreleaser 177 | ``` 178 | 179 | ## Plugin System 180 | 181 | Since v2, semantic-release is equipped with a plugin system. The plugins are standalone binaries that use [hashicorp/go-plugin](https://github.com/hashicorp/go-plugin) as a plugin library. `semantic-release` automatically downloads the necessary plugins if they don't exist locally. The plugins are stored in the `.semrel` directory of the current working directory in the following format: `.semrel/_///`. The go-semantic-release plugins registry (https://registry.go-semantic-release.xyz/) is used to resolve plugins and to download the correct binary. With the new [plugin-registry](https://github.com/go-semantic-release/plugin-registry) service the API also supports batch requests to resolve multiple plugins at once and caching of the plugins. 182 | 183 | ### Running semantic-release in an air-gapped environment 184 | As plugins are only downloaded if they do not exist in the `.semrel` folder, it is possible to download the plugins and archive the `.semrel` folder. This way it is possible to run `semantic-release` in an air-gapped environment. 185 | 186 | ```bash 187 | # specify all required plugins and download them 188 | ./semantic-release --download-plugins --show-progress --provider github --ci-condition github --hooks goreleaser 189 | # archive the .semrel folder 190 | tar -czvf ./semrel-plugins.tgz .semrel/ 191 | 192 | # copy the archive to the air-gapped environment 193 | 194 | # extract the archive 195 | tar -xzvf ./semrel-plugins.tgz 196 | # run semantic-release 197 | ./semantic-release --provider github --condition github --hooks goreleaser 198 | ``` 199 | 200 | ### Plugins 201 | 202 | * Provider ([Docs](https://pkg.go.dev/github.com/go-semantic-release/semantic-release/v2/pkg/provider?tab=doc#Provider)) 203 | * [GitHub](https://github.com/go-semantic-release/provider-github) 204 | * [GitLab](https://github.com/go-semantic-release/provider-gitlab) 205 | * [Git](https://github.com/go-semantic-release/provider-git) 206 | * CI Condition ([Docs](https://pkg.go.dev/github.com/go-semantic-release/semantic-release/v2/pkg/condition?tab=doc#CICondition)) 207 | * [GitHub Actions](https://github.com/go-semantic-release/condition-github) 208 | * [GitLab CI](https://github.com/go-semantic-release/condition-gitlab) 209 | * [Bitbucket Pipelines](https://github.com/go-semantic-release/condition-bitbucket) 210 | * [Default](https://github.com/go-semantic-release/condition-default) 211 | * Commit Analyzer ([Docs](https://pkg.go.dev/github.com/go-semantic-release/semantic-release/v2/pkg/analyzer?tab=doc#CommitAnalyzer)) 212 | * [Conventional Commits](https://github.com/go-semantic-release/commit-analyzer-cz) 213 | * Changelog Generator ([Docs](https://pkg.go.dev/github.com/go-semantic-release/semantic-release/v2/pkg/generator?tab=doc#ChangelogGenerator)) 214 | * [Default](https://github.com/go-semantic-release/changelog-generator-default) 215 | * Hooks ([Docs](https://pkg.go.dev/github.com/go-semantic-release/semantic-release/v2/pkg/hooks?tab=doc#Hooks)) 216 | * [GoReleaser](https://github.com/go-semantic-release/hooks-goreleaser) 217 | * [npm-binary-releaser](https://github.com/go-semantic-release/hooks-npm-binary-releaser) 218 | * [plugin-registry-update](https://github.com/go-semantic-release/hooks-plugin-registry-update) 219 | * [exec](https://github.com/go-semantic-release/hooks-exec) 220 | * Files Updater ([Docs](https://pkg.go.dev/github.com/go-semantic-release/semantic-release/v2/pkg/updater?tab=doc#FilesUpdater)) 221 | * [npm](https://github.com/go-semantic-release/files-updater-npm) 222 | * [helm](https://github.com/go-semantic-release/files-updater-helm) 223 | 224 | ### Configuration 225 | 226 | Plugins can be configured using CLI flags or the `.semrelrc` config file. By using a `@` sign after the plugin name, the required version of the plugin can be specified. Otherwise, any locally installed version will be used. If the plugin does not exist locally, the latest version will be downloaded. This is an example of the `.semrelrc` config file: 227 | 228 | ```json 229 | { 230 | "plugins": { 231 | "commit-analyzer": { 232 | "name": "default@^1.0.0" 233 | }, 234 | "ci-condition": { 235 | "name": "default" 236 | }, 237 | "changelog-generator": { 238 | "name": "default", 239 | "options": { 240 | "emojis": "true" 241 | } 242 | }, 243 | "provider": { 244 | "name": "gitlab", 245 | "options": { 246 | "gitlab_projectid": "123456" 247 | } 248 | }, 249 | "files-updater": { 250 | "names": ["npm"] 251 | } 252 | } 253 | } 254 | ``` 255 | 256 | ## Beta release support 257 | Beta release support empowers you to release beta, rc, etc. versions with `semantic-release` (e.g. v2.0.0-beta.1). To enable this feature you need to create a new branch (e.g. beta/v2) and check in a `.semrelrc` file with the following content: 258 | ``` 259 | { 260 | "maintainedVersion": "2-beta" 261 | } 262 | ``` 263 | If you commit to this branch a new incremental pre-release is created everytime you push. (2.0.0-beta.1, 2.0.0-beta.2, ...) 264 | 265 | ## Licence 266 | 267 | The [MIT License (MIT)](http://opensource.org/licenses/MIT) 268 | 269 | Copyright © 2024 [Christoph Witzko](https://twitter.com/christophwitzko) 270 | -------------------------------------------------------------------------------- /cmd/semantic-release/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "log" 7 | "os" 8 | "os/signal" 9 | "strings" 10 | "syscall" 11 | 12 | "github.com/Masterminds/semver/v3" 13 | "github.com/go-semantic-release/semantic-release/v2/pkg/config" 14 | "github.com/go-semantic-release/semantic-release/v2/pkg/generator" 15 | "github.com/go-semantic-release/semantic-release/v2/pkg/hooks" 16 | "github.com/go-semantic-release/semantic-release/v2/pkg/plugin/manager" 17 | "github.com/go-semantic-release/semantic-release/v2/pkg/provider" 18 | "github.com/go-semantic-release/semantic-release/v2/pkg/semrel" 19 | "github.com/spf13/cobra" 20 | ) 21 | 22 | // SRVERSION is the semantic-release version (added at compile time) 23 | var SRVERSION string 24 | 25 | var logger = log.New(os.Stderr, "[go-semantic-release]: ", 0) 26 | 27 | var exitHandler func() 28 | 29 | func exitIfError(err error, exitCode ...int) { 30 | if err == nil { 31 | return 32 | } 33 | logger.Println(err) 34 | if exitHandler != nil { 35 | exitHandler() 36 | } 37 | if len(exitCode) == 1 { 38 | os.Exit(exitCode[0]) 39 | return 40 | } 41 | os.Exit(1) 42 | } 43 | 44 | func main() { 45 | cmd := &cobra.Command{ 46 | Use: "semantic-release", 47 | Short: "semantic-release - fully automated package/module/image publishing", 48 | Run: cliHandler, 49 | Version: SRVERSION, 50 | } 51 | 52 | config.SetFlags(cmd) 53 | cobra.OnInitialize(func() { 54 | err := config.InitConfig(cmd) 55 | if err != nil { 56 | logger.Printf("Config error: %s", err.Error()) 57 | os.Exit(1) 58 | return 59 | } 60 | }) 61 | err := cmd.Execute() 62 | if err != nil { 63 | logger.Printf("ERROR: %s", err.Error()) 64 | os.Exit(1) 65 | } 66 | } 67 | 68 | func mergeConfigWithDefaults(defaults, conf map[string]string) { 69 | for k, v := range conf { 70 | defaults[k] = v 71 | // case-insensitive overwrite default values 72 | keyLower := strings.ToLower(k) 73 | for dk := range defaults { 74 | if strings.ToLower(dk) == keyLower && dk != k { 75 | defaults[dk] = v 76 | } 77 | } 78 | } 79 | } 80 | 81 | //gocyclo:ignore 82 | func cliHandler(cmd *cobra.Command, _ []string) { 83 | logger.Printf("version: %s\n", SRVERSION) 84 | 85 | conf, err := config.NewConfig(cmd) 86 | exitIfError(err) 87 | 88 | pluginManager, err := manager.New(conf) 89 | exitIfError(err) 90 | exitHandler = func() { 91 | logger.Println("stopping plugins...") 92 | pluginManager.Stop() 93 | } 94 | defer exitHandler() 95 | 96 | c := make(chan os.Signal, 1) 97 | signal.Notify(c, os.Interrupt, syscall.SIGTERM) 98 | go func() { 99 | termSignal := <-c 100 | logger.Println("terminating...") 101 | exitIfError(fmt.Errorf("received signal: %s", termSignal)) 102 | }() 103 | 104 | if conf.DownloadPlugins { 105 | exitIfError(pluginManager.FetchAllPlugins()) 106 | logger.Println("all plugins were downloaded!") 107 | return 108 | } 109 | 110 | if !conf.PluginResolverDisableBatchPrefetch { 111 | logger.Println("trying to prefetch plugins...") 112 | pluginsWerePrefetched, _, pErr := pluginManager.PrefetchAllPluginsIfBatchIsPossible() 113 | if pErr != nil { 114 | logger.Printf("warning: failed to prefetch plugins: %v", pErr) 115 | } else { 116 | if pluginsWerePrefetched { 117 | logger.Println("all plugins were prefetched!") 118 | } else { 119 | logger.Println("prefetching plugins was not possible.") 120 | } 121 | } 122 | } 123 | 124 | ci, err := pluginManager.GetCICondition() 125 | exitIfError(err) 126 | ciName := ci.Name() 127 | logger.Printf("ci-condition plugin: %s@%s\n", ciName, ci.Version()) 128 | 129 | prov, err := pluginManager.GetProvider() 130 | exitIfError(err) 131 | provName := prov.Name() 132 | logger.Printf("provider plugin: %s@%s\n", provName, prov.Version()) 133 | 134 | if conf.ProviderOpts["token"] == "" { 135 | conf.ProviderOpts["token"] = conf.Token 136 | } 137 | err = prov.Init(conf.ProviderOpts) 138 | exitIfError(err) 139 | 140 | logger.Println("getting default branch...") 141 | repoInfo, err := prov.GetInfo() 142 | exitIfError(err) 143 | logger.Println("found default branch: " + repoInfo.DefaultBranch) 144 | if repoInfo.Private { 145 | logger.Println("repo is private") 146 | } 147 | 148 | currentBranch := ci.GetCurrentBranch() 149 | if currentBranch == "" { 150 | exitIfError(fmt.Errorf("current branch not found")) 151 | } 152 | logger.Println("found current branch: " + currentBranch) 153 | 154 | if !conf.AllowMaintainedVersionOnDefaultBranch && conf.MaintainedVersion != "" && currentBranch == repoInfo.DefaultBranch { 155 | exitIfError(fmt.Errorf("maintained version not allowed on default branch")) 156 | } 157 | 158 | if conf.MaintainedVersion != "" { 159 | logger.Println("found maintained version: " + conf.MaintainedVersion) 160 | repoInfo.DefaultBranch = "*" 161 | } 162 | 163 | currentSha := ci.GetCurrentSHA() 164 | logger.Println("found current sha: " + currentSha) 165 | 166 | hooksExecutor, err := pluginManager.GetChainedHooksExecutor() 167 | exitIfError(err) 168 | 169 | hooksNames := hooksExecutor.GetNameVersionPairs() 170 | if len(hooksNames) > 0 { 171 | logger.Printf("hooks plugins: %s\n", strings.Join(hooksNames, ", ")) 172 | } 173 | 174 | hooksConfig := map[string]string{ 175 | "provider": provName, 176 | "ci": ciName, 177 | "currentBranch": currentBranch, 178 | "currentSha": currentSha, 179 | "defaultBranch": repoInfo.DefaultBranch, 180 | "prerelease": fmt.Sprintf("%t", conf.Prerelease), 181 | } 182 | mergeConfigWithDefaults(hooksConfig, conf.HooksOpts) 183 | 184 | exitIfError(hooksExecutor.Init(hooksConfig)) 185 | 186 | if !conf.NoCi { 187 | logger.Println("running CI condition...") 188 | conditionConfig := map[string]string{ 189 | "token": conf.Token, 190 | "defaultBranch": repoInfo.DefaultBranch, 191 | "private": fmt.Sprintf("%t", repoInfo.Private), 192 | } 193 | mergeConfigWithDefaults(conditionConfig, conf.CiConditionOpts) 194 | 195 | err = ci.RunCondition(conditionConfig) 196 | if err != nil { 197 | herr := hooksExecutor.NoRelease(&hooks.NoReleaseConfig{ 198 | Reason: hooks.NoReleaseReason_CONDITION, 199 | Message: err.Error(), 200 | }) 201 | if herr != nil { 202 | logger.Printf("there was an error executing the hooks plugins: %s", herr.Error()) 203 | } 204 | exitIfError(err, 66) 205 | } 206 | 207 | } 208 | 209 | logger.Println("getting latest release...") 210 | matchRegex := "" 211 | match := strings.TrimSpace(conf.Match) 212 | if match != "" { 213 | logger.Printf("getting latest release matching %s...", match) 214 | matchRegex = "^" + match 215 | } 216 | releases, err := prov.GetReleases(matchRegex) 217 | exitIfError(err) 218 | release, err := semrel.GetLatestReleaseFromReleases(releases, conf.MaintainedVersion) 219 | exitIfError(err) 220 | logger.Println("found version: " + release.Version) 221 | 222 | if strings.Contains(conf.MaintainedVersion, "-") && semver.MustParse(release.Version).Prerelease() == "" { 223 | exitIfError(fmt.Errorf("no pre-release for this version possible")) 224 | } 225 | 226 | logger.Println("getting commits...") 227 | rawCommits, err := prov.GetCommits(release.SHA, currentSha) 228 | exitIfError(err) 229 | 230 | logger.Println("analyzing commits...") 231 | commitAnalyzer, err := pluginManager.GetCommitAnalyzer() 232 | exitIfError(err) 233 | logger.Printf("commit-analyzer plugin: %s@%s\n", commitAnalyzer.Name(), commitAnalyzer.Version()) 234 | exitIfError(commitAnalyzer.Init(conf.CommitAnalyzerOpts)) 235 | 236 | commits := commitAnalyzer.Analyze(rawCommits) 237 | 238 | logger.Println("calculating new version...") 239 | newVer := semrel.GetNewVersion(conf, commits, release) 240 | if newVer == "" { 241 | herr := hooksExecutor.NoRelease(&hooks.NoReleaseConfig{ 242 | Reason: hooks.NoReleaseReason_NO_CHANGE, 243 | Message: "", 244 | }) 245 | if herr != nil { 246 | logger.Printf("there was an error executing the hooks plugins: %s", herr.Error()) 247 | } 248 | errNoChange := errors.New("no change") 249 | if conf.AllowNoChanges { 250 | exitIfError(errNoChange, 0) 251 | } else { 252 | exitIfError(errNoChange, 65) 253 | } 254 | } 255 | logger.Println("new version: " + newVer) 256 | 257 | logger.Println("generating changelog...") 258 | changelogGenerator, err := pluginManager.GetChangelogGenerator() 259 | exitIfError(err) 260 | logger.Printf("changelog-generator plugin: %s@%s\n", changelogGenerator.Name(), changelogGenerator.Version()) 261 | exitIfError(changelogGenerator.Init(conf.ChangelogGeneratorOpts)) 262 | 263 | changelogRes := changelogGenerator.Generate(&generator.ChangelogGeneratorConfig{ 264 | Commits: commits, 265 | LatestRelease: release, 266 | NewVersion: newVer, 267 | }) 268 | if conf.Changelog != "" { 269 | oldFile := make([]byte, 0) 270 | if conf.PrependChangelog { 271 | oldFileData, err := os.ReadFile(conf.Changelog) 272 | if err == nil { 273 | oldFile = append([]byte("\n"), oldFileData...) 274 | } 275 | } 276 | changelogData := append([]byte(changelogRes), oldFile...) 277 | exitIfError(os.WriteFile(conf.Changelog, changelogData, 0o644)) 278 | } 279 | 280 | if conf.Dry { 281 | if conf.VersionFile { 282 | exitIfError(os.WriteFile(".version-unreleased", []byte(newVer), 0o644)) 283 | } 284 | exitIfError(errors.New("DRY RUN: no release was created"), 0) 285 | } 286 | 287 | logger.Println("creating release...") 288 | newRelease := &provider.CreateReleaseConfig{ 289 | Changelog: changelogRes, 290 | NewVersion: newVer, 291 | Prerelease: conf.Prerelease, 292 | Branch: currentBranch, 293 | SHA: currentSha, 294 | } 295 | exitIfError(prov.CreateRelease(newRelease)) 296 | 297 | if conf.Ghr { 298 | exitIfError(os.WriteFile(".ghr", []byte(fmt.Sprintf("-u %s -r %s v%s", repoInfo.Owner, repoInfo.Repo, newVer)), 0o644)) 299 | } 300 | 301 | if conf.VersionFile { 302 | exitIfError(os.WriteFile(".version", []byte(newVer), 0o644)) 303 | } 304 | 305 | if len(conf.UpdateFiles) == 0 && len(conf.FilesUpdaterPlugins) > 0 { 306 | logger.Println("warning: file update plugins found but no files marked for update. You may be missing the update flag, e.g. --update package.json") 307 | } 308 | 309 | if len(conf.UpdateFiles) > 0 { 310 | logger.Println("updating files...") 311 | updater, err := pluginManager.GetChainedUpdater() 312 | exitIfError(err) 313 | logger.Printf("files-updater plugins: %s\n", strings.Join(updater.GetNameVersionPairs(), ", ")) 314 | exitIfError(updater.Init(conf.FilesUpdaterOpts)) 315 | 316 | for _, f := range conf.UpdateFiles { 317 | exitIfError(updater.Apply(f, newVer)) 318 | } 319 | } 320 | 321 | herr := hooksExecutor.Success(&hooks.SuccessHookConfig{ 322 | Commits: commits, 323 | PrevRelease: release, 324 | NewRelease: &semrel.Release{ 325 | SHA: currentSha, 326 | Version: newVer, 327 | }, 328 | Changelog: changelogRes, 329 | RepoInfo: repoInfo, 330 | }) 331 | exitIfError(herr) 332 | 333 | logger.Println("done.") 334 | } 335 | -------------------------------------------------------------------------------- /docker/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | if [ "$GITLAB_CI" == "true" ]; then 5 | # Gitlab CI environment detected, skipping entrypoint 6 | exit 0 7 | fi 8 | 9 | if [ "$1" == "semantic-release" ]; then 10 | # remove first argument 11 | shift 1 12 | fi 13 | 14 | exec semantic-release "$@" 15 | -------------------------------------------------------------------------------- /docs/upgrade-from-v1-to-v2.md: -------------------------------------------------------------------------------- 1 | ## 🚨 Upgrade to semantic-release v2 🚨 2 | 3 | `semantic-release` v2 is now available. If you run into any problems, please create a [GitHub issue](https://github.com/go-semantic-release/semantic-release/issues/new). You can always downgrade to v1 with: 4 | 5 | ``` 6 | curl -SL https://get-release.xyz/semantic-release/linux/amd64/1.22.1 -o ./semantic-release && chmod +x ./semantic-release 7 | ``` 8 | 9 | ### Breaking changes 10 | 11 | * It is now necessary to use **double dashes** for CLI flags (e.g. `--dry`) 12 | * **Travis CI** support has been **removed** 13 | * Some CLI flags have changed: 14 | 15 | | v1 | v2 | 16 | |:--------------------------:|:------------------------------------------------:| 17 | | `-vf` | `-f` | 18 | | `--noci` | `--no-ci` | 19 | | `--ghe-host ` | `--provider-opt "github_enterprise_host="` | 20 | | `--travis-com` | _removed_ | 21 | | `--gitlab` | `--provider gitlab` | 22 | | `--gitlab-base-url ` | `--provider-opt "gitlab_baseurl="` | 23 | | `--gitlab-project-id ` | `--provider-opt "gitlab_projectid="` | 24 | | `--slug` | `--provider-opt "slug="` | 25 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/go-semantic-release/semantic-release/v2 2 | 3 | go 1.23 4 | 5 | require ( 6 | github.com/Masterminds/semver/v3 v3.3.0 7 | github.com/cavaliergopher/grab/v3 v3.0.1 8 | github.com/go-semantic-release/plugin-registry v1.26.1 9 | github.com/google/go-github/v59 v59.0.0 10 | github.com/hashicorp/go-hclog v1.6.3 11 | github.com/hashicorp/go-plugin v1.6.1 12 | github.com/schollz/progressbar/v3 v3.16.1 13 | github.com/spf13/cobra v1.8.1 14 | github.com/spf13/viper v1.19.0 15 | github.com/stretchr/testify v1.9.0 16 | golang.org/x/oauth2 v0.23.0 17 | google.golang.org/grpc v1.67.1 18 | google.golang.org/protobuf v1.35.1 19 | ) 20 | 21 | require ( 22 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 23 | github.com/fatih/color v1.17.0 // indirect 24 | github.com/fsnotify/fsnotify v1.7.0 // indirect 25 | github.com/golang/protobuf v1.5.4 // indirect 26 | github.com/google/go-querystring v1.1.0 // indirect 27 | github.com/hashicorp/hcl v1.0.0 // indirect 28 | github.com/hashicorp/yamux v0.1.2 // indirect 29 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 30 | github.com/magiconair/properties v1.8.7 // indirect 31 | github.com/mattn/go-colorable v0.1.13 // indirect 32 | github.com/mattn/go-isatty v0.0.20 // indirect 33 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect 34 | github.com/mitchellh/go-testing-interface v1.14.1 // indirect 35 | github.com/mitchellh/mapstructure v1.5.0 // indirect 36 | github.com/oklog/run v1.1.0 // indirect 37 | github.com/pelletier/go-toml/v2 v2.2.3 // indirect 38 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 39 | github.com/rivo/uniseg v0.4.7 // indirect 40 | github.com/sagikazarmark/locafero v0.6.0 // indirect 41 | github.com/sagikazarmark/slog-shim v0.1.0 // indirect 42 | github.com/sourcegraph/conc v0.3.0 // indirect 43 | github.com/spf13/afero v1.11.0 // indirect 44 | github.com/spf13/cast v1.7.0 // indirect 45 | github.com/spf13/pflag v1.0.5 // indirect 46 | github.com/subosito/gotenv v1.6.0 // indirect 47 | go.uber.org/multierr v1.11.0 // indirect 48 | golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect 49 | golang.org/x/net v0.30.0 // indirect 50 | golang.org/x/sys v0.26.0 // indirect 51 | golang.org/x/term v0.25.0 // indirect 52 | golang.org/x/text v0.19.0 // indirect 53 | google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect 54 | gopkg.in/ini.v1 v1.67.0 // indirect 55 | gopkg.in/yaml.v3 v3.0.1 // indirect 56 | ) 57 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= 2 | github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= 3 | github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= 4 | github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= 5 | github.com/cavaliergopher/grab/v3 v3.0.1 h1:4z7TkBfmPjmLAAmkkAZNX/6QJ1nNFdv3SdIHXju0Fr4= 6 | github.com/cavaliergopher/grab/v3 v3.0.1/go.mod h1:1U/KNnD+Ft6JJiYoYBAimKH2XrYptb8Kl3DFGmsjpq4= 7 | github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM= 8 | github.com/chengxilo/virtualterm v1.0.4/go.mod h1:DyxxBZz/x1iqJjFxTFcr6/x+jSpqN0iwWCOK1q10rlY= 9 | github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 10 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 11 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 12 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= 13 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 14 | github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= 15 | github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= 16 | github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= 17 | github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= 18 | github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= 19 | github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= 20 | github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= 21 | github.com/go-semantic-release/plugin-registry v1.26.1 h1:wCqgReUWX5PHyyKZsqdK4bEWx+tliOFEUXB+4x3JcVQ= 22 | github.com/go-semantic-release/plugin-registry v1.26.1/go.mod h1:gScpRcO9ITYDBnoW2S54MsiosSGXPj/hDh0Yo1CmNW0= 23 | github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= 24 | github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 25 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 26 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 27 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 28 | github.com/google/go-github/v59 v59.0.0 h1:7h6bgpF5as0YQLLkEiVqpgtJqjimMYhBkD4jT5aN3VA= 29 | github.com/google/go-github/v59 v59.0.0/go.mod h1:rJU4R0rQHFVFDOkqGWxfLNo6vEk4dv40oDjhV/gH6wM= 30 | github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= 31 | github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= 32 | github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= 33 | github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= 34 | github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOsVdwI= 35 | github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0= 36 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 37 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 38 | github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= 39 | github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= 40 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 41 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 42 | github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= 43 | github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= 44 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 45 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 46 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 47 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 48 | github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= 49 | github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= 50 | github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 51 | github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= 52 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 53 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 54 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 55 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 56 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 57 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 58 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 59 | github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= 60 | github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 61 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= 62 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= 63 | github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= 64 | github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= 65 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 66 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 67 | github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= 68 | github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= 69 | github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= 70 | github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= 71 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 72 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= 73 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 74 | github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= 75 | github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 76 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 77 | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= 78 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 79 | github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk= 80 | github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0= 81 | github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= 82 | github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= 83 | github.com/schollz/progressbar/v3 v3.16.1 h1:RnF1neWZFzLCoGx8yp1yF7SDl4AzNDI5y4I0aUJRrZQ= 84 | github.com/schollz/progressbar/v3 v3.16.1/go.mod h1:I2ILR76gz5VXqYMIY/LdLecvMHDPVcQm3W/MSKi1TME= 85 | github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= 86 | github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= 87 | github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= 88 | github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= 89 | github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= 90 | github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= 91 | github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= 92 | github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= 93 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 94 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 95 | github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= 96 | github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= 97 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 98 | github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= 99 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 100 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 101 | github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= 102 | github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= 103 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 104 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 105 | golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= 106 | golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= 107 | golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= 108 | golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= 109 | golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= 110 | golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= 111 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 112 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 113 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 114 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 115 | golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 116 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 117 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 118 | golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= 119 | golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 120 | golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= 121 | golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= 122 | golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= 123 | golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= 124 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 125 | google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc= 126 | google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= 127 | google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= 128 | google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= 129 | google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= 130 | google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 131 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 132 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 133 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 134 | gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= 135 | gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 136 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 137 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 138 | -------------------------------------------------------------------------------- /pkg/analyzer/commit_analyzer.go: -------------------------------------------------------------------------------- 1 | package analyzer 2 | 3 | import ( 4 | "github.com/go-semantic-release/semantic-release/v2/pkg/semrel" 5 | ) 6 | 7 | type CommitAnalyzer interface { 8 | Init(map[string]string) error 9 | Name() string 10 | Version() string 11 | Analyze([]*semrel.RawCommit) []*semrel.Commit 12 | } 13 | -------------------------------------------------------------------------------- /pkg/analyzer/commit_analyzer.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option go_package = "github.com/go-semantic-release/semantic-release/v2/pkg/analyzer"; 3 | 4 | import "pkg/semrel/structs.proto"; 5 | 6 | message CommitAnalyzerInit { 7 | message Request { 8 | map config = 1; 9 | } 10 | message Response { 11 | string error = 1; 12 | } 13 | } 14 | 15 | message CommitAnalyzerName { 16 | message Request {} 17 | message Response { 18 | string name = 1; 19 | } 20 | } 21 | 22 | message CommitAnalyzerVersion { 23 | message Request {} 24 | message Response { 25 | string version = 1; 26 | } 27 | } 28 | 29 | message AnalyzeCommits { 30 | message Request { 31 | repeated RawCommit raw_commits = 1; 32 | } 33 | message Response { 34 | repeated Commit commits = 1; 35 | } 36 | } 37 | 38 | 39 | service CommitAnalyzerPlugin { 40 | rpc Init(CommitAnalyzerInit.Request) returns (CommitAnalyzerInit.Response); 41 | rpc Name(CommitAnalyzerName.Request) returns (CommitAnalyzerName.Response); 42 | rpc Version(CommitAnalyzerVersion.Request) returns (CommitAnalyzerVersion.Response); 43 | rpc Analyze(AnalyzeCommits.Request) returns (AnalyzeCommits.Response); 44 | } 45 | -------------------------------------------------------------------------------- /pkg/analyzer/commit_analyzer_grpc.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT. 2 | // versions: 3 | // - protoc-gen-go-grpc v1.3.0 4 | // - protoc v4.24.4 5 | // source: pkg/analyzer/commit_analyzer.proto 6 | 7 | package analyzer 8 | 9 | import ( 10 | context "context" 11 | grpc "google.golang.org/grpc" 12 | codes "google.golang.org/grpc/codes" 13 | status "google.golang.org/grpc/status" 14 | ) 15 | 16 | // This is a compile-time assertion to ensure that this generated file 17 | // is compatible with the grpc package it is being compiled against. 18 | // Requires gRPC-Go v1.32.0 or later. 19 | const _ = grpc.SupportPackageIsVersion7 20 | 21 | const ( 22 | CommitAnalyzerPlugin_Init_FullMethodName = "/CommitAnalyzerPlugin/Init" 23 | CommitAnalyzerPlugin_Name_FullMethodName = "/CommitAnalyzerPlugin/Name" 24 | CommitAnalyzerPlugin_Version_FullMethodName = "/CommitAnalyzerPlugin/Version" 25 | CommitAnalyzerPlugin_Analyze_FullMethodName = "/CommitAnalyzerPlugin/Analyze" 26 | ) 27 | 28 | // CommitAnalyzerPluginClient is the client API for CommitAnalyzerPlugin service. 29 | // 30 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. 31 | type CommitAnalyzerPluginClient interface { 32 | Init(ctx context.Context, in *CommitAnalyzerInit_Request, opts ...grpc.CallOption) (*CommitAnalyzerInit_Response, error) 33 | Name(ctx context.Context, in *CommitAnalyzerName_Request, opts ...grpc.CallOption) (*CommitAnalyzerName_Response, error) 34 | Version(ctx context.Context, in *CommitAnalyzerVersion_Request, opts ...grpc.CallOption) (*CommitAnalyzerVersion_Response, error) 35 | Analyze(ctx context.Context, in *AnalyzeCommits_Request, opts ...grpc.CallOption) (*AnalyzeCommits_Response, error) 36 | } 37 | 38 | type commitAnalyzerPluginClient struct { 39 | cc grpc.ClientConnInterface 40 | } 41 | 42 | func NewCommitAnalyzerPluginClient(cc grpc.ClientConnInterface) CommitAnalyzerPluginClient { 43 | return &commitAnalyzerPluginClient{cc} 44 | } 45 | 46 | func (c *commitAnalyzerPluginClient) Init(ctx context.Context, in *CommitAnalyzerInit_Request, opts ...grpc.CallOption) (*CommitAnalyzerInit_Response, error) { 47 | out := new(CommitAnalyzerInit_Response) 48 | err := c.cc.Invoke(ctx, CommitAnalyzerPlugin_Init_FullMethodName, in, out, opts...) 49 | if err != nil { 50 | return nil, err 51 | } 52 | return out, nil 53 | } 54 | 55 | func (c *commitAnalyzerPluginClient) Name(ctx context.Context, in *CommitAnalyzerName_Request, opts ...grpc.CallOption) (*CommitAnalyzerName_Response, error) { 56 | out := new(CommitAnalyzerName_Response) 57 | err := c.cc.Invoke(ctx, CommitAnalyzerPlugin_Name_FullMethodName, in, out, opts...) 58 | if err != nil { 59 | return nil, err 60 | } 61 | return out, nil 62 | } 63 | 64 | func (c *commitAnalyzerPluginClient) Version(ctx context.Context, in *CommitAnalyzerVersion_Request, opts ...grpc.CallOption) (*CommitAnalyzerVersion_Response, error) { 65 | out := new(CommitAnalyzerVersion_Response) 66 | err := c.cc.Invoke(ctx, CommitAnalyzerPlugin_Version_FullMethodName, in, out, opts...) 67 | if err != nil { 68 | return nil, err 69 | } 70 | return out, nil 71 | } 72 | 73 | func (c *commitAnalyzerPluginClient) Analyze(ctx context.Context, in *AnalyzeCommits_Request, opts ...grpc.CallOption) (*AnalyzeCommits_Response, error) { 74 | out := new(AnalyzeCommits_Response) 75 | err := c.cc.Invoke(ctx, CommitAnalyzerPlugin_Analyze_FullMethodName, in, out, opts...) 76 | if err != nil { 77 | return nil, err 78 | } 79 | return out, nil 80 | } 81 | 82 | // CommitAnalyzerPluginServer is the server API for CommitAnalyzerPlugin service. 83 | // All implementations must embed UnimplementedCommitAnalyzerPluginServer 84 | // for forward compatibility 85 | type CommitAnalyzerPluginServer interface { 86 | Init(context.Context, *CommitAnalyzerInit_Request) (*CommitAnalyzerInit_Response, error) 87 | Name(context.Context, *CommitAnalyzerName_Request) (*CommitAnalyzerName_Response, error) 88 | Version(context.Context, *CommitAnalyzerVersion_Request) (*CommitAnalyzerVersion_Response, error) 89 | Analyze(context.Context, *AnalyzeCommits_Request) (*AnalyzeCommits_Response, error) 90 | mustEmbedUnimplementedCommitAnalyzerPluginServer() 91 | } 92 | 93 | // UnimplementedCommitAnalyzerPluginServer must be embedded to have forward compatible implementations. 94 | type UnimplementedCommitAnalyzerPluginServer struct { 95 | } 96 | 97 | func (UnimplementedCommitAnalyzerPluginServer) Init(context.Context, *CommitAnalyzerInit_Request) (*CommitAnalyzerInit_Response, error) { 98 | return nil, status.Errorf(codes.Unimplemented, "method Init not implemented") 99 | } 100 | func (UnimplementedCommitAnalyzerPluginServer) Name(context.Context, *CommitAnalyzerName_Request) (*CommitAnalyzerName_Response, error) { 101 | return nil, status.Errorf(codes.Unimplemented, "method Name not implemented") 102 | } 103 | func (UnimplementedCommitAnalyzerPluginServer) Version(context.Context, *CommitAnalyzerVersion_Request) (*CommitAnalyzerVersion_Response, error) { 104 | return nil, status.Errorf(codes.Unimplemented, "method Version not implemented") 105 | } 106 | func (UnimplementedCommitAnalyzerPluginServer) Analyze(context.Context, *AnalyzeCommits_Request) (*AnalyzeCommits_Response, error) { 107 | return nil, status.Errorf(codes.Unimplemented, "method Analyze not implemented") 108 | } 109 | func (UnimplementedCommitAnalyzerPluginServer) mustEmbedUnimplementedCommitAnalyzerPluginServer() {} 110 | 111 | // UnsafeCommitAnalyzerPluginServer may be embedded to opt out of forward compatibility for this service. 112 | // Use of this interface is not recommended, as added methods to CommitAnalyzerPluginServer will 113 | // result in compilation errors. 114 | type UnsafeCommitAnalyzerPluginServer interface { 115 | mustEmbedUnimplementedCommitAnalyzerPluginServer() 116 | } 117 | 118 | func RegisterCommitAnalyzerPluginServer(s grpc.ServiceRegistrar, srv CommitAnalyzerPluginServer) { 119 | s.RegisterService(&CommitAnalyzerPlugin_ServiceDesc, srv) 120 | } 121 | 122 | func _CommitAnalyzerPlugin_Init_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 123 | in := new(CommitAnalyzerInit_Request) 124 | if err := dec(in); err != nil { 125 | return nil, err 126 | } 127 | if interceptor == nil { 128 | return srv.(CommitAnalyzerPluginServer).Init(ctx, in) 129 | } 130 | info := &grpc.UnaryServerInfo{ 131 | Server: srv, 132 | FullMethod: CommitAnalyzerPlugin_Init_FullMethodName, 133 | } 134 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 135 | return srv.(CommitAnalyzerPluginServer).Init(ctx, req.(*CommitAnalyzerInit_Request)) 136 | } 137 | return interceptor(ctx, in, info, handler) 138 | } 139 | 140 | func _CommitAnalyzerPlugin_Name_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 141 | in := new(CommitAnalyzerName_Request) 142 | if err := dec(in); err != nil { 143 | return nil, err 144 | } 145 | if interceptor == nil { 146 | return srv.(CommitAnalyzerPluginServer).Name(ctx, in) 147 | } 148 | info := &grpc.UnaryServerInfo{ 149 | Server: srv, 150 | FullMethod: CommitAnalyzerPlugin_Name_FullMethodName, 151 | } 152 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 153 | return srv.(CommitAnalyzerPluginServer).Name(ctx, req.(*CommitAnalyzerName_Request)) 154 | } 155 | return interceptor(ctx, in, info, handler) 156 | } 157 | 158 | func _CommitAnalyzerPlugin_Version_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 159 | in := new(CommitAnalyzerVersion_Request) 160 | if err := dec(in); err != nil { 161 | return nil, err 162 | } 163 | if interceptor == nil { 164 | return srv.(CommitAnalyzerPluginServer).Version(ctx, in) 165 | } 166 | info := &grpc.UnaryServerInfo{ 167 | Server: srv, 168 | FullMethod: CommitAnalyzerPlugin_Version_FullMethodName, 169 | } 170 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 171 | return srv.(CommitAnalyzerPluginServer).Version(ctx, req.(*CommitAnalyzerVersion_Request)) 172 | } 173 | return interceptor(ctx, in, info, handler) 174 | } 175 | 176 | func _CommitAnalyzerPlugin_Analyze_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 177 | in := new(AnalyzeCommits_Request) 178 | if err := dec(in); err != nil { 179 | return nil, err 180 | } 181 | if interceptor == nil { 182 | return srv.(CommitAnalyzerPluginServer).Analyze(ctx, in) 183 | } 184 | info := &grpc.UnaryServerInfo{ 185 | Server: srv, 186 | FullMethod: CommitAnalyzerPlugin_Analyze_FullMethodName, 187 | } 188 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 189 | return srv.(CommitAnalyzerPluginServer).Analyze(ctx, req.(*AnalyzeCommits_Request)) 190 | } 191 | return interceptor(ctx, in, info, handler) 192 | } 193 | 194 | // CommitAnalyzerPlugin_ServiceDesc is the grpc.ServiceDesc for CommitAnalyzerPlugin service. 195 | // It's only intended for direct use with grpc.RegisterService, 196 | // and not to be introspected or modified (even as a copy) 197 | var CommitAnalyzerPlugin_ServiceDesc = grpc.ServiceDesc{ 198 | ServiceName: "CommitAnalyzerPlugin", 199 | HandlerType: (*CommitAnalyzerPluginServer)(nil), 200 | Methods: []grpc.MethodDesc{ 201 | { 202 | MethodName: "Init", 203 | Handler: _CommitAnalyzerPlugin_Init_Handler, 204 | }, 205 | { 206 | MethodName: "Name", 207 | Handler: _CommitAnalyzerPlugin_Name_Handler, 208 | }, 209 | { 210 | MethodName: "Version", 211 | Handler: _CommitAnalyzerPlugin_Version_Handler, 212 | }, 213 | { 214 | MethodName: "Analyze", 215 | Handler: _CommitAnalyzerPlugin_Analyze_Handler, 216 | }, 217 | }, 218 | Streams: []grpc.StreamDesc{}, 219 | Metadata: "pkg/analyzer/commit_analyzer.proto", 220 | } 221 | -------------------------------------------------------------------------------- /pkg/analyzer/commit_analyzer_wrapper.go: -------------------------------------------------------------------------------- 1 | package analyzer 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "github.com/go-semantic-release/semantic-release/v2/pkg/semrel" 8 | ) 9 | 10 | const CommitAnalyzerPluginName = "commit_analyzer" 11 | 12 | type CommitAnalyzerServer struct { 13 | Impl CommitAnalyzer 14 | UnimplementedCommitAnalyzerPluginServer 15 | } 16 | 17 | func (c *CommitAnalyzerServer) Init(_ context.Context, request *CommitAnalyzerInit_Request) (*CommitAnalyzerInit_Response, error) { 18 | err := c.Impl.Init(request.Config) 19 | res := &CommitAnalyzerInit_Response{} 20 | if err != nil { 21 | res.Error = err.Error() 22 | } 23 | return res, nil 24 | } 25 | 26 | func (c *CommitAnalyzerServer) Name(_ context.Context, _ *CommitAnalyzerName_Request) (*CommitAnalyzerName_Response, error) { 27 | return &CommitAnalyzerName_Response{Name: c.Impl.Name()}, nil 28 | } 29 | 30 | func (c *CommitAnalyzerServer) Version(_ context.Context, _ *CommitAnalyzerVersion_Request) (*CommitAnalyzerVersion_Response, error) { 31 | return &CommitAnalyzerVersion_Response{Version: c.Impl.Version()}, nil 32 | } 33 | 34 | func (c *CommitAnalyzerServer) Analyze(_ context.Context, request *AnalyzeCommits_Request) (*AnalyzeCommits_Response, error) { 35 | return &AnalyzeCommits_Response{ 36 | Commits: c.Impl.Analyze(request.RawCommits), 37 | }, nil 38 | } 39 | 40 | type CommitAnalyzerClient struct { 41 | Impl CommitAnalyzerPluginClient 42 | } 43 | 44 | func (c *CommitAnalyzerClient) Init(m map[string]string) error { 45 | res, err := c.Impl.Init(context.Background(), &CommitAnalyzerInit_Request{ 46 | Config: m, 47 | }) 48 | if err != nil { 49 | return err 50 | } 51 | if res.Error != "" { 52 | return errors.New(res.Error) 53 | } 54 | return nil 55 | } 56 | 57 | func (c *CommitAnalyzerClient) Name() string { 58 | res, err := c.Impl.Name(context.Background(), &CommitAnalyzerName_Request{}) 59 | if err != nil { 60 | panic(err) 61 | } 62 | return res.Name 63 | } 64 | 65 | func (c *CommitAnalyzerClient) Version() string { 66 | res, err := c.Impl.Version(context.Background(), &CommitAnalyzerVersion_Request{}) 67 | if err != nil { 68 | panic(err) 69 | } 70 | return res.Version 71 | } 72 | 73 | func (c *CommitAnalyzerClient) Analyze(commits []*semrel.RawCommit) []*semrel.Commit { 74 | res, err := c.Impl.Analyze(context.Background(), &AnalyzeCommits_Request{RawCommits: commits}) 75 | if err != nil { 76 | panic(err) 77 | } 78 | return res.Commits 79 | } 80 | -------------------------------------------------------------------------------- /pkg/condition/ci_condition.go: -------------------------------------------------------------------------------- 1 | package condition 2 | 3 | type CICondition interface { 4 | Name() string 5 | Version() string 6 | RunCondition(map[string]string) error 7 | GetCurrentBranch() string 8 | GetCurrentSHA() string 9 | } 10 | -------------------------------------------------------------------------------- /pkg/condition/ci_condition.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option go_package = "github.com/go-semantic-release/semantic-release/v2/pkg/condition"; 3 | 4 | message CIName { 5 | message Request {} 6 | message Response { 7 | string name = 1; 8 | } 9 | } 10 | 11 | message CIVersion { 12 | message Request {} 13 | message Response { 14 | string version = 1; 15 | } 16 | } 17 | 18 | message RunCondition { 19 | message Request { 20 | map value = 1; 21 | 22 | } 23 | message Response { 24 | string error = 1; 25 | } 26 | } 27 | 28 | message GetCurrentBranch { 29 | message Request {} 30 | message Response { 31 | string value = 1; 32 | } 33 | } 34 | 35 | message GetCurrentSHA { 36 | message Request {} 37 | message Response { 38 | string value = 1; 39 | } 40 | } 41 | 42 | service CIConditionPlugin { 43 | rpc Name(CIName.Request) returns (CIName.Response); 44 | rpc Version(CIVersion.Request) returns (CIVersion.Response); 45 | rpc RunCondition(RunCondition.Request) returns (RunCondition.Response); 46 | rpc GetCurrentBranch(GetCurrentBranch.Request) returns (GetCurrentBranch.Response); 47 | rpc GetCurrentSHA(GetCurrentSHA.Request) returns (GetCurrentSHA.Response); 48 | } 49 | -------------------------------------------------------------------------------- /pkg/condition/ci_condition_grpc.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT. 2 | // versions: 3 | // - protoc-gen-go-grpc v1.3.0 4 | // - protoc v4.24.4 5 | // source: pkg/condition/ci_condition.proto 6 | 7 | package condition 8 | 9 | import ( 10 | context "context" 11 | grpc "google.golang.org/grpc" 12 | codes "google.golang.org/grpc/codes" 13 | status "google.golang.org/grpc/status" 14 | ) 15 | 16 | // This is a compile-time assertion to ensure that this generated file 17 | // is compatible with the grpc package it is being compiled against. 18 | // Requires gRPC-Go v1.32.0 or later. 19 | const _ = grpc.SupportPackageIsVersion7 20 | 21 | const ( 22 | CIConditionPlugin_Name_FullMethodName = "/CIConditionPlugin/Name" 23 | CIConditionPlugin_Version_FullMethodName = "/CIConditionPlugin/Version" 24 | CIConditionPlugin_RunCondition_FullMethodName = "/CIConditionPlugin/RunCondition" 25 | CIConditionPlugin_GetCurrentBranch_FullMethodName = "/CIConditionPlugin/GetCurrentBranch" 26 | CIConditionPlugin_GetCurrentSHA_FullMethodName = "/CIConditionPlugin/GetCurrentSHA" 27 | ) 28 | 29 | // CIConditionPluginClient is the client API for CIConditionPlugin service. 30 | // 31 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. 32 | type CIConditionPluginClient interface { 33 | Name(ctx context.Context, in *CIName_Request, opts ...grpc.CallOption) (*CIName_Response, error) 34 | Version(ctx context.Context, in *CIVersion_Request, opts ...grpc.CallOption) (*CIVersion_Response, error) 35 | RunCondition(ctx context.Context, in *RunCondition_Request, opts ...grpc.CallOption) (*RunCondition_Response, error) 36 | GetCurrentBranch(ctx context.Context, in *GetCurrentBranch_Request, opts ...grpc.CallOption) (*GetCurrentBranch_Response, error) 37 | GetCurrentSHA(ctx context.Context, in *GetCurrentSHA_Request, opts ...grpc.CallOption) (*GetCurrentSHA_Response, error) 38 | } 39 | 40 | type cIConditionPluginClient struct { 41 | cc grpc.ClientConnInterface 42 | } 43 | 44 | func NewCIConditionPluginClient(cc grpc.ClientConnInterface) CIConditionPluginClient { 45 | return &cIConditionPluginClient{cc} 46 | } 47 | 48 | func (c *cIConditionPluginClient) Name(ctx context.Context, in *CIName_Request, opts ...grpc.CallOption) (*CIName_Response, error) { 49 | out := new(CIName_Response) 50 | err := c.cc.Invoke(ctx, CIConditionPlugin_Name_FullMethodName, in, out, opts...) 51 | if err != nil { 52 | return nil, err 53 | } 54 | return out, nil 55 | } 56 | 57 | func (c *cIConditionPluginClient) Version(ctx context.Context, in *CIVersion_Request, opts ...grpc.CallOption) (*CIVersion_Response, error) { 58 | out := new(CIVersion_Response) 59 | err := c.cc.Invoke(ctx, CIConditionPlugin_Version_FullMethodName, in, out, opts...) 60 | if err != nil { 61 | return nil, err 62 | } 63 | return out, nil 64 | } 65 | 66 | func (c *cIConditionPluginClient) RunCondition(ctx context.Context, in *RunCondition_Request, opts ...grpc.CallOption) (*RunCondition_Response, error) { 67 | out := new(RunCondition_Response) 68 | err := c.cc.Invoke(ctx, CIConditionPlugin_RunCondition_FullMethodName, in, out, opts...) 69 | if err != nil { 70 | return nil, err 71 | } 72 | return out, nil 73 | } 74 | 75 | func (c *cIConditionPluginClient) GetCurrentBranch(ctx context.Context, in *GetCurrentBranch_Request, opts ...grpc.CallOption) (*GetCurrentBranch_Response, error) { 76 | out := new(GetCurrentBranch_Response) 77 | err := c.cc.Invoke(ctx, CIConditionPlugin_GetCurrentBranch_FullMethodName, in, out, opts...) 78 | if err != nil { 79 | return nil, err 80 | } 81 | return out, nil 82 | } 83 | 84 | func (c *cIConditionPluginClient) GetCurrentSHA(ctx context.Context, in *GetCurrentSHA_Request, opts ...grpc.CallOption) (*GetCurrentSHA_Response, error) { 85 | out := new(GetCurrentSHA_Response) 86 | err := c.cc.Invoke(ctx, CIConditionPlugin_GetCurrentSHA_FullMethodName, in, out, opts...) 87 | if err != nil { 88 | return nil, err 89 | } 90 | return out, nil 91 | } 92 | 93 | // CIConditionPluginServer is the server API for CIConditionPlugin service. 94 | // All implementations must embed UnimplementedCIConditionPluginServer 95 | // for forward compatibility 96 | type CIConditionPluginServer interface { 97 | Name(context.Context, *CIName_Request) (*CIName_Response, error) 98 | Version(context.Context, *CIVersion_Request) (*CIVersion_Response, error) 99 | RunCondition(context.Context, *RunCondition_Request) (*RunCondition_Response, error) 100 | GetCurrentBranch(context.Context, *GetCurrentBranch_Request) (*GetCurrentBranch_Response, error) 101 | GetCurrentSHA(context.Context, *GetCurrentSHA_Request) (*GetCurrentSHA_Response, error) 102 | mustEmbedUnimplementedCIConditionPluginServer() 103 | } 104 | 105 | // UnimplementedCIConditionPluginServer must be embedded to have forward compatible implementations. 106 | type UnimplementedCIConditionPluginServer struct { 107 | } 108 | 109 | func (UnimplementedCIConditionPluginServer) Name(context.Context, *CIName_Request) (*CIName_Response, error) { 110 | return nil, status.Errorf(codes.Unimplemented, "method Name not implemented") 111 | } 112 | func (UnimplementedCIConditionPluginServer) Version(context.Context, *CIVersion_Request) (*CIVersion_Response, error) { 113 | return nil, status.Errorf(codes.Unimplemented, "method Version not implemented") 114 | } 115 | func (UnimplementedCIConditionPluginServer) RunCondition(context.Context, *RunCondition_Request) (*RunCondition_Response, error) { 116 | return nil, status.Errorf(codes.Unimplemented, "method RunCondition not implemented") 117 | } 118 | func (UnimplementedCIConditionPluginServer) GetCurrentBranch(context.Context, *GetCurrentBranch_Request) (*GetCurrentBranch_Response, error) { 119 | return nil, status.Errorf(codes.Unimplemented, "method GetCurrentBranch not implemented") 120 | } 121 | func (UnimplementedCIConditionPluginServer) GetCurrentSHA(context.Context, *GetCurrentSHA_Request) (*GetCurrentSHA_Response, error) { 122 | return nil, status.Errorf(codes.Unimplemented, "method GetCurrentSHA not implemented") 123 | } 124 | func (UnimplementedCIConditionPluginServer) mustEmbedUnimplementedCIConditionPluginServer() {} 125 | 126 | // UnsafeCIConditionPluginServer may be embedded to opt out of forward compatibility for this service. 127 | // Use of this interface is not recommended, as added methods to CIConditionPluginServer will 128 | // result in compilation errors. 129 | type UnsafeCIConditionPluginServer interface { 130 | mustEmbedUnimplementedCIConditionPluginServer() 131 | } 132 | 133 | func RegisterCIConditionPluginServer(s grpc.ServiceRegistrar, srv CIConditionPluginServer) { 134 | s.RegisterService(&CIConditionPlugin_ServiceDesc, srv) 135 | } 136 | 137 | func _CIConditionPlugin_Name_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 138 | in := new(CIName_Request) 139 | if err := dec(in); err != nil { 140 | return nil, err 141 | } 142 | if interceptor == nil { 143 | return srv.(CIConditionPluginServer).Name(ctx, in) 144 | } 145 | info := &grpc.UnaryServerInfo{ 146 | Server: srv, 147 | FullMethod: CIConditionPlugin_Name_FullMethodName, 148 | } 149 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 150 | return srv.(CIConditionPluginServer).Name(ctx, req.(*CIName_Request)) 151 | } 152 | return interceptor(ctx, in, info, handler) 153 | } 154 | 155 | func _CIConditionPlugin_Version_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 156 | in := new(CIVersion_Request) 157 | if err := dec(in); err != nil { 158 | return nil, err 159 | } 160 | if interceptor == nil { 161 | return srv.(CIConditionPluginServer).Version(ctx, in) 162 | } 163 | info := &grpc.UnaryServerInfo{ 164 | Server: srv, 165 | FullMethod: CIConditionPlugin_Version_FullMethodName, 166 | } 167 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 168 | return srv.(CIConditionPluginServer).Version(ctx, req.(*CIVersion_Request)) 169 | } 170 | return interceptor(ctx, in, info, handler) 171 | } 172 | 173 | func _CIConditionPlugin_RunCondition_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 174 | in := new(RunCondition_Request) 175 | if err := dec(in); err != nil { 176 | return nil, err 177 | } 178 | if interceptor == nil { 179 | return srv.(CIConditionPluginServer).RunCondition(ctx, in) 180 | } 181 | info := &grpc.UnaryServerInfo{ 182 | Server: srv, 183 | FullMethod: CIConditionPlugin_RunCondition_FullMethodName, 184 | } 185 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 186 | return srv.(CIConditionPluginServer).RunCondition(ctx, req.(*RunCondition_Request)) 187 | } 188 | return interceptor(ctx, in, info, handler) 189 | } 190 | 191 | func _CIConditionPlugin_GetCurrentBranch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 192 | in := new(GetCurrentBranch_Request) 193 | if err := dec(in); err != nil { 194 | return nil, err 195 | } 196 | if interceptor == nil { 197 | return srv.(CIConditionPluginServer).GetCurrentBranch(ctx, in) 198 | } 199 | info := &grpc.UnaryServerInfo{ 200 | Server: srv, 201 | FullMethod: CIConditionPlugin_GetCurrentBranch_FullMethodName, 202 | } 203 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 204 | return srv.(CIConditionPluginServer).GetCurrentBranch(ctx, req.(*GetCurrentBranch_Request)) 205 | } 206 | return interceptor(ctx, in, info, handler) 207 | } 208 | 209 | func _CIConditionPlugin_GetCurrentSHA_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 210 | in := new(GetCurrentSHA_Request) 211 | if err := dec(in); err != nil { 212 | return nil, err 213 | } 214 | if interceptor == nil { 215 | return srv.(CIConditionPluginServer).GetCurrentSHA(ctx, in) 216 | } 217 | info := &grpc.UnaryServerInfo{ 218 | Server: srv, 219 | FullMethod: CIConditionPlugin_GetCurrentSHA_FullMethodName, 220 | } 221 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 222 | return srv.(CIConditionPluginServer).GetCurrentSHA(ctx, req.(*GetCurrentSHA_Request)) 223 | } 224 | return interceptor(ctx, in, info, handler) 225 | } 226 | 227 | // CIConditionPlugin_ServiceDesc is the grpc.ServiceDesc for CIConditionPlugin service. 228 | // It's only intended for direct use with grpc.RegisterService, 229 | // and not to be introspected or modified (even as a copy) 230 | var CIConditionPlugin_ServiceDesc = grpc.ServiceDesc{ 231 | ServiceName: "CIConditionPlugin", 232 | HandlerType: (*CIConditionPluginServer)(nil), 233 | Methods: []grpc.MethodDesc{ 234 | { 235 | MethodName: "Name", 236 | Handler: _CIConditionPlugin_Name_Handler, 237 | }, 238 | { 239 | MethodName: "Version", 240 | Handler: _CIConditionPlugin_Version_Handler, 241 | }, 242 | { 243 | MethodName: "RunCondition", 244 | Handler: _CIConditionPlugin_RunCondition_Handler, 245 | }, 246 | { 247 | MethodName: "GetCurrentBranch", 248 | Handler: _CIConditionPlugin_GetCurrentBranch_Handler, 249 | }, 250 | { 251 | MethodName: "GetCurrentSHA", 252 | Handler: _CIConditionPlugin_GetCurrentSHA_Handler, 253 | }, 254 | }, 255 | Streams: []grpc.StreamDesc{}, 256 | Metadata: "pkg/condition/ci_condition.proto", 257 | } 258 | -------------------------------------------------------------------------------- /pkg/condition/ci_condition_wrapper.go: -------------------------------------------------------------------------------- 1 | package condition 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | ) 7 | 8 | const CIConditionPluginName = "ci_condition" 9 | 10 | type CIConditionServer struct { 11 | Impl CICondition 12 | UnimplementedCIConditionPluginServer 13 | } 14 | 15 | func (c *CIConditionServer) Name(_ context.Context, _ *CIName_Request) (*CIName_Response, error) { 16 | return &CIName_Response{Name: c.Impl.Name()}, nil 17 | } 18 | 19 | func (c *CIConditionServer) Version(_ context.Context, _ *CIVersion_Request) (*CIVersion_Response, error) { 20 | return &CIVersion_Response{Version: c.Impl.Version()}, nil 21 | } 22 | 23 | func (c *CIConditionServer) RunCondition(_ context.Context, request *RunCondition_Request) (*RunCondition_Response, error) { 24 | err := c.Impl.RunCondition(request.Value) 25 | ret := &RunCondition_Response{} 26 | if err != nil { 27 | ret.Error = err.Error() 28 | } 29 | return ret, nil 30 | } 31 | 32 | func (c *CIConditionServer) GetCurrentBranch(_ context.Context, _ *GetCurrentBranch_Request) (*GetCurrentBranch_Response, error) { 33 | return &GetCurrentBranch_Response{Value: c.Impl.GetCurrentBranch()}, nil 34 | } 35 | 36 | func (c *CIConditionServer) GetCurrentSHA(_ context.Context, _ *GetCurrentSHA_Request) (*GetCurrentSHA_Response, error) { 37 | return &GetCurrentSHA_Response{Value: c.Impl.GetCurrentSHA()}, nil 38 | } 39 | 40 | type CIConditionClient struct { 41 | Impl CIConditionPluginClient 42 | } 43 | 44 | func (c *CIConditionClient) Name() string { 45 | res, err := c.Impl.Name(context.Background(), &CIName_Request{}) 46 | if err != nil { 47 | panic(err) 48 | } 49 | return res.Name 50 | } 51 | 52 | func (c *CIConditionClient) Version() string { 53 | res, err := c.Impl.Version(context.Background(), &CIVersion_Request{}) 54 | if err != nil { 55 | panic(err) 56 | } 57 | return res.Version 58 | } 59 | 60 | func (c *CIConditionClient) RunCondition(m map[string]string) error { 61 | res, err := c.Impl.RunCondition(context.Background(), &RunCondition_Request{Value: m}) 62 | if err != nil { 63 | return err 64 | } 65 | if res.Error != "" { 66 | return errors.New(res.Error) 67 | } 68 | return nil 69 | } 70 | 71 | func (c *CIConditionClient) GetCurrentBranch() string { 72 | res, err := c.Impl.GetCurrentBranch(context.Background(), &GetCurrentBranch_Request{}) 73 | if err != nil { 74 | panic(err) 75 | } 76 | return res.Value 77 | } 78 | 79 | func (c *CIConditionClient) GetCurrentSHA() string { 80 | res, err := c.Impl.GetCurrentSHA(context.Background(), &GetCurrentSHA_Request{}) 81 | if err != nil { 82 | panic(err) 83 | } 84 | return res.Value 85 | } 86 | -------------------------------------------------------------------------------- /pkg/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "strings" 7 | 8 | "github.com/spf13/cobra" 9 | "github.com/spf13/viper" 10 | ) 11 | 12 | func mustGetString(cmd *cobra.Command, name string) string { 13 | res, err := cmd.Flags().GetString(name) 14 | if err != nil { 15 | panic(err) 16 | } 17 | return res 18 | } 19 | 20 | func mustGetStringArray(cmd *cobra.Command, name string) []string { 21 | res, err := cmd.Flags().GetStringArray(name) 22 | if err != nil { 23 | panic(err) 24 | } 25 | return res 26 | } 27 | 28 | func mustGetBool(cmd *cobra.Command, name string) bool { 29 | res, err := cmd.Flags().GetBool(name) 30 | if err != nil { 31 | panic(err) 32 | } 33 | return res 34 | } 35 | 36 | func mergeOpts(v map[string]string, c []string) map[string]string { 37 | opts := make(map[string]string) 38 | for k, v := range v { 39 | opts[k] = v 40 | } 41 | for _, opt := range c { 42 | sOpt := strings.SplitN(opt, "=", 2) 43 | if len(sOpt) < 2 { 44 | continue 45 | } 46 | opts[strings.ToLower(sOpt[0])] = sOpt[1] 47 | } 48 | return opts 49 | } 50 | 51 | func NewConfig(cmd *cobra.Command) (*Config, error) { 52 | provOpts := mergeOpts( 53 | viper.GetStringMapString("plugins.provider.options"), 54 | mustGetStringArray(cmd, "provider-opt")) 55 | caOpts := mergeOpts( 56 | viper.GetStringMapString("plugins.commit-analyzer.options"), 57 | mustGetStringArray(cmd, "commit-analyzer-opt")) 58 | ciOpts := mergeOpts( 59 | viper.GetStringMapString("plugins.ci-condition.options"), 60 | mustGetStringArray(cmd, "ci-condition-opt")) 61 | cgOpts := mergeOpts( 62 | viper.GetStringMapString("plugins.changelog-generator.options"), 63 | mustGetStringArray(cmd, "changelog-generator-opt")) 64 | fuOpts := mergeOpts( 65 | viper.GetStringMapString("plugins.files-updater.options"), 66 | mustGetStringArray(cmd, "files-updater-opt")) 67 | hoOpts := mergeOpts( 68 | viper.GetStringMapString("plugins.hooks.options"), 69 | mustGetStringArray(cmd, "hooks-opt")) 70 | 71 | conf := &Config{ 72 | Token: mustGetString(cmd, "token"), 73 | ProviderPlugin: viper.GetString("plugins.provider.name"), 74 | ProviderOpts: provOpts, 75 | CommitAnalyzerPlugin: viper.GetString("plugins.commit-analyzer.name"), 76 | CommitAnalyzerOpts: caOpts, 77 | CiConditionPlugin: viper.GetString("plugins.ci-condition.name"), 78 | CiConditionOpts: ciOpts, 79 | ChangelogGeneratorPlugin: viper.GetString("plugins.changelog-generator.name"), 80 | ChangelogGeneratorOpts: cgOpts, 81 | Changelog: mustGetString(cmd, "changelog"), 82 | FilesUpdaterPlugins: viper.GetStringSlice("plugins.files-updater.names"), 83 | FilesUpdaterOpts: fuOpts, 84 | HooksPlugins: viper.GetStringSlice("plugins.hooks.names"), 85 | HooksOpts: hoOpts, 86 | UpdateFiles: mustGetStringArray(cmd, "update"), 87 | Match: mustGetString(cmd, "match"), 88 | VersionFile: mustGetBool(cmd, "version-file"), 89 | Prerelease: mustGetBool(cmd, "prerelease"), 90 | Ghr: mustGetBool(cmd, "ghr"), 91 | NoCi: mustGetBool(cmd, "no-ci"), 92 | Dry: mustGetBool(cmd, "dry"), 93 | AllowInitialDevelopmentVersions: mustGetBool(cmd, "allow-initial-development-versions"), 94 | AllowNoChanges: mustGetBool(cmd, "allow-no-changes"), 95 | ForceBumpPatchVersion: mustGetBool(cmd, "force-bump-patch-version"), 96 | MaintainedVersion: viper.GetString("maintainedVersion"), 97 | PrependChangelog: mustGetBool(cmd, "prepend-changelog"), 98 | DownloadPlugins: mustGetBool(cmd, "download-plugins"), 99 | ShowProgress: mustGetBool(cmd, "show-progress"), 100 | AllowMaintainedVersionOnDefaultBranch: mustGetBool(cmd, "allow-maintained-version-on-default-branch"), 101 | PluginResolver: viper.GetString("pluginResolver"), 102 | PluginResolverEndpoint: viper.GetString("pluginResolverEndpoint"), 103 | PluginResolverDisableBatchPrefetch: viper.GetBool("pluginResolverDisableBatchPrefetch"), 104 | } 105 | return conf, nil 106 | } 107 | 108 | func must(err error) { 109 | if err != nil { 110 | panic(err) 111 | } 112 | } 113 | 114 | var trueString = "true" 115 | 116 | func detectCI() string { 117 | if os.Getenv("GITHUB_ACTIONS") == trueString { 118 | return "github" 119 | } 120 | if os.Getenv("GITLAB_CI") == trueString { 121 | return "gitlab" 122 | } 123 | return "default" 124 | } 125 | 126 | func defaultProvider() string { 127 | if os.Getenv("GITLAB_CI") == trueString { 128 | return "gitlab" 129 | } 130 | return "github" 131 | } 132 | 133 | func SetFlags(cmd *cobra.Command) { 134 | cmd.Flags().StringP("token", "t", "", "provider token") 135 | cmd.Flags().String("provider", defaultProvider(), "provider plugin name") 136 | cmd.Flags().StringArray("provider-opt", []string{}, "options that are passed to the provider plugin") 137 | cmd.Flags().String("commit-analyzer", "default", "commit-analyzer plugin name") 138 | cmd.Flags().StringArray("commit-analyzer-opt", []string{}, "options that are passed to the commit-analyzer plugin") 139 | cmd.Flags().String("ci-condition", detectCI(), "ci-condition plugin name") 140 | cmd.Flags().StringArray("ci-condition-opt", []string{}, "options that are passed to the ci-condition plugin") 141 | cmd.Flags().String("changelog-generator", "default", "changelog-generator plugin name") 142 | cmd.Flags().StringArray("changelog-generator-opt", []string{}, "options that are passed to the changelog-generator plugin") 143 | cmd.Flags().String("changelog", "", "creates a changelog file") 144 | cmd.Flags().StringSlice("files-updater", []string{"npm"}, "files-updater plugin names") 145 | cmd.Flags().StringArray("files-updater-opt", []string{}, "options that are passed to the files-updater plugins") 146 | cmd.Flags().StringSlice("hooks", []string{}, "hooks plugin names") 147 | cmd.Flags().StringArray("hooks-opt", []string{}, "options that are passed to the hooks plugins") 148 | cmd.Flags().StringArrayP("update", "u", []string{}, "updates the version of a certain files") 149 | cmd.Flags().String("match", "", "only consider tags matching the given glob(7) pattern, excluding the \"refs/tags/\" prefix.") 150 | cmd.Flags().String("maintained-version", "", "set the maintained version as base for new releases") 151 | cmd.Flags().BoolP("version-file", "f", false, "create a .version file with the new version") 152 | cmd.Flags().Bool("prerelease", false, "flags the release as a prerelease") 153 | cmd.Flags().Bool("ghr", false, "create a .ghr file with the parameters for ghr") 154 | cmd.Flags().Bool("no-ci", false, "run semantic-release locally") 155 | cmd.Flags().Bool("dry", false, "do not create release") 156 | cmd.Flags().Bool("allow-initial-development-versions", false, "semantic-release will start your initial development release at 0.1.0 and will handle breaking changes as minor version updates. This option will be ignored if a release with major version greater than or equal 1 exists.") 157 | cmd.Flags().Bool("allow-no-changes", false, "exit with code 0 if no changes are found, useful if semantic-release is automatically run") 158 | cmd.Flags().Bool("force-bump-patch-version", false, "increments the patch version if no changes are found") 159 | cmd.Flags().Bool("prepend-changelog", false, "if the changelog file already exist the new changelog is prepended") 160 | cmd.Flags().Bool("download-plugins", false, "downloads all required plugins if needed") 161 | cmd.Flags().Bool("show-progress", false, "shows the plugin download progress") 162 | cmd.Flags().String("config", "", "config file (default is .semrelrc)") 163 | cmd.Flags().Bool("allow-maintained-version-on-default-branch", false, "allow configuring the maintained version on the default branch") 164 | cmd.Flags().String("plugin-resolver", "registry", "which resolver should be used to resolve plugins (registry[-v2] or github)") 165 | cmd.Flags().Bool("plugin-resolver-disable-batch-prefetch", false, "plugins should not be batch prefetched using the registry") 166 | cmd.Flags().String("plugin-resolver-endpoint", "", "explicitly specify the resolver endpoint that should be used for resolving the plugin dependencies") 167 | cmd.Flags().SortFlags = true 168 | 169 | must(viper.BindPFlag("maintainedVersion", cmd.Flags().Lookup("maintained-version"))) 170 | must(viper.BindEnv("maintainedVersion", "MAINTAINED_VERSION")) 171 | 172 | must(viper.BindPFlag("plugins.provider.name", cmd.Flags().Lookup("provider"))) 173 | must(viper.BindPFlag("plugins.commit-analyzer.name", cmd.Flags().Lookup("commit-analyzer"))) 174 | must(viper.BindPFlag("plugins.ci-condition.name", cmd.Flags().Lookup("ci-condition"))) 175 | must(viper.BindPFlag("plugins.changelog-generator.name", cmd.Flags().Lookup("changelog-generator"))) 176 | must(viper.BindPFlag("plugins.files-updater.names", cmd.Flags().Lookup("files-updater"))) 177 | must(viper.BindPFlag("plugins.hooks.names", cmd.Flags().Lookup("hooks"))) 178 | 179 | must(viper.BindPFlag("pluginResolver", cmd.Flags().Lookup("plugin-resolver"))) 180 | must(viper.BindEnv("pluginResolver", "SEMREL_PLUGIN_RESOLVER")) 181 | must(viper.BindPFlag("pluginResolverDisableBatchPrefetch", cmd.Flags().Lookup("plugin-resolver-disable-batch-prefetch"))) 182 | must(viper.BindPFlag("pluginResolverEndpoint", cmd.Flags().Lookup("plugin-resolver-endpoint"))) 183 | } 184 | 185 | func InitConfig(cmd *cobra.Command) error { 186 | cfgFile := mustGetString(cmd, "config") 187 | if cfgFile != "" { 188 | viper.SetConfigFile(cfgFile) 189 | } else { 190 | viper.AddConfigPath(".") 191 | viper.SetConfigName(".semrelrc") 192 | } 193 | viper.SetConfigType("json") 194 | 195 | if err := viper.ReadInConfig(); err != nil { 196 | var viperConfigNotFound viper.ConfigFileNotFoundError 197 | if !errors.As(err, &viperConfigNotFound) { 198 | return err 199 | } 200 | } 201 | return nil 202 | } 203 | -------------------------------------------------------------------------------- /pkg/config/config.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option go_package = "github.com/go-semantic-release/semantic-release/v2/pkg/config"; 3 | 4 | // Config is a complete set of app configuration 5 | message Config { 6 | string token = 1; 7 | string provider_plugin = 2; 8 | map provider_opts = 3; 9 | string commit_analyzer_plugin = 4; 10 | map commit_analyzer_opts = 5; 11 | string ci_condition_plugin = 6; 12 | map ci_condition_opts = 7; 13 | string changelog_generator_plugin = 8; 14 | map changelog_generator_opts = 9; 15 | string changelog = 10; 16 | repeated string files_updater_plugins = 11; 17 | map files_updater_opts = 12; 18 | repeated string hooks_plugins = 13; 19 | map hooks_opts = 14; 20 | repeated string update_files = 15; 21 | string match = 16; 22 | bool version_file = 17; 23 | bool prerelease = 18; 24 | bool ghr = 19; 25 | bool no_ci = 20; 26 | bool dry = 21; 27 | bool allow_initial_development_versions = 22; 28 | bool allow_no_changes = 23; 29 | bool force_bump_patch_version = 24; 30 | string maintained_version = 25; 31 | bool prepend_changelog = 26; 32 | bool download_plugins = 27; 33 | bool show_progress = 28; 34 | bool allow_maintained_version_on_default_branch = 29; 35 | string plugin_resolver = 30; 36 | string plugin_resolver_endpoint = 31; 37 | bool plugin_resolver_disable_batch_prefetch = 32; 38 | } 39 | -------------------------------------------------------------------------------- /pkg/generator/changelog_generator.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | type ChangelogGenerator interface { 4 | Init(map[string]string) error 5 | Name() string 6 | Version() string 7 | Generate(*ChangelogGeneratorConfig) string 8 | } 9 | -------------------------------------------------------------------------------- /pkg/generator/changelog_generator.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option go_package = "github.com/go-semantic-release/semantic-release/v2/pkg/generator"; 3 | 4 | import "pkg/semrel/structs.proto"; 5 | 6 | message ChangelogGeneratorInit { 7 | message Request { 8 | map config = 1; 9 | } 10 | message Response { 11 | string error = 1; 12 | } 13 | } 14 | 15 | message ChangelogGeneratorName { 16 | message Request {} 17 | message Response { 18 | string name = 1; 19 | } 20 | } 21 | 22 | message ChangelogGeneratorVersion { 23 | message Request {} 24 | message Response { 25 | string version = 1; 26 | } 27 | } 28 | 29 | message ChangelogGeneratorConfig { 30 | repeated Commit commits = 1; 31 | Release latest_release = 2; 32 | string new_version = 3; 33 | } 34 | 35 | message GenerateChangelog { 36 | message Request { 37 | ChangelogGeneratorConfig config = 1; 38 | } 39 | message Response { 40 | string changelog = 1; 41 | } 42 | } 43 | 44 | service ChangelogGeneratorPlugin { 45 | rpc Init(ChangelogGeneratorInit.Request) returns (ChangelogGeneratorInit.Response); 46 | rpc Name(ChangelogGeneratorName.Request) returns (ChangelogGeneratorName.Response); 47 | rpc Version(ChangelogGeneratorVersion.Request) returns (ChangelogGeneratorVersion.Response); 48 | rpc Generate(GenerateChangelog.Request) returns (GenerateChangelog.Response); 49 | } 50 | -------------------------------------------------------------------------------- /pkg/generator/changelog_generator_grpc.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT. 2 | // versions: 3 | // - protoc-gen-go-grpc v1.3.0 4 | // - protoc v4.24.4 5 | // source: pkg/generator/changelog_generator.proto 6 | 7 | package generator 8 | 9 | import ( 10 | context "context" 11 | grpc "google.golang.org/grpc" 12 | codes "google.golang.org/grpc/codes" 13 | status "google.golang.org/grpc/status" 14 | ) 15 | 16 | // This is a compile-time assertion to ensure that this generated file 17 | // is compatible with the grpc package it is being compiled against. 18 | // Requires gRPC-Go v1.32.0 or later. 19 | const _ = grpc.SupportPackageIsVersion7 20 | 21 | const ( 22 | ChangelogGeneratorPlugin_Init_FullMethodName = "/ChangelogGeneratorPlugin/Init" 23 | ChangelogGeneratorPlugin_Name_FullMethodName = "/ChangelogGeneratorPlugin/Name" 24 | ChangelogGeneratorPlugin_Version_FullMethodName = "/ChangelogGeneratorPlugin/Version" 25 | ChangelogGeneratorPlugin_Generate_FullMethodName = "/ChangelogGeneratorPlugin/Generate" 26 | ) 27 | 28 | // ChangelogGeneratorPluginClient is the client API for ChangelogGeneratorPlugin service. 29 | // 30 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. 31 | type ChangelogGeneratorPluginClient interface { 32 | Init(ctx context.Context, in *ChangelogGeneratorInit_Request, opts ...grpc.CallOption) (*ChangelogGeneratorInit_Response, error) 33 | Name(ctx context.Context, in *ChangelogGeneratorName_Request, opts ...grpc.CallOption) (*ChangelogGeneratorName_Response, error) 34 | Version(ctx context.Context, in *ChangelogGeneratorVersion_Request, opts ...grpc.CallOption) (*ChangelogGeneratorVersion_Response, error) 35 | Generate(ctx context.Context, in *GenerateChangelog_Request, opts ...grpc.CallOption) (*GenerateChangelog_Response, error) 36 | } 37 | 38 | type changelogGeneratorPluginClient struct { 39 | cc grpc.ClientConnInterface 40 | } 41 | 42 | func NewChangelogGeneratorPluginClient(cc grpc.ClientConnInterface) ChangelogGeneratorPluginClient { 43 | return &changelogGeneratorPluginClient{cc} 44 | } 45 | 46 | func (c *changelogGeneratorPluginClient) Init(ctx context.Context, in *ChangelogGeneratorInit_Request, opts ...grpc.CallOption) (*ChangelogGeneratorInit_Response, error) { 47 | out := new(ChangelogGeneratorInit_Response) 48 | err := c.cc.Invoke(ctx, ChangelogGeneratorPlugin_Init_FullMethodName, in, out, opts...) 49 | if err != nil { 50 | return nil, err 51 | } 52 | return out, nil 53 | } 54 | 55 | func (c *changelogGeneratorPluginClient) Name(ctx context.Context, in *ChangelogGeneratorName_Request, opts ...grpc.CallOption) (*ChangelogGeneratorName_Response, error) { 56 | out := new(ChangelogGeneratorName_Response) 57 | err := c.cc.Invoke(ctx, ChangelogGeneratorPlugin_Name_FullMethodName, in, out, opts...) 58 | if err != nil { 59 | return nil, err 60 | } 61 | return out, nil 62 | } 63 | 64 | func (c *changelogGeneratorPluginClient) Version(ctx context.Context, in *ChangelogGeneratorVersion_Request, opts ...grpc.CallOption) (*ChangelogGeneratorVersion_Response, error) { 65 | out := new(ChangelogGeneratorVersion_Response) 66 | err := c.cc.Invoke(ctx, ChangelogGeneratorPlugin_Version_FullMethodName, in, out, opts...) 67 | if err != nil { 68 | return nil, err 69 | } 70 | return out, nil 71 | } 72 | 73 | func (c *changelogGeneratorPluginClient) Generate(ctx context.Context, in *GenerateChangelog_Request, opts ...grpc.CallOption) (*GenerateChangelog_Response, error) { 74 | out := new(GenerateChangelog_Response) 75 | err := c.cc.Invoke(ctx, ChangelogGeneratorPlugin_Generate_FullMethodName, in, out, opts...) 76 | if err != nil { 77 | return nil, err 78 | } 79 | return out, nil 80 | } 81 | 82 | // ChangelogGeneratorPluginServer is the server API for ChangelogGeneratorPlugin service. 83 | // All implementations must embed UnimplementedChangelogGeneratorPluginServer 84 | // for forward compatibility 85 | type ChangelogGeneratorPluginServer interface { 86 | Init(context.Context, *ChangelogGeneratorInit_Request) (*ChangelogGeneratorInit_Response, error) 87 | Name(context.Context, *ChangelogGeneratorName_Request) (*ChangelogGeneratorName_Response, error) 88 | Version(context.Context, *ChangelogGeneratorVersion_Request) (*ChangelogGeneratorVersion_Response, error) 89 | Generate(context.Context, *GenerateChangelog_Request) (*GenerateChangelog_Response, error) 90 | mustEmbedUnimplementedChangelogGeneratorPluginServer() 91 | } 92 | 93 | // UnimplementedChangelogGeneratorPluginServer must be embedded to have forward compatible implementations. 94 | type UnimplementedChangelogGeneratorPluginServer struct { 95 | } 96 | 97 | func (UnimplementedChangelogGeneratorPluginServer) Init(context.Context, *ChangelogGeneratorInit_Request) (*ChangelogGeneratorInit_Response, error) { 98 | return nil, status.Errorf(codes.Unimplemented, "method Init not implemented") 99 | } 100 | func (UnimplementedChangelogGeneratorPluginServer) Name(context.Context, *ChangelogGeneratorName_Request) (*ChangelogGeneratorName_Response, error) { 101 | return nil, status.Errorf(codes.Unimplemented, "method Name not implemented") 102 | } 103 | func (UnimplementedChangelogGeneratorPluginServer) Version(context.Context, *ChangelogGeneratorVersion_Request) (*ChangelogGeneratorVersion_Response, error) { 104 | return nil, status.Errorf(codes.Unimplemented, "method Version not implemented") 105 | } 106 | func (UnimplementedChangelogGeneratorPluginServer) Generate(context.Context, *GenerateChangelog_Request) (*GenerateChangelog_Response, error) { 107 | return nil, status.Errorf(codes.Unimplemented, "method Generate not implemented") 108 | } 109 | func (UnimplementedChangelogGeneratorPluginServer) mustEmbedUnimplementedChangelogGeneratorPluginServer() { 110 | } 111 | 112 | // UnsafeChangelogGeneratorPluginServer may be embedded to opt out of forward compatibility for this service. 113 | // Use of this interface is not recommended, as added methods to ChangelogGeneratorPluginServer will 114 | // result in compilation errors. 115 | type UnsafeChangelogGeneratorPluginServer interface { 116 | mustEmbedUnimplementedChangelogGeneratorPluginServer() 117 | } 118 | 119 | func RegisterChangelogGeneratorPluginServer(s grpc.ServiceRegistrar, srv ChangelogGeneratorPluginServer) { 120 | s.RegisterService(&ChangelogGeneratorPlugin_ServiceDesc, srv) 121 | } 122 | 123 | func _ChangelogGeneratorPlugin_Init_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 124 | in := new(ChangelogGeneratorInit_Request) 125 | if err := dec(in); err != nil { 126 | return nil, err 127 | } 128 | if interceptor == nil { 129 | return srv.(ChangelogGeneratorPluginServer).Init(ctx, in) 130 | } 131 | info := &grpc.UnaryServerInfo{ 132 | Server: srv, 133 | FullMethod: ChangelogGeneratorPlugin_Init_FullMethodName, 134 | } 135 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 136 | return srv.(ChangelogGeneratorPluginServer).Init(ctx, req.(*ChangelogGeneratorInit_Request)) 137 | } 138 | return interceptor(ctx, in, info, handler) 139 | } 140 | 141 | func _ChangelogGeneratorPlugin_Name_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 142 | in := new(ChangelogGeneratorName_Request) 143 | if err := dec(in); err != nil { 144 | return nil, err 145 | } 146 | if interceptor == nil { 147 | return srv.(ChangelogGeneratorPluginServer).Name(ctx, in) 148 | } 149 | info := &grpc.UnaryServerInfo{ 150 | Server: srv, 151 | FullMethod: ChangelogGeneratorPlugin_Name_FullMethodName, 152 | } 153 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 154 | return srv.(ChangelogGeneratorPluginServer).Name(ctx, req.(*ChangelogGeneratorName_Request)) 155 | } 156 | return interceptor(ctx, in, info, handler) 157 | } 158 | 159 | func _ChangelogGeneratorPlugin_Version_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 160 | in := new(ChangelogGeneratorVersion_Request) 161 | if err := dec(in); err != nil { 162 | return nil, err 163 | } 164 | if interceptor == nil { 165 | return srv.(ChangelogGeneratorPluginServer).Version(ctx, in) 166 | } 167 | info := &grpc.UnaryServerInfo{ 168 | Server: srv, 169 | FullMethod: ChangelogGeneratorPlugin_Version_FullMethodName, 170 | } 171 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 172 | return srv.(ChangelogGeneratorPluginServer).Version(ctx, req.(*ChangelogGeneratorVersion_Request)) 173 | } 174 | return interceptor(ctx, in, info, handler) 175 | } 176 | 177 | func _ChangelogGeneratorPlugin_Generate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 178 | in := new(GenerateChangelog_Request) 179 | if err := dec(in); err != nil { 180 | return nil, err 181 | } 182 | if interceptor == nil { 183 | return srv.(ChangelogGeneratorPluginServer).Generate(ctx, in) 184 | } 185 | info := &grpc.UnaryServerInfo{ 186 | Server: srv, 187 | FullMethod: ChangelogGeneratorPlugin_Generate_FullMethodName, 188 | } 189 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 190 | return srv.(ChangelogGeneratorPluginServer).Generate(ctx, req.(*GenerateChangelog_Request)) 191 | } 192 | return interceptor(ctx, in, info, handler) 193 | } 194 | 195 | // ChangelogGeneratorPlugin_ServiceDesc is the grpc.ServiceDesc for ChangelogGeneratorPlugin service. 196 | // It's only intended for direct use with grpc.RegisterService, 197 | // and not to be introspected or modified (even as a copy) 198 | var ChangelogGeneratorPlugin_ServiceDesc = grpc.ServiceDesc{ 199 | ServiceName: "ChangelogGeneratorPlugin", 200 | HandlerType: (*ChangelogGeneratorPluginServer)(nil), 201 | Methods: []grpc.MethodDesc{ 202 | { 203 | MethodName: "Init", 204 | Handler: _ChangelogGeneratorPlugin_Init_Handler, 205 | }, 206 | { 207 | MethodName: "Name", 208 | Handler: _ChangelogGeneratorPlugin_Name_Handler, 209 | }, 210 | { 211 | MethodName: "Version", 212 | Handler: _ChangelogGeneratorPlugin_Version_Handler, 213 | }, 214 | { 215 | MethodName: "Generate", 216 | Handler: _ChangelogGeneratorPlugin_Generate_Handler, 217 | }, 218 | }, 219 | Streams: []grpc.StreamDesc{}, 220 | Metadata: "pkg/generator/changelog_generator.proto", 221 | } 222 | -------------------------------------------------------------------------------- /pkg/generator/changelog_generator_wrapper.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | ) 7 | 8 | const ChangelogGeneratorPluginName = "changelog_generator" 9 | 10 | type ChangelogGeneratorServer struct { 11 | Impl ChangelogGenerator 12 | UnimplementedChangelogGeneratorPluginServer 13 | } 14 | 15 | func (c *ChangelogGeneratorServer) Init(_ context.Context, request *ChangelogGeneratorInit_Request) (*ChangelogGeneratorInit_Response, error) { 16 | err := c.Impl.Init(request.Config) 17 | res := &ChangelogGeneratorInit_Response{} 18 | if err != nil { 19 | res.Error = err.Error() 20 | } 21 | return res, nil 22 | } 23 | 24 | func (c *ChangelogGeneratorServer) Name(_ context.Context, _ *ChangelogGeneratorName_Request) (*ChangelogGeneratorName_Response, error) { 25 | return &ChangelogGeneratorName_Response{Name: c.Impl.Name()}, nil 26 | } 27 | 28 | func (c *ChangelogGeneratorServer) Version(_ context.Context, _ *ChangelogGeneratorVersion_Request) (*ChangelogGeneratorVersion_Response, error) { 29 | return &ChangelogGeneratorVersion_Response{Version: c.Impl.Version()}, nil 30 | } 31 | 32 | func (c *ChangelogGeneratorServer) Generate(_ context.Context, request *GenerateChangelog_Request) (*GenerateChangelog_Response, error) { 33 | return &GenerateChangelog_Response{ 34 | Changelog: c.Impl.Generate(request.Config), 35 | }, nil 36 | } 37 | 38 | type ChangelogGeneratorClient struct { 39 | Impl ChangelogGeneratorPluginClient 40 | } 41 | 42 | func (c *ChangelogGeneratorClient) Generate(config *ChangelogGeneratorConfig) string { 43 | res, err := c.Impl.Generate(context.Background(), &GenerateChangelog_Request{Config: config}) 44 | if err != nil { 45 | panic(err) 46 | } 47 | return res.Changelog 48 | } 49 | 50 | func (c *ChangelogGeneratorClient) Init(m map[string]string) error { 51 | res, err := c.Impl.Init(context.Background(), &ChangelogGeneratorInit_Request{ 52 | Config: m, 53 | }) 54 | if err != nil { 55 | return err 56 | } 57 | if res.Error != "" { 58 | return errors.New(res.Error) 59 | } 60 | return nil 61 | } 62 | 63 | func (c *ChangelogGeneratorClient) Name() string { 64 | res, err := c.Impl.Name(context.Background(), &ChangelogGeneratorName_Request{}) 65 | if err != nil { 66 | panic(err) 67 | } 68 | return res.Name 69 | } 70 | 71 | func (c *ChangelogGeneratorClient) Version() string { 72 | res, err := c.Impl.Version(context.Background(), &ChangelogGeneratorVersion_Request{}) 73 | if err != nil { 74 | panic(err) 75 | } 76 | return res.Version 77 | } 78 | -------------------------------------------------------------------------------- /pkg/hooks/hooks.go: -------------------------------------------------------------------------------- 1 | package hooks 2 | 3 | import "fmt" 4 | 5 | type Hooks interface { 6 | Init(map[string]string) error 7 | Name() string 8 | Version() string 9 | Success(*SuccessHookConfig) error 10 | NoRelease(*NoReleaseConfig) error 11 | } 12 | 13 | type ChainedHooksExecutor struct { 14 | HooksChain []Hooks 15 | } 16 | 17 | func (c *ChainedHooksExecutor) Success(config *SuccessHookConfig) error { 18 | for _, h := range c.HooksChain { 19 | name := h.Name() 20 | err := h.Success(config) 21 | if err != nil { 22 | return fmt.Errorf("%s hook has failed: %w", name, err) 23 | } 24 | } 25 | return nil 26 | } 27 | 28 | func (c *ChainedHooksExecutor) NoRelease(config *NoReleaseConfig) error { 29 | for _, h := range c.HooksChain { 30 | name := h.Name() 31 | err := h.NoRelease(config) 32 | if err != nil { 33 | return fmt.Errorf("%s hook has failed: %w", name, err) 34 | } 35 | } 36 | return nil 37 | } 38 | 39 | func (c *ChainedHooksExecutor) Init(conf map[string]string) error { 40 | for _, h := range c.HooksChain { 41 | err := h.Init(conf) 42 | if err != nil { 43 | return err 44 | } 45 | } 46 | return nil 47 | } 48 | 49 | func (c *ChainedHooksExecutor) GetNameVersionPairs() []string { 50 | ret := make([]string, len(c.HooksChain)) 51 | for i, h := range c.HooksChain { 52 | ret[i] = fmt.Sprintf("%s@%s", h.Name(), h.Version()) 53 | } 54 | return ret 55 | } 56 | -------------------------------------------------------------------------------- /pkg/hooks/hooks.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option go_package = "github.com/go-semantic-release/semantic-release/v2/pkg/hooks"; 3 | 4 | import "pkg/semrel/structs.proto"; 5 | import "pkg/provider/provider.proto"; 6 | 7 | message HooksInit { 8 | message Request { 9 | map config = 1; 10 | } 11 | message Response { 12 | string error = 1; 13 | } 14 | } 15 | 16 | message HooksName { 17 | message Request {} 18 | message Response { 19 | string name = 1; 20 | } 21 | } 22 | 23 | message HooksVersion { 24 | message Request {} 25 | message Response { 26 | string version = 1; 27 | } 28 | } 29 | 30 | message SuccessHookConfig { 31 | repeated Commit commits = 1; 32 | Release prev_release = 2; 33 | Release new_release = 3; 34 | string changelog = 4; 35 | RepositoryInfo repo_info = 5; 36 | } 37 | 38 | message SuccessHook { 39 | message Request { 40 | SuccessHookConfig config = 1; 41 | } 42 | message Response { 43 | string error = 1; 44 | } 45 | } 46 | 47 | enum NoReleaseReason { 48 | CONDITION = 0; 49 | NO_CHANGE = 1; 50 | } 51 | 52 | message NoReleaseConfig { 53 | NoReleaseReason reason = 1; 54 | string message = 2; 55 | } 56 | 57 | message NoReleaseHook { 58 | message Request { 59 | NoReleaseConfig config = 1; 60 | } 61 | message Response { 62 | string error = 1; 63 | } 64 | } 65 | 66 | service HooksPlugin { 67 | rpc Init(HooksInit.Request) returns (HooksInit.Response); 68 | rpc Name(HooksName.Request) returns (HooksName.Response); 69 | rpc Version(HooksVersion.Request) returns (HooksVersion.Response); 70 | rpc Success(SuccessHook.Request) returns (SuccessHook.Response); 71 | rpc NoRelease(NoReleaseHook.Request) returns (NoReleaseHook.Response); 72 | } 73 | -------------------------------------------------------------------------------- /pkg/hooks/hooks_grpc.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT. 2 | // versions: 3 | // - protoc-gen-go-grpc v1.3.0 4 | // - protoc v4.24.4 5 | // source: pkg/hooks/hooks.proto 6 | 7 | package hooks 8 | 9 | import ( 10 | context "context" 11 | grpc "google.golang.org/grpc" 12 | codes "google.golang.org/grpc/codes" 13 | status "google.golang.org/grpc/status" 14 | ) 15 | 16 | // This is a compile-time assertion to ensure that this generated file 17 | // is compatible with the grpc package it is being compiled against. 18 | // Requires gRPC-Go v1.32.0 or later. 19 | const _ = grpc.SupportPackageIsVersion7 20 | 21 | const ( 22 | HooksPlugin_Init_FullMethodName = "/HooksPlugin/Init" 23 | HooksPlugin_Name_FullMethodName = "/HooksPlugin/Name" 24 | HooksPlugin_Version_FullMethodName = "/HooksPlugin/Version" 25 | HooksPlugin_Success_FullMethodName = "/HooksPlugin/Success" 26 | HooksPlugin_NoRelease_FullMethodName = "/HooksPlugin/NoRelease" 27 | ) 28 | 29 | // HooksPluginClient is the client API for HooksPlugin service. 30 | // 31 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. 32 | type HooksPluginClient interface { 33 | Init(ctx context.Context, in *HooksInit_Request, opts ...grpc.CallOption) (*HooksInit_Response, error) 34 | Name(ctx context.Context, in *HooksName_Request, opts ...grpc.CallOption) (*HooksName_Response, error) 35 | Version(ctx context.Context, in *HooksVersion_Request, opts ...grpc.CallOption) (*HooksVersion_Response, error) 36 | Success(ctx context.Context, in *SuccessHook_Request, opts ...grpc.CallOption) (*SuccessHook_Response, error) 37 | NoRelease(ctx context.Context, in *NoReleaseHook_Request, opts ...grpc.CallOption) (*NoReleaseHook_Response, error) 38 | } 39 | 40 | type hooksPluginClient struct { 41 | cc grpc.ClientConnInterface 42 | } 43 | 44 | func NewHooksPluginClient(cc grpc.ClientConnInterface) HooksPluginClient { 45 | return &hooksPluginClient{cc} 46 | } 47 | 48 | func (c *hooksPluginClient) Init(ctx context.Context, in *HooksInit_Request, opts ...grpc.CallOption) (*HooksInit_Response, error) { 49 | out := new(HooksInit_Response) 50 | err := c.cc.Invoke(ctx, HooksPlugin_Init_FullMethodName, in, out, opts...) 51 | if err != nil { 52 | return nil, err 53 | } 54 | return out, nil 55 | } 56 | 57 | func (c *hooksPluginClient) Name(ctx context.Context, in *HooksName_Request, opts ...grpc.CallOption) (*HooksName_Response, error) { 58 | out := new(HooksName_Response) 59 | err := c.cc.Invoke(ctx, HooksPlugin_Name_FullMethodName, in, out, opts...) 60 | if err != nil { 61 | return nil, err 62 | } 63 | return out, nil 64 | } 65 | 66 | func (c *hooksPluginClient) Version(ctx context.Context, in *HooksVersion_Request, opts ...grpc.CallOption) (*HooksVersion_Response, error) { 67 | out := new(HooksVersion_Response) 68 | err := c.cc.Invoke(ctx, HooksPlugin_Version_FullMethodName, in, out, opts...) 69 | if err != nil { 70 | return nil, err 71 | } 72 | return out, nil 73 | } 74 | 75 | func (c *hooksPluginClient) Success(ctx context.Context, in *SuccessHook_Request, opts ...grpc.CallOption) (*SuccessHook_Response, error) { 76 | out := new(SuccessHook_Response) 77 | err := c.cc.Invoke(ctx, HooksPlugin_Success_FullMethodName, in, out, opts...) 78 | if err != nil { 79 | return nil, err 80 | } 81 | return out, nil 82 | } 83 | 84 | func (c *hooksPluginClient) NoRelease(ctx context.Context, in *NoReleaseHook_Request, opts ...grpc.CallOption) (*NoReleaseHook_Response, error) { 85 | out := new(NoReleaseHook_Response) 86 | err := c.cc.Invoke(ctx, HooksPlugin_NoRelease_FullMethodName, in, out, opts...) 87 | if err != nil { 88 | return nil, err 89 | } 90 | return out, nil 91 | } 92 | 93 | // HooksPluginServer is the server API for HooksPlugin service. 94 | // All implementations must embed UnimplementedHooksPluginServer 95 | // for forward compatibility 96 | type HooksPluginServer interface { 97 | Init(context.Context, *HooksInit_Request) (*HooksInit_Response, error) 98 | Name(context.Context, *HooksName_Request) (*HooksName_Response, error) 99 | Version(context.Context, *HooksVersion_Request) (*HooksVersion_Response, error) 100 | Success(context.Context, *SuccessHook_Request) (*SuccessHook_Response, error) 101 | NoRelease(context.Context, *NoReleaseHook_Request) (*NoReleaseHook_Response, error) 102 | mustEmbedUnimplementedHooksPluginServer() 103 | } 104 | 105 | // UnimplementedHooksPluginServer must be embedded to have forward compatible implementations. 106 | type UnimplementedHooksPluginServer struct { 107 | } 108 | 109 | func (UnimplementedHooksPluginServer) Init(context.Context, *HooksInit_Request) (*HooksInit_Response, error) { 110 | return nil, status.Errorf(codes.Unimplemented, "method Init not implemented") 111 | } 112 | func (UnimplementedHooksPluginServer) Name(context.Context, *HooksName_Request) (*HooksName_Response, error) { 113 | return nil, status.Errorf(codes.Unimplemented, "method Name not implemented") 114 | } 115 | func (UnimplementedHooksPluginServer) Version(context.Context, *HooksVersion_Request) (*HooksVersion_Response, error) { 116 | return nil, status.Errorf(codes.Unimplemented, "method Version not implemented") 117 | } 118 | func (UnimplementedHooksPluginServer) Success(context.Context, *SuccessHook_Request) (*SuccessHook_Response, error) { 119 | return nil, status.Errorf(codes.Unimplemented, "method Success not implemented") 120 | } 121 | func (UnimplementedHooksPluginServer) NoRelease(context.Context, *NoReleaseHook_Request) (*NoReleaseHook_Response, error) { 122 | return nil, status.Errorf(codes.Unimplemented, "method NoRelease not implemented") 123 | } 124 | func (UnimplementedHooksPluginServer) mustEmbedUnimplementedHooksPluginServer() {} 125 | 126 | // UnsafeHooksPluginServer may be embedded to opt out of forward compatibility for this service. 127 | // Use of this interface is not recommended, as added methods to HooksPluginServer will 128 | // result in compilation errors. 129 | type UnsafeHooksPluginServer interface { 130 | mustEmbedUnimplementedHooksPluginServer() 131 | } 132 | 133 | func RegisterHooksPluginServer(s grpc.ServiceRegistrar, srv HooksPluginServer) { 134 | s.RegisterService(&HooksPlugin_ServiceDesc, srv) 135 | } 136 | 137 | func _HooksPlugin_Init_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 138 | in := new(HooksInit_Request) 139 | if err := dec(in); err != nil { 140 | return nil, err 141 | } 142 | if interceptor == nil { 143 | return srv.(HooksPluginServer).Init(ctx, in) 144 | } 145 | info := &grpc.UnaryServerInfo{ 146 | Server: srv, 147 | FullMethod: HooksPlugin_Init_FullMethodName, 148 | } 149 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 150 | return srv.(HooksPluginServer).Init(ctx, req.(*HooksInit_Request)) 151 | } 152 | return interceptor(ctx, in, info, handler) 153 | } 154 | 155 | func _HooksPlugin_Name_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 156 | in := new(HooksName_Request) 157 | if err := dec(in); err != nil { 158 | return nil, err 159 | } 160 | if interceptor == nil { 161 | return srv.(HooksPluginServer).Name(ctx, in) 162 | } 163 | info := &grpc.UnaryServerInfo{ 164 | Server: srv, 165 | FullMethod: HooksPlugin_Name_FullMethodName, 166 | } 167 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 168 | return srv.(HooksPluginServer).Name(ctx, req.(*HooksName_Request)) 169 | } 170 | return interceptor(ctx, in, info, handler) 171 | } 172 | 173 | func _HooksPlugin_Version_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 174 | in := new(HooksVersion_Request) 175 | if err := dec(in); err != nil { 176 | return nil, err 177 | } 178 | if interceptor == nil { 179 | return srv.(HooksPluginServer).Version(ctx, in) 180 | } 181 | info := &grpc.UnaryServerInfo{ 182 | Server: srv, 183 | FullMethod: HooksPlugin_Version_FullMethodName, 184 | } 185 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 186 | return srv.(HooksPluginServer).Version(ctx, req.(*HooksVersion_Request)) 187 | } 188 | return interceptor(ctx, in, info, handler) 189 | } 190 | 191 | func _HooksPlugin_Success_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 192 | in := new(SuccessHook_Request) 193 | if err := dec(in); err != nil { 194 | return nil, err 195 | } 196 | if interceptor == nil { 197 | return srv.(HooksPluginServer).Success(ctx, in) 198 | } 199 | info := &grpc.UnaryServerInfo{ 200 | Server: srv, 201 | FullMethod: HooksPlugin_Success_FullMethodName, 202 | } 203 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 204 | return srv.(HooksPluginServer).Success(ctx, req.(*SuccessHook_Request)) 205 | } 206 | return interceptor(ctx, in, info, handler) 207 | } 208 | 209 | func _HooksPlugin_NoRelease_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 210 | in := new(NoReleaseHook_Request) 211 | if err := dec(in); err != nil { 212 | return nil, err 213 | } 214 | if interceptor == nil { 215 | return srv.(HooksPluginServer).NoRelease(ctx, in) 216 | } 217 | info := &grpc.UnaryServerInfo{ 218 | Server: srv, 219 | FullMethod: HooksPlugin_NoRelease_FullMethodName, 220 | } 221 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 222 | return srv.(HooksPluginServer).NoRelease(ctx, req.(*NoReleaseHook_Request)) 223 | } 224 | return interceptor(ctx, in, info, handler) 225 | } 226 | 227 | // HooksPlugin_ServiceDesc is the grpc.ServiceDesc for HooksPlugin service. 228 | // It's only intended for direct use with grpc.RegisterService, 229 | // and not to be introspected or modified (even as a copy) 230 | var HooksPlugin_ServiceDesc = grpc.ServiceDesc{ 231 | ServiceName: "HooksPlugin", 232 | HandlerType: (*HooksPluginServer)(nil), 233 | Methods: []grpc.MethodDesc{ 234 | { 235 | MethodName: "Init", 236 | Handler: _HooksPlugin_Init_Handler, 237 | }, 238 | { 239 | MethodName: "Name", 240 | Handler: _HooksPlugin_Name_Handler, 241 | }, 242 | { 243 | MethodName: "Version", 244 | Handler: _HooksPlugin_Version_Handler, 245 | }, 246 | { 247 | MethodName: "Success", 248 | Handler: _HooksPlugin_Success_Handler, 249 | }, 250 | { 251 | MethodName: "NoRelease", 252 | Handler: _HooksPlugin_NoRelease_Handler, 253 | }, 254 | }, 255 | Streams: []grpc.StreamDesc{}, 256 | Metadata: "pkg/hooks/hooks.proto", 257 | } 258 | -------------------------------------------------------------------------------- /pkg/hooks/hooks_wrapper.go: -------------------------------------------------------------------------------- 1 | package hooks 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | ) 7 | 8 | const PluginName = "hooks" 9 | 10 | type Server struct { 11 | Impl Hooks 12 | UnimplementedHooksPluginServer 13 | } 14 | 15 | func (h *Server) Init(_ context.Context, request *HooksInit_Request) (*HooksInit_Response, error) { 16 | err := h.Impl.Init(request.Config) 17 | res := &HooksInit_Response{} 18 | if err != nil { 19 | res.Error = err.Error() 20 | } 21 | return res, nil 22 | } 23 | 24 | func (h *Server) Name(_ context.Context, _ *HooksName_Request) (*HooksName_Response, error) { 25 | return &HooksName_Response{Name: h.Impl.Name()}, nil 26 | } 27 | 28 | func (h *Server) Version(_ context.Context, _ *HooksVersion_Request) (*HooksVersion_Response, error) { 29 | return &HooksVersion_Response{Version: h.Impl.Version()}, nil 30 | } 31 | 32 | func (h *Server) Success(_ context.Context, request *SuccessHook_Request) (*SuccessHook_Response, error) { 33 | err := h.Impl.Success(request.Config) 34 | res := &SuccessHook_Response{} 35 | if err != nil { 36 | res.Error = err.Error() 37 | } 38 | return res, nil 39 | } 40 | 41 | func (h *Server) NoRelease(_ context.Context, request *NoReleaseHook_Request) (*NoReleaseHook_Response, error) { 42 | err := h.Impl.NoRelease(request.Config) 43 | res := &NoReleaseHook_Response{} 44 | if err != nil { 45 | res.Error = err.Error() 46 | } 47 | return res, nil 48 | } 49 | 50 | type Client struct { 51 | Impl HooksPluginClient 52 | } 53 | 54 | func (h *Client) Init(m map[string]string) error { 55 | res, err := h.Impl.Init(context.Background(), &HooksInit_Request{ 56 | Config: m, 57 | }) 58 | if err != nil { 59 | return err 60 | } 61 | if res.Error != "" { 62 | return errors.New(res.Error) 63 | } 64 | return nil 65 | } 66 | 67 | func (h *Client) Name() string { 68 | res, err := h.Impl.Name(context.Background(), &HooksName_Request{}) 69 | if err != nil { 70 | panic(err) 71 | } 72 | return res.Name 73 | } 74 | 75 | func (h *Client) Version() string { 76 | res, err := h.Impl.Version(context.Background(), &HooksVersion_Request{}) 77 | if err != nil { 78 | panic(err) 79 | } 80 | return res.Version 81 | } 82 | 83 | func (h *Client) Success(config *SuccessHookConfig) error { 84 | res, err := h.Impl.Success(context.Background(), &SuccessHook_Request{ 85 | Config: config, 86 | }) 87 | if err != nil { 88 | return err 89 | } 90 | if res.Error != "" { 91 | return errors.New(res.Error) 92 | } 93 | return nil 94 | } 95 | 96 | func (h *Client) NoRelease(config *NoReleaseConfig) error { 97 | res, err := h.Impl.NoRelease(context.Background(), &NoReleaseHook_Request{ 98 | Config: config, 99 | }) 100 | if err != nil { 101 | return err 102 | } 103 | if res.Error != "" { 104 | return errors.New(res.Error) 105 | } 106 | return nil 107 | } 108 | -------------------------------------------------------------------------------- /pkg/plugin/client.go: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "log" 8 | "os" 9 | "os/exec" 10 | "strings" 11 | "sync" 12 | 13 | "github.com/hashicorp/go-hclog" 14 | "github.com/hashicorp/go-plugin" 15 | ) 16 | 17 | var ( 18 | runningClientsMx sync.Mutex 19 | runningClients = make([]*plugin.Client, 0) 20 | ) 21 | 22 | func KillAllPlugins() { 23 | runningClientsMx.Lock() 24 | defer runningClientsMx.Unlock() 25 | for _, c := range runningClients { 26 | c.Kill() 27 | } 28 | } 29 | 30 | func StartPlugin(pluginInfo *Info) (interface{}, error) { 31 | runningClientsMx.Lock() 32 | defer runningClientsMx.Unlock() 33 | logR, logW := io.Pipe() 34 | pluginLogger := log.New(os.Stderr, fmt.Sprintf("[%s]: ", pluginInfo.ShortNormalizedName), 0) 35 | go func() { 36 | logLineScanner := bufio.NewScanner(logR) 37 | for logLineScanner.Scan() { 38 | line := logLineScanner.Text() 39 | // skip JSON logging 40 | if strings.HasPrefix(line, "{") { 41 | continue 42 | } 43 | pluginLogger.Println(line) 44 | } 45 | }() 46 | 47 | cmd := exec.Command(pluginInfo.BinPath) 48 | cmd.SysProcAttr = GetSysProcAttr() 49 | 50 | client := plugin.NewClient(&plugin.ClientConfig{ 51 | HandshakeConfig: Handshake, 52 | VersionedPlugins: map[int]plugin.PluginSet{ 53 | 1: { 54 | pluginInfo.Type: &GRPCWrapper{ 55 | Type: pluginInfo.Type, 56 | }, 57 | }, 58 | }, 59 | Cmd: cmd, 60 | AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC}, 61 | Logger: hclog.NewNullLogger(), 62 | Stderr: logW, 63 | }) 64 | 65 | rpcClient, err := client.Client() 66 | if err != nil { 67 | client.Kill() 68 | return nil, err 69 | } 70 | raw, err := rpcClient.Dispense(pluginInfo.Type) 71 | if err != nil { 72 | client.Kill() 73 | return nil, err 74 | } 75 | runningClients = append(runningClients, client) 76 | return raw, nil 77 | } 78 | -------------------------------------------------------------------------------- /pkg/plugin/discovery/discovery.go: -------------------------------------------------------------------------------- 1 | package discovery 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/go-semantic-release/semantic-release/v2/pkg/config" 8 | "github.com/go-semantic-release/semantic-release/v2/pkg/plugin" 9 | "github.com/go-semantic-release/semantic-release/v2/pkg/plugin/discovery/resolver" 10 | "github.com/go-semantic-release/semantic-release/v2/pkg/plugin/discovery/resolver/github" 11 | "github.com/go-semantic-release/semantic-release/v2/pkg/plugin/discovery/resolver/registry" 12 | ) 13 | 14 | type Discovery struct { 15 | config *config.Config 16 | resolvers map[string]resolver.Resolver 17 | } 18 | 19 | func loadResolvers(resolvers ...resolver.Resolver) (map[string]resolver.Resolver, error) { 20 | resolversMap := make(map[string]resolver.Resolver) 21 | for _, r := range resolvers { 22 | for _, name := range r.Names() { 23 | if name == "default" { 24 | return nil, fmt.Errorf("resolver name default is reserved") 25 | } 26 | if _, ok := resolversMap[name]; ok { 27 | return nil, fmt.Errorf("resolver %s already exists", name) 28 | } 29 | resolversMap[name] = r 30 | } 31 | } 32 | return resolversMap, nil 33 | } 34 | 35 | func New(config *config.Config) (*Discovery, error) { 36 | resolvers, err := loadResolvers( 37 | registry.NewResolver(config.PluginResolverEndpoint), 38 | github.NewResolver(), 39 | ) 40 | if err != nil { 41 | return nil, err 42 | } 43 | // use the registry resolver as default 44 | resolvers["default"] = resolvers[config.PluginResolver] 45 | 46 | if resolvers["default"] == nil { 47 | return nil, fmt.Errorf("resolver %s does not exist", config.PluginResolver) 48 | } 49 | 50 | return &Discovery{ 51 | config: config, 52 | resolvers: resolvers, 53 | }, nil 54 | } 55 | 56 | func (d *Discovery) fetchPlugin(pluginInfo *plugin.Info) (string, error) { 57 | pluginResolver, ok := d.resolvers[pluginInfo.Resolver] 58 | if !ok { 59 | return "", fmt.Errorf("resolver %s not found", pluginInfo.Resolver) 60 | } 61 | 62 | downloadInfo, err := pluginResolver.ResolvePlugin(pluginInfo) 63 | if err != nil { 64 | return "", err 65 | } 66 | 67 | return downloadPlugin(pluginInfo, downloadInfo, d.config.ShowProgress) 68 | } 69 | 70 | func (d *Discovery) IsBatchResolver(resolverName string) bool { 71 | _, ok := d.resolvers[resolverName].(resolver.BatchResolver) 72 | return ok 73 | } 74 | 75 | func (d *Discovery) FindPluginByPluginInfo(pInfo *plugin.Info) error { 76 | err := setAndEnsurePluginPath(pInfo) 77 | if err != nil { 78 | return err 79 | } 80 | 81 | binPath, err := findPluginLocally(pInfo) 82 | if errors.Is(err, ErrPluginNotFound) { 83 | binPath, err = d.fetchPlugin(pInfo) 84 | if err != nil { 85 | return err 86 | } 87 | } else if err != nil { 88 | return err 89 | } 90 | pInfo.BinPath = binPath 91 | return nil 92 | } 93 | 94 | func (d *Discovery) FindPlugin(t, name string) (*plugin.Info, error) { 95 | pInfo, err := plugin.GetPluginInfo(t, name) 96 | if err != nil { 97 | return nil, err 98 | } 99 | err = d.FindPluginByPluginInfo(pInfo) 100 | if err != nil { 101 | return nil, err 102 | } 103 | return pInfo, nil 104 | } 105 | 106 | func (d *Discovery) FindPluginsWithBatchResolver(resolverName string, pInfos []*plugin.Info) error { 107 | if !d.IsBatchResolver(resolverName) { 108 | return fmt.Errorf("resolver %s does not support batch resolving", resolverName) 109 | } 110 | missingPlugins := make([]*plugin.Info, 0) 111 | for _, pInfo := range pInfos { 112 | err := setAndEnsurePluginPath(pInfo) 113 | if err != nil { 114 | return err 115 | } 116 | 117 | binPath, err := findPluginLocally(pInfo) 118 | if errors.Is(err, ErrPluginNotFound) { 119 | missingPlugins = append(missingPlugins, pInfo) 120 | continue 121 | } else if err != nil { 122 | return err 123 | } 124 | pInfo.BinPath = binPath 125 | } 126 | 127 | // no plugins need to be downloaded 128 | if len(missingPlugins) == 0 { 129 | return nil 130 | } 131 | 132 | if len(missingPlugins) == 1 { 133 | return fmt.Errorf("no batch prefetching possible for a single plugin") 134 | } 135 | 136 | batchResolver := d.resolvers[resolverName].(resolver.BatchResolver) 137 | batchDownloadInfo, err := batchResolver.BatchResolvePlugins(missingPlugins) 138 | if err != nil { 139 | return err 140 | } 141 | 142 | return downloadBatchPlugins(missingPlugins, batchDownloadInfo, d.config.ShowProgress) 143 | } 144 | -------------------------------------------------------------------------------- /pkg/plugin/discovery/download.go: -------------------------------------------------------------------------------- 1 | package discovery 2 | 3 | import ( 4 | "archive/tar" 5 | "compress/gzip" 6 | "crypto/sha256" 7 | "encoding/hex" 8 | "errors" 9 | "fmt" 10 | "io" 11 | "os" 12 | "path" 13 | "regexp" 14 | "strings" 15 | "time" 16 | 17 | "github.com/cavaliergopher/grab/v3" 18 | "github.com/go-semantic-release/semantic-release/v2/pkg/plugin" 19 | "github.com/go-semantic-release/semantic-release/v2/pkg/plugin/discovery/resolver" 20 | "github.com/schollz/progressbar/v3" 21 | ) 22 | 23 | func showDownloadProgressBar(name string, res *grab.Response) { 24 | bar := progressbar.NewOptions64( 25 | res.Size(), 26 | progressbar.OptionSetDescription(name), 27 | progressbar.OptionSetWriter(os.Stderr), 28 | progressbar.OptionShowBytes(true), 29 | progressbar.OptionSetWidth(10), 30 | progressbar.OptionThrottle(65*time.Millisecond), 31 | progressbar.OptionShowCount(), 32 | progressbar.OptionSetWidth(40), 33 | progressbar.OptionClearOnFinish(), 34 | progressbar.OptionSetPredictTime(false), 35 | ) 36 | t := time.NewTicker(100 * time.Millisecond) 37 | done := make(chan struct{}) 38 | go func() { 39 | for { 40 | select { 41 | case <-t.C: 42 | _ = bar.Set64(res.BytesComplete()) 43 | case <-res.Done: 44 | _ = bar.Finish() 45 | t.Stop() 46 | done <- struct{}{} 47 | return 48 | } 49 | } 50 | }() 51 | <-done 52 | } 53 | 54 | func extractFileFromTarGz(name, inputFile, outputFile string) error { 55 | compressedFile, err := os.Open(inputFile) 56 | if err != nil { 57 | return err 58 | } 59 | defer compressedFile.Close() 60 | 61 | decompressedFile, err := gzip.NewReader(compressedFile) 62 | if err != nil { 63 | return err 64 | } 65 | defer decompressedFile.Close() 66 | 67 | tarReader := tar.NewReader(decompressedFile) 68 | for { 69 | header, err := tarReader.Next() 70 | if errors.Is(err, io.EOF) { 71 | return fmt.Errorf("could not extract file") 72 | } 73 | if err != nil { 74 | return err 75 | } 76 | if header.Typeflag == tar.TypeReg && strings.HasPrefix(header.Name, name) { 77 | outFile, err := os.OpenFile(outputFile, os.O_CREATE|os.O_WRONLY, 0o755) 78 | if err != nil { 79 | return err 80 | } 81 | _, err = io.Copy(outFile, tarReader) 82 | outFile.Close() 83 | if err != nil { 84 | return err 85 | } 86 | return nil 87 | } 88 | } 89 | } 90 | 91 | var tgzRegexp = regexp.MustCompile(`^(.*)\.(tgz|tar\.gz)$`) 92 | 93 | func downloadPlugin(pluginInfo *plugin.Info, downloadInfo *resolver.PluginDownloadInfo, showProgress bool) (string, error) { 94 | versionDir := path.Join(pluginInfo.PluginPath, downloadInfo.Version) 95 | targetFile := path.Join(versionDir, downloadInfo.FileName) 96 | req, err := grab.NewRequest(targetFile, downloadInfo.URL) 97 | if err != nil { 98 | return "", err 99 | } 100 | if downloadInfo.Checksum != "" { 101 | sum, decErr := hex.DecodeString(downloadInfo.Checksum) 102 | if decErr != nil { 103 | return "", decErr 104 | } 105 | req.SetChecksum(sha256.New(), sum, true) 106 | } 107 | 108 | res := grab.DefaultClient.Do(req) 109 | if showProgress { 110 | showDownloadProgressBar(pluginInfo.ShortNormalizedName, res) 111 | } 112 | err = res.Err() 113 | if err != nil { 114 | return "", err 115 | } 116 | 117 | tgzMatch := tgzRegexp.FindStringSubmatch(downloadInfo.FileName) 118 | if len(tgzMatch) > 2 { 119 | outFile := path.Join(versionDir, tgzMatch[1]) 120 | if err = extractFileFromTarGz(pluginInfo.Name, targetFile, outFile); err != nil { 121 | return "", err 122 | } 123 | targetFile = outFile 124 | } 125 | if err := os.Chmod(targetFile, 0o755); err != nil { 126 | return "", err 127 | } 128 | return targetFile, nil 129 | } 130 | 131 | //gocyclo:ignore 132 | func downloadBatchPlugins(pluginInfos []*plugin.Info, downloadInfo *resolver.BatchPluginDownloadInfo, showProgress bool) error { 133 | req, err := grab.NewRequest(PluginDir, downloadInfo.URL) 134 | if err != nil { 135 | return err 136 | } 137 | if downloadInfo.Checksum != "" { 138 | sum, decErr := hex.DecodeString(downloadInfo.Checksum) 139 | if decErr != nil { 140 | return fmt.Errorf("could not decode checksum: %w", decErr) 141 | } 142 | req.SetChecksum(sha256.New(), sum, true) 143 | } 144 | 145 | res := grab.DefaultClient.Do(req) 146 | if showProgress { 147 | showDownloadProgressBar("plugin-archive.tar.gz", res) 148 | } 149 | err = res.Err() 150 | if err != nil { 151 | return err 152 | } 153 | defer os.Remove(res.Filename) 154 | 155 | tgzFile, err := os.Open(res.Filename) 156 | if err != nil { 157 | return err 158 | } 159 | defer tgzFile.Close() 160 | 161 | gunzip, err := gzip.NewReader(tgzFile) 162 | if err != nil { 163 | return err 164 | } 165 | defer gunzip.Close() 166 | 167 | tarReader := tar.NewReader(gunzip) 168 | for { 169 | header, tarErr := tarReader.Next() 170 | if errors.Is(tarErr, io.EOF) { 171 | break 172 | } 173 | if tarErr != nil { 174 | return tarErr 175 | } 176 | if header.Typeflag != tar.TypeReg { 177 | continue 178 | } 179 | 180 | outFileName := path.Join(PluginDir, header.Name) 181 | outDirName := path.Dir(outFileName) 182 | if err = os.MkdirAll(outDirName, 0o755); err != nil { 183 | return err 184 | } 185 | 186 | outFile, oErr := os.OpenFile(outFileName, os.O_CREATE|os.O_WRONLY, 0o755) 187 | if oErr != nil { 188 | return oErr 189 | } 190 | _, cErr := io.Copy(outFile, tarReader) 191 | _ = outFile.Close() 192 | if cErr != nil { 193 | return cErr 194 | } 195 | 196 | for _, pluginInfo := range pluginInfos { 197 | if strings.HasPrefix(path.Join(PluginDir, header.Name), pluginInfo.PluginPath) { 198 | pluginInfo.BinPath = outFileName 199 | } 200 | } 201 | 202 | } 203 | return nil 204 | } 205 | -------------------------------------------------------------------------------- /pkg/plugin/discovery/local.go: -------------------------------------------------------------------------------- 1 | package discovery 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "path" 8 | "runtime" 9 | "sort" 10 | 11 | "github.com/Masterminds/semver/v3" 12 | "github.com/go-semantic-release/semantic-release/v2/pkg/plugin" 13 | ) 14 | 15 | const PluginDir = ".semrel" 16 | 17 | var osArchDir = runtime.GOOS + "_" + runtime.GOARCH 18 | 19 | func setAndEnsurePluginPath(pluginInfo *plugin.Info) error { 20 | pluginPath := path.Join(PluginDir, osArchDir, pluginInfo.ShortNormalizedName) 21 | if _, err := os.Stat(pluginPath); os.IsNotExist(err) { 22 | err = os.MkdirAll(pluginPath, 0o755) 23 | if err != nil { 24 | return err 25 | } 26 | } else if err != nil { 27 | return err 28 | } 29 | pluginInfo.PluginPath = pluginPath 30 | return nil 31 | } 32 | 33 | var ErrPluginNotFound = errors.New("no plugin was found") 34 | 35 | func getMatchingVersionDir(pluginInfo *plugin.Info) (string, error) { 36 | vDirs, err := os.ReadDir(pluginInfo.PluginPath) 37 | if err != nil { 38 | return "", err 39 | } 40 | foundVersions := make(semver.Collection, 0) 41 | for _, f := range vDirs { 42 | if f.IsDir() { 43 | fVer, err := semver.NewVersion(f.Name()) 44 | if err != nil { 45 | continue 46 | } 47 | foundVersions = append(foundVersions, fVer) 48 | } 49 | } 50 | 51 | if len(foundVersions) == 0 { 52 | return "", nil 53 | } 54 | sort.Sort(sort.Reverse(foundVersions)) 55 | 56 | if pluginInfo.Constraint == nil { 57 | return path.Join(pluginInfo.PluginPath, foundVersions[0].String()), nil 58 | } 59 | 60 | for _, v := range foundVersions { 61 | if pluginInfo.Constraint.Check(v) { 62 | return path.Join(pluginInfo.PluginPath, v.String()), nil 63 | } 64 | } 65 | return "", nil 66 | } 67 | 68 | func findPluginLocally(pluginInfo *plugin.Info) (string, error) { 69 | vPth, err := getMatchingVersionDir(pluginInfo) 70 | if err != nil { 71 | return "", err 72 | } 73 | 74 | if vPth == "" { 75 | return "", ErrPluginNotFound 76 | } 77 | 78 | files, err := os.ReadDir(vPth) 79 | if err != nil { 80 | return "", err 81 | } 82 | if len(files) == 0 { 83 | return "", ErrPluginNotFound 84 | } 85 | for _, f := range files { 86 | if f.IsDir() { 87 | continue 88 | } 89 | fInfo, err := f.Info() 90 | if err != nil { 91 | return "", fmt.Errorf("failed to get file info for %s: %w", f.Name(), err) 92 | } 93 | // check if the file is executable by the user 94 | if fInfo.Mode()&0o100 == 0 { 95 | continue 96 | } 97 | return path.Join(vPth, f.Name()), nil 98 | } 99 | return "", ErrPluginNotFound 100 | } 101 | -------------------------------------------------------------------------------- /pkg/plugin/discovery/resolver/github/github.go: -------------------------------------------------------------------------------- 1 | package github 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "os" 9 | "regexp" 10 | "runtime" 11 | "sort" 12 | "strings" 13 | 14 | "github.com/Masterminds/semver/v3" 15 | "github.com/go-semantic-release/semantic-release/v2/pkg/plugin" 16 | "github.com/go-semantic-release/semantic-release/v2/pkg/plugin/discovery/resolver" 17 | "github.com/google/go-github/v59/github" 18 | "golang.org/x/oauth2" 19 | ) 20 | 21 | var _ resolver.Resolver = &Resolver{} 22 | 23 | type Resolver struct { 24 | ghClient *github.Client 25 | } 26 | 27 | func NewResolver() *Resolver { 28 | var tc *http.Client 29 | if ghToken := os.Getenv("GITHUB_TOKEN"); ghToken != "" { 30 | tc = oauth2.NewClient(context.Background(), oauth2.StaticTokenSource(&oauth2.Token{AccessToken: ghToken})) 31 | } 32 | return &Resolver{ 33 | ghClient: github.NewClient(tc), 34 | } 35 | } 36 | 37 | //gocyclo:ignore 38 | func (g *Resolver) githubReleaseToDownloadInfo(repoOwner, repoName string, release *github.RepositoryRelease) (*resolver.PluginDownloadInfo, error) { 39 | var checksumAsset *github.ReleaseAsset 40 | var pluginAsset *github.ReleaseAsset 41 | osArchRe := regexp.MustCompile("(?i)" + runtime.GOOS + "(_|-)" + runtime.GOARCH) 42 | for _, asset := range release.Assets { 43 | assetName := asset.GetName() 44 | if checksumAsset == nil && asset.GetSize() <= 4096 && strings.Contains(strings.ToLower(assetName), "checksum") { 45 | checksumAsset = asset 46 | } 47 | if pluginAsset == nil && osArchRe.MatchString(assetName) { 48 | pluginAsset = asset 49 | } 50 | } 51 | 52 | if pluginAsset == nil { 53 | return nil, fmt.Errorf("no matching plugin binary was found for %s/%s", runtime.GOOS, runtime.GOARCH) 54 | } 55 | 56 | foundChecksum := "" 57 | if checksumAsset != nil { 58 | checksumDownload, _, err := g.ghClient.Repositories.DownloadReleaseAsset(context.Background(), repoOwner, repoName, checksumAsset.GetID(), http.DefaultClient) 59 | if err != nil { 60 | return nil, err 61 | } 62 | checksumData, err := io.ReadAll(checksumDownload) 63 | checksumDownload.Close() 64 | if err != nil { 65 | return nil, err 66 | } 67 | for _, l := range strings.Split(string(checksumData), "\n") { 68 | sl := strings.Split(l, " ") 69 | if len(sl) < 3 { 70 | continue 71 | } 72 | if sl[2] == pluginAsset.GetName() { 73 | foundChecksum = sl[0] 74 | } 75 | } 76 | } 77 | 78 | return &resolver.PluginDownloadInfo{ 79 | URL: pluginAsset.GetBrowserDownloadURL(), 80 | Checksum: foundChecksum, 81 | FileName: pluginAsset.GetName(), 82 | Version: strings.TrimLeft(release.GetTagName(), "v"), 83 | }, nil 84 | } 85 | 86 | type ghRelease struct { 87 | version *semver.Version 88 | release *github.RepositoryRelease 89 | } 90 | 91 | type ghReleases []*ghRelease 92 | 93 | func (gr ghReleases) Len() int { return len(gr) } 94 | func (gr ghReleases) Less(i, j int) bool { return gr[j].version.LessThan(gr[i].version) } 95 | func (gr ghReleases) Swap(i, j int) { gr[i], gr[j] = gr[j], gr[i] } 96 | 97 | func (g *Resolver) getAllValidGitHubReleases(repoOwner, repoName string) (ghReleases, error) { 98 | ret := make(ghReleases, 0) 99 | opts := &github.ListOptions{Page: 1, PerPage: 100} 100 | for { 101 | releases, resp, err := g.ghClient.Repositories.ListReleases(context.Background(), repoOwner, repoName, opts) 102 | if err != nil { 103 | return nil, err 104 | } 105 | for _, release := range releases { 106 | if release.GetDraft() { 107 | continue 108 | } 109 | 110 | if len(release.Assets) == 0 { 111 | continue 112 | } 113 | 114 | if semverTag, err := semver.NewVersion(release.GetTagName()); err == nil { 115 | ret = append(ret, &ghRelease{version: semverTag, release: release}) 116 | } 117 | } 118 | 119 | if resp.NextPage == 0 { 120 | break 121 | } 122 | opts.Page = resp.NextPage 123 | } 124 | sort.Sort(ret) 125 | return ret, nil 126 | } 127 | 128 | func (g *Resolver) ResolvePlugin(pluginInfo *plugin.Info) (*resolver.PluginDownloadInfo, error) { 129 | if pluginInfo.RepoSlug == "" { 130 | pluginInfo.RepoSlug = knownPlugins[pluginInfo.ShortNormalizedName] 131 | } 132 | if pluginInfo.RepoSlug == "" { 133 | return nil, fmt.Errorf("repo slug not found") 134 | } 135 | repoOwner, repoName, _ := strings.Cut(pluginInfo.RepoSlug, "/") 136 | 137 | var foundRelease *github.RepositoryRelease 138 | if pluginInfo.Constraint == nil { 139 | release, _, err := g.ghClient.Repositories.GetLatestRelease(context.Background(), repoOwner, repoName) 140 | if err != nil { 141 | return nil, err 142 | } 143 | foundRelease = release 144 | } else { 145 | validReleases, err := g.getAllValidGitHubReleases(repoOwner, repoName) 146 | if err != nil { 147 | return nil, err 148 | } 149 | for _, release := range validReleases { 150 | if pluginInfo.Constraint.Check(release.version) { 151 | foundRelease = release.release 152 | break 153 | } 154 | } 155 | } 156 | 157 | if foundRelease == nil { 158 | return nil, fmt.Errorf("no matching release was found") 159 | } 160 | 161 | di, err := g.githubReleaseToDownloadInfo(repoOwner, repoName, foundRelease) 162 | return di, err 163 | } 164 | 165 | func (g *Resolver) Names() []string { 166 | return []string{"github", "gh"} 167 | } 168 | -------------------------------------------------------------------------------- /pkg/plugin/discovery/resolver/github/known_plugins.go: -------------------------------------------------------------------------------- 1 | package github 2 | 3 | var knownPlugins = map[string]string{ 4 | "provider-github": "go-semantic-release/provider-github", 5 | "provider-gitlab": "go-semantic-release/provider-gitlab", 6 | "changelog-generator-default": "go-semantic-release/changelog-generator-default", 7 | "commit-analyzer-default": "go-semantic-release/commit-analyzer-cz", 8 | "condition-default": "go-semantic-release/condition-default", 9 | "condition-github": "go-semantic-release/condition-github", 10 | "condition-gitlab": "go-semantic-release/condition-gitlab", 11 | "files-updater-npm": "go-semantic-release/files-updater-npm", 12 | "provider-git": "go-semantic-release/provider-git", 13 | "condition-bitbucket": "go-semantic-release/condition-bitbucket", 14 | "files-updater-helm": "go-semantic-release/files-updater-helm", 15 | "hooks-goreleaser": "go-semantic-release/hooks-goreleaser", 16 | "hooks-npm-binary-releaser": "go-semantic-release/hooks-npm-binary-releaser", 17 | "hooks-plugin-registry-update": "go-semantic-release/hooks-plugin-registry-update", 18 | "hooks-exec": "go-semantic-release/hooks-exec", 19 | } 20 | -------------------------------------------------------------------------------- /pkg/plugin/discovery/resolver/registry/resolver.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "runtime" 7 | "sort" 8 | 9 | "github.com/Masterminds/semver/v3" 10 | "github.com/go-semantic-release/plugin-registry/pkg/client" 11 | "github.com/go-semantic-release/plugin-registry/pkg/registry" 12 | "github.com/go-semantic-release/semantic-release/v2/pkg/plugin" 13 | "github.com/go-semantic-release/semantic-release/v2/pkg/plugin/discovery/resolver" 14 | ) 15 | 16 | var ( 17 | _ resolver.Resolver = &Resolver{} 18 | _ resolver.BatchResolver = &Resolver{} 19 | ) 20 | 21 | type Resolver struct { 22 | client *client.Client 23 | } 24 | 25 | func NewResolver(endpoint string) *Resolver { 26 | if endpoint == "" { 27 | endpoint = client.DefaultProductionEndpoint 28 | } 29 | return &Resolver{ 30 | client: client.New(endpoint), 31 | } 32 | } 33 | 34 | func (r *Resolver) ResolvePlugin(pluginInfo *plugin.Info) (*resolver.PluginDownloadInfo, error) { 35 | getPluginRes, err := r.client.GetPlugin(context.Background(), pluginInfo.ShortNormalizedName) 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | osArch := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH) 41 | if pluginInfo.Constraint == nil { 42 | foundAsset := getPluginRes.LatestRelease.Assets[osArch] 43 | if foundAsset == nil { 44 | return nil, fmt.Errorf("a matching plugin was not found for %s/%s", runtime.GOOS, runtime.GOARCH) 45 | } 46 | return &resolver.PluginDownloadInfo{ 47 | URL: foundAsset.URL, 48 | Checksum: foundAsset.Checksum, 49 | FileName: foundAsset.FileName, 50 | Version: getPluginRes.LatestRelease.Version, 51 | }, nil 52 | } 53 | 54 | foundVersion := "" 55 | versions := make(semver.Collection, 0) 56 | for _, v := range getPluginRes.Versions { 57 | pv, sErr := semver.NewVersion(v) 58 | if sErr != nil { 59 | return nil, sErr 60 | } 61 | versions = append(versions, pv) 62 | } 63 | sort.Sort(sort.Reverse(versions)) 64 | for _, v := range versions { 65 | if pluginInfo.Constraint.Check(v) { 66 | foundVersion = v.String() 67 | break 68 | } 69 | } 70 | if foundVersion == "" { 71 | return nil, fmt.Errorf("no matching version was found") 72 | } 73 | 74 | pluginRelease, err := r.client.GetPluginRelease(context.Background(), pluginInfo.ShortNormalizedName, foundVersion) 75 | if err != nil { 76 | return nil, err 77 | } 78 | foundAsset := pluginRelease.Assets[osArch] 79 | if foundAsset == nil { 80 | return nil, fmt.Errorf("a matching plugin was not found for %s/%s", runtime.GOOS, runtime.GOARCH) 81 | } 82 | 83 | return &resolver.PluginDownloadInfo{ 84 | URL: foundAsset.URL, 85 | Checksum: foundAsset.Checksum, 86 | FileName: foundAsset.FileName, 87 | Version: getPluginRes.LatestRelease.Version, 88 | }, nil 89 | } 90 | 91 | func (r *Resolver) BatchResolvePlugins(pluginInfos []*plugin.Info) (*resolver.BatchPluginDownloadInfo, error) { 92 | batchRequest := ®istry.BatchRequest{ 93 | OS: runtime.GOOS, 94 | Arch: runtime.GOARCH, 95 | Plugins: make([]*registry.BatchRequestPlugin, len(pluginInfos)), 96 | } 97 | for i, pluginInfo := range pluginInfos { 98 | versionConstraint := "latest" 99 | if pluginInfo.Constraint != nil { 100 | versionConstraint = pluginInfo.Constraint.String() 101 | } 102 | batchRequest.Plugins[i] = ®istry.BatchRequestPlugin{ 103 | FullName: pluginInfo.ShortNormalizedName, 104 | VersionConstraint: versionConstraint, 105 | } 106 | } 107 | batchResponse, err := r.client.SendBatchRequest(context.Background(), batchRequest) 108 | if err != nil { 109 | return nil, err 110 | } 111 | 112 | return &resolver.BatchPluginDownloadInfo{ 113 | URL: batchResponse.DownloadURL, 114 | Checksum: batchResponse.DownloadChecksum, 115 | }, nil 116 | } 117 | 118 | func (r *Resolver) Names() []string { 119 | return []string{"registry", "registry-v2"} 120 | } 121 | -------------------------------------------------------------------------------- /pkg/plugin/discovery/resolver/resolver.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import "github.com/go-semantic-release/semantic-release/v2/pkg/plugin" 4 | 5 | type PluginDownloadInfo struct { 6 | URL string 7 | Checksum string 8 | FileName string 9 | Version string 10 | } 11 | 12 | type BatchPluginDownloadInfo struct { 13 | URL string 14 | Checksum string 15 | } 16 | 17 | type Resolver interface { 18 | ResolvePlugin(*plugin.Info) (*PluginDownloadInfo, error) 19 | Names() []string 20 | } 21 | 22 | type BatchResolver interface { 23 | // BatchResolvePlugins resolves a list of plugins and returns a single URL to download all plugins as tgz archive. 24 | BatchResolvePlugins([]*plugin.Info) (*BatchPluginDownloadInfo, error) 25 | } 26 | -------------------------------------------------------------------------------- /pkg/plugin/grpc.go: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "github.com/go-semantic-release/semantic-release/v2/pkg/analyzer" 8 | "github.com/go-semantic-release/semantic-release/v2/pkg/condition" 9 | "github.com/go-semantic-release/semantic-release/v2/pkg/generator" 10 | "github.com/go-semantic-release/semantic-release/v2/pkg/hooks" 11 | "github.com/go-semantic-release/semantic-release/v2/pkg/provider" 12 | "github.com/go-semantic-release/semantic-release/v2/pkg/updater" 13 | "github.com/hashicorp/go-plugin" 14 | "google.golang.org/grpc" 15 | ) 16 | 17 | type GRPCWrapper struct { 18 | Type string 19 | Impl interface{} 20 | plugin.NetRPCUnsupportedPlugin 21 | } 22 | 23 | func (p *GRPCWrapper) GRPCServer(_ *plugin.GRPCBroker, s *grpc.Server) error { 24 | switch p.Type { 25 | case analyzer.CommitAnalyzerPluginName: 26 | analyzer.RegisterCommitAnalyzerPluginServer(s, &analyzer.CommitAnalyzerServer{ 27 | Impl: p.Impl.(analyzer.CommitAnalyzer), 28 | }) 29 | case condition.CIConditionPluginName: 30 | condition.RegisterCIConditionPluginServer(s, &condition.CIConditionServer{ 31 | Impl: p.Impl.(condition.CICondition), 32 | }) 33 | case generator.ChangelogGeneratorPluginName: 34 | generator.RegisterChangelogGeneratorPluginServer(s, &generator.ChangelogGeneratorServer{ 35 | Impl: p.Impl.(generator.ChangelogGenerator), 36 | }) 37 | case provider.PluginName: 38 | provider.RegisterProviderPluginServer(s, &provider.Server{ 39 | Impl: p.Impl.(provider.Provider), 40 | }) 41 | case updater.FilesUpdaterPluginName: 42 | updater.RegisterFilesUpdaterPluginServer(s, &updater.FilesUpdaterServer{ 43 | Impl: p.Impl.(updater.FilesUpdater), 44 | }) 45 | case hooks.PluginName: 46 | hooks.RegisterHooksPluginServer(s, &hooks.Server{ 47 | Impl: p.Impl.(hooks.Hooks), 48 | }) 49 | default: 50 | return errors.New("unknown plugin type") 51 | } 52 | return nil 53 | } 54 | 55 | func (p *GRPCWrapper) GRPCClient(_ context.Context, _ *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) { 56 | switch p.Type { 57 | case analyzer.CommitAnalyzerPluginName: 58 | return &analyzer.CommitAnalyzerClient{ 59 | Impl: analyzer.NewCommitAnalyzerPluginClient(c), 60 | }, nil 61 | case condition.CIConditionPluginName: 62 | return &condition.CIConditionClient{ 63 | Impl: condition.NewCIConditionPluginClient(c), 64 | }, nil 65 | case generator.ChangelogGeneratorPluginName: 66 | return &generator.ChangelogGeneratorClient{ 67 | Impl: generator.NewChangelogGeneratorPluginClient(c), 68 | }, nil 69 | case provider.PluginName: 70 | return &provider.Client{ 71 | Impl: provider.NewProviderPluginClient(c), 72 | }, nil 73 | case updater.FilesUpdaterPluginName: 74 | return &updater.FilesUpdaterClient{ 75 | Impl: updater.NewFilesUpdaterPluginClient(c), 76 | }, nil 77 | case hooks.PluginName: 78 | return &hooks.Client{ 79 | Impl: hooks.NewHooksPluginClient(c), 80 | }, nil 81 | } 82 | return nil, errors.New("unknown plugin type") 83 | } 84 | -------------------------------------------------------------------------------- /pkg/plugin/manager/manager.go: -------------------------------------------------------------------------------- 1 | package manager 2 | 3 | import ( 4 | "github.com/go-semantic-release/semantic-release/v2/pkg/analyzer" 5 | "github.com/go-semantic-release/semantic-release/v2/pkg/condition" 6 | "github.com/go-semantic-release/semantic-release/v2/pkg/config" 7 | "github.com/go-semantic-release/semantic-release/v2/pkg/generator" 8 | "github.com/go-semantic-release/semantic-release/v2/pkg/hooks" 9 | "github.com/go-semantic-release/semantic-release/v2/pkg/plugin" 10 | "github.com/go-semantic-release/semantic-release/v2/pkg/plugin/discovery" 11 | "github.com/go-semantic-release/semantic-release/v2/pkg/provider" 12 | "github.com/go-semantic-release/semantic-release/v2/pkg/updater" 13 | ) 14 | 15 | type PluginManager struct { 16 | config *config.Config 17 | discovery *discovery.Discovery 18 | } 19 | 20 | func New(config *config.Config) (*PluginManager, error) { 21 | dis, err := discovery.New(config) 22 | if err != nil { 23 | return nil, err 24 | } 25 | return &PluginManager{ 26 | config: config, 27 | discovery: dis, 28 | }, nil 29 | } 30 | 31 | func (m *PluginManager) GetCICondition() (condition.CICondition, error) { 32 | pluginInfo, err := m.discovery.FindPlugin(condition.CIConditionPluginName, m.config.CiConditionPlugin) 33 | if err != nil { 34 | return nil, err 35 | } 36 | 37 | cic, err := plugin.StartPlugin(pluginInfo) 38 | if err != nil { 39 | return nil, err 40 | } 41 | return cic.(condition.CICondition), nil 42 | } 43 | 44 | func (m *PluginManager) GetProvider() (provider.Provider, error) { 45 | pluginInfo, err := m.discovery.FindPlugin(provider.PluginName, m.config.ProviderPlugin) 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | prov, err := plugin.StartPlugin(pluginInfo) 51 | if err != nil { 52 | return nil, err 53 | } 54 | return prov.(provider.Provider), nil 55 | } 56 | 57 | func (m *PluginManager) GetCommitAnalyzer() (analyzer.CommitAnalyzer, error) { 58 | pluginInfo, err := m.discovery.FindPlugin(analyzer.CommitAnalyzerPluginName, m.config.CommitAnalyzerPlugin) 59 | if err != nil { 60 | return nil, err 61 | } 62 | 63 | ca, err := plugin.StartPlugin(pluginInfo) 64 | if err != nil { 65 | return nil, err 66 | } 67 | return ca.(analyzer.CommitAnalyzer), nil 68 | } 69 | 70 | func (m *PluginManager) GetChangelogGenerator() (generator.ChangelogGenerator, error) { 71 | pluginInfo, err := m.discovery.FindPlugin(generator.ChangelogGeneratorPluginName, m.config.ChangelogGeneratorPlugin) 72 | if err != nil { 73 | return nil, err 74 | } 75 | 76 | cg, err := plugin.StartPlugin(pluginInfo) 77 | if err != nil { 78 | return nil, err 79 | } 80 | return cg.(generator.ChangelogGenerator), nil 81 | } 82 | 83 | func (m *PluginManager) GetChainedUpdater() (*updater.ChainedUpdater, error) { 84 | updaters := make([]updater.FilesUpdater, 0) 85 | for _, pl := range m.config.FilesUpdaterPlugins { 86 | pluginInfo, err := m.discovery.FindPlugin(updater.FilesUpdaterPluginName, pl) 87 | if err != nil { 88 | return nil, err 89 | } 90 | 91 | upd, err := plugin.StartPlugin(pluginInfo) 92 | if err != nil { 93 | return nil, err 94 | } 95 | updaters = append(updaters, upd.(updater.FilesUpdater)) 96 | } 97 | 98 | return &updater.ChainedUpdater{ 99 | Updaters: updaters, 100 | }, nil 101 | } 102 | 103 | func (m *PluginManager) GetChainedHooksExecutor() (*hooks.ChainedHooksExecutor, error) { 104 | hooksChain := make([]hooks.Hooks, 0) 105 | for _, pl := range m.config.HooksPlugins { 106 | pluginInfo, err := m.discovery.FindPlugin(hooks.PluginName, pl) 107 | if err != nil { 108 | return nil, err 109 | } 110 | 111 | hp, err := plugin.StartPlugin(pluginInfo) 112 | if err != nil { 113 | return nil, err 114 | } 115 | hooksChain = append(hooksChain, hp.(hooks.Hooks)) 116 | } 117 | 118 | return &hooks.ChainedHooksExecutor{ 119 | HooksChain: hooksChain, 120 | }, nil 121 | } 122 | 123 | func (m *PluginManager) Stop() { 124 | plugin.KillAllPlugins() 125 | } 126 | 127 | func (m *PluginManager) getAllPlugins() [][]string { 128 | plugins := make([][]string, 0, 4) 129 | // required plugins 130 | plugins = append(plugins, []string{condition.CIConditionPluginName, m.config.CiConditionPlugin}) 131 | plugins = append(plugins, []string{provider.PluginName, m.config.ProviderPlugin}) 132 | plugins = append(plugins, []string{analyzer.CommitAnalyzerPluginName, m.config.CommitAnalyzerPlugin}) 133 | plugins = append(plugins, []string{generator.ChangelogGeneratorPluginName, m.config.ChangelogGeneratorPlugin}) 134 | 135 | // optional plugins 136 | for _, pl := range m.config.FilesUpdaterPlugins { 137 | plugins = append(plugins, []string{updater.FilesUpdaterPluginName, pl}) 138 | } 139 | for _, pl := range m.config.HooksPlugins { 140 | plugins = append(plugins, []string{hooks.PluginName, pl}) 141 | } 142 | return plugins 143 | } 144 | 145 | func (m *PluginManager) getAllPluginInfos() ([]*plugin.Info, error) { 146 | infos := make([]*plugin.Info, 0, 4) 147 | for _, pl := range m.getAllPlugins() { 148 | pInfo, err := plugin.GetPluginInfo(pl[0], pl[1]) 149 | if err != nil { 150 | return nil, err 151 | } 152 | infos = append(infos, pInfo) 153 | } 154 | return infos, nil 155 | } 156 | 157 | func (m *PluginManager) checkIfSameResolvers(infos []*plugin.Info) (string, bool) { 158 | resolver := "" 159 | for _, info := range infos { 160 | if resolver == "" { 161 | resolver = info.Resolver 162 | } else if resolver != info.Resolver { 163 | return "", false 164 | } 165 | } 166 | return resolver, true 167 | } 168 | 169 | func (m *PluginManager) PrefetchAllPluginsIfBatchIsPossible() (bool, []*plugin.Info, error) { 170 | pInfos, err := m.getAllPluginInfos() 171 | if err != nil { 172 | return false, nil, err 173 | } 174 | 175 | if m.config.PluginResolverDisableBatchPrefetch { 176 | return false, pInfos, nil 177 | } 178 | 179 | if resolver, ok := m.checkIfSameResolvers(pInfos); ok && m.discovery.IsBatchResolver(resolver) { 180 | // all plugins have the same resolver, and it supports batch resolving 181 | bErr := m.discovery.FindPluginsWithBatchResolver(resolver, pInfos) 182 | if bErr != nil { 183 | return false, pInfos, bErr 184 | } 185 | return true, pInfos, nil 186 | } 187 | return false, pInfos, nil 188 | } 189 | 190 | func (m *PluginManager) FetchAllPlugins() error { 191 | batchWasPossible, pInfos, err := m.PrefetchAllPluginsIfBatchIsPossible() 192 | if err != nil && pInfos == nil { 193 | return err 194 | } 195 | if batchWasPossible { 196 | return nil 197 | } 198 | 199 | // try to find plugins one by one 200 | for _, pInfo := range pInfos { 201 | fErr := m.discovery.FindPluginByPluginInfo(pInfo) 202 | if fErr != nil { 203 | return fErr 204 | } 205 | } 206 | return nil 207 | } 208 | -------------------------------------------------------------------------------- /pkg/plugin/plugin.go: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/Masterminds/semver/v3" 8 | "github.com/go-semantic-release/semantic-release/v2/pkg/analyzer" 9 | "github.com/go-semantic-release/semantic-release/v2/pkg/condition" 10 | "github.com/go-semantic-release/semantic-release/v2/pkg/generator" 11 | "github.com/go-semantic-release/semantic-release/v2/pkg/hooks" 12 | "github.com/go-semantic-release/semantic-release/v2/pkg/provider" 13 | "github.com/go-semantic-release/semantic-release/v2/pkg/updater" 14 | ) 15 | 16 | type Info struct { 17 | Type string 18 | Name string 19 | NormalizedName string 20 | ShortNormalizedName string 21 | Constraint *semver.Constraints 22 | Resolver string 23 | RepoSlug string 24 | PluginPath string 25 | BinPath string 26 | } 27 | 28 | func normalizedPluginType(t string) string { 29 | switch t { 30 | case analyzer.CommitAnalyzerPluginName: 31 | return "commit-analyzer" 32 | case condition.CIConditionPluginName: 33 | return "condition" 34 | case generator.ChangelogGeneratorPluginName: 35 | return "changelog-generator" 36 | case provider.PluginName: 37 | return "provider" 38 | case updater.FilesUpdaterPluginName: 39 | return "files-updater" 40 | case hooks.PluginName: 41 | return "hooks" 42 | } 43 | return "" 44 | } 45 | 46 | var nameNormalizer = strings.NewReplacer(":", "-", "/", "-") 47 | 48 | func GetPluginInfo(pluginType, pluginName string) (*Info, error) { 49 | nPluginType := normalizedPluginType(pluginType) 50 | if nPluginType == "" { 51 | return nil, fmt.Errorf("invalid plugin type: %s", pluginType) 52 | } 53 | resolver := "default" 54 | repoSlug := "" 55 | name := pluginName 56 | normalizedName := nameNormalizer.Replace(fmt.Sprintf("%s-%s", nPluginType, pluginName)) 57 | 58 | if strings.Contains(name, ":") { 59 | parts := strings.Split(name, ":") 60 | if len(parts) != 2 { 61 | return nil, fmt.Errorf("invalid plugin name format") 62 | } 63 | resolver = parts[0] 64 | name = parts[1] 65 | } 66 | 67 | if strings.Contains(name, "/") { 68 | slashParts := strings.Split(name, "/") 69 | pName := slashParts[len(slashParts)-1] 70 | // remove version constraint from the slug 71 | if strings.Contains(pName, "@") { 72 | nameWithoutVersion, _, _ := strings.Cut(pName, "@") 73 | repoSlug = strings.Join(slashParts[:len(slashParts)-1], "/") + "/" + nameWithoutVersion 74 | } else { 75 | repoSlug = name 76 | } 77 | // the last part is the plugin name 78 | name = pName 79 | } 80 | 81 | var constraint *semver.Constraints 82 | if strings.Contains(name, "@") { 83 | parts := strings.Split(name, "@") 84 | if len(parts) != 2 { 85 | return nil, fmt.Errorf("invalid plugin name format") 86 | } 87 | v, err := semver.NewConstraint(parts[1]) 88 | if err != nil { 89 | return nil, err 90 | } 91 | name = parts[0] 92 | constraint = v 93 | 94 | // remove version constraint from the normalized name 95 | normalizedParts := strings.Split(normalizedName, "@") 96 | normalizedName = strings.Join(normalizedParts[:len(normalizedParts)-1], "@") 97 | } 98 | 99 | return &Info{ 100 | Type: pluginType, 101 | Name: name, 102 | NormalizedName: normalizedName, 103 | ShortNormalizedName: fmt.Sprintf("%s-%s", nPluginType, name), 104 | Constraint: constraint, 105 | Resolver: resolver, 106 | RepoSlug: repoSlug, 107 | }, nil 108 | } 109 | -------------------------------------------------------------------------------- /pkg/plugin/plugin_test.go: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/Masterminds/semver/v3" 7 | 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func parseConstraint(c string) *semver.Constraints { 12 | constraint, _ := semver.NewConstraint(c) 13 | return constraint 14 | } 15 | 16 | func TestGetPluginInfo(t *testing.T) { 17 | testCases := []struct { 18 | t string 19 | input string 20 | results *Info 21 | }{ 22 | {"provider", "git", &Info{ 23 | Type: "provider", 24 | Name: "git", 25 | NormalizedName: "provider-git", 26 | ShortNormalizedName: "provider-git", 27 | Resolver: "default", 28 | }}, 29 | {"provider", "github:myorg/myplugin", &Info{ 30 | Type: "provider", 31 | Name: "myplugin", 32 | NormalizedName: "provider-github-myorg-myplugin", 33 | ShortNormalizedName: "provider-myplugin", 34 | Resolver: "github", 35 | RepoSlug: "myorg/myplugin", 36 | }}, 37 | {"ci_condition", "github:myorg/myplugin", &Info{ 38 | Type: "ci_condition", 39 | Name: "myplugin", 40 | NormalizedName: "condition-github-myorg-myplugin", 41 | ShortNormalizedName: "condition-myplugin", 42 | Resolver: "github", 43 | RepoSlug: "myorg/myplugin", 44 | }}, 45 | {"provider", "github:myorg/myplugin@^1.0.0", &Info{ 46 | Type: "provider", 47 | Name: "myplugin", 48 | NormalizedName: "provider-github-myorg-myplugin", 49 | ShortNormalizedName: "provider-myplugin", 50 | Resolver: "github", 51 | RepoSlug: "myorg/myplugin", 52 | Constraint: parseConstraint("^1.0.0"), 53 | }}, 54 | {"provider", "git@2.0.0", &Info{ 55 | Type: "provider", 56 | Name: "git", 57 | NormalizedName: "provider-git", 58 | ShortNormalizedName: "provider-git", 59 | Resolver: "default", 60 | Constraint: parseConstraint("2.0.0"), 61 | }}, 62 | {"hooks", "registry:logger", &Info{ 63 | Type: "hooks", 64 | Name: "logger", 65 | NormalizedName: "hooks-registry-logger", 66 | ShortNormalizedName: "hooks-logger", 67 | Resolver: "registry", 68 | }}, 69 | {"hooks", "myresolver:@myorg/myplugin", &Info{ 70 | Type: "hooks", 71 | Name: "myplugin", 72 | NormalizedName: "hooks-myresolver-@myorg-myplugin", 73 | ShortNormalizedName: "hooks-myplugin", 74 | Resolver: "myresolver", 75 | RepoSlug: "@myorg/myplugin", 76 | }}, 77 | {"hooks", "myresolver:@myorg/myplugin@1.2.3", &Info{ 78 | Type: "hooks", 79 | Name: "myplugin", 80 | NormalizedName: "hooks-myresolver-@myorg-myplugin", 81 | ShortNormalizedName: "hooks-myplugin", 82 | Resolver: "myresolver", 83 | RepoSlug: "@myorg/myplugin", 84 | Constraint: parseConstraint("1.2.3"), 85 | }}, 86 | } 87 | for _, testCase := range testCases { 88 | results, err := GetPluginInfo(testCase.t, testCase.input) 89 | require.NoError(t, err) 90 | require.Equal(t, testCase.results, results) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /pkg/plugin/serve.go: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import ( 4 | "github.com/go-semantic-release/semantic-release/v2/pkg/analyzer" 5 | "github.com/go-semantic-release/semantic-release/v2/pkg/condition" 6 | "github.com/go-semantic-release/semantic-release/v2/pkg/generator" 7 | "github.com/go-semantic-release/semantic-release/v2/pkg/hooks" 8 | "github.com/go-semantic-release/semantic-release/v2/pkg/provider" 9 | "github.com/go-semantic-release/semantic-release/v2/pkg/updater" 10 | "github.com/hashicorp/go-plugin" 11 | ) 12 | 13 | var Handshake = plugin.HandshakeConfig{ 14 | MagicCookieKey: "GO_SEMANTIC_RELEASE_MAGIC_COOKIE", 15 | MagicCookieValue: "beepboop", 16 | } 17 | 18 | type ( 19 | CommitAnalyzerFunc func() analyzer.CommitAnalyzer 20 | CIConditionFunc func() condition.CICondition 21 | ChangelogGeneratorFunc func() generator.ChangelogGenerator 22 | ProviderFunc func() provider.Provider 23 | FilesUpdaterFunc func() updater.FilesUpdater 24 | HooksFunc func() hooks.Hooks 25 | ) 26 | 27 | type ServeOpts struct { 28 | CommitAnalyzer CommitAnalyzerFunc 29 | CICondition CIConditionFunc 30 | ChangelogGenerator ChangelogGeneratorFunc 31 | Provider ProviderFunc 32 | FilesUpdater FilesUpdaterFunc 33 | Hooks HooksFunc 34 | } 35 | 36 | func Serve(opts *ServeOpts) { 37 | pluginSet := make(plugin.PluginSet) 38 | 39 | if opts.CommitAnalyzer != nil { 40 | pluginSet[analyzer.CommitAnalyzerPluginName] = &GRPCWrapper{ 41 | Type: analyzer.CommitAnalyzerPluginName, 42 | Impl: opts.CommitAnalyzer(), 43 | } 44 | } 45 | 46 | if opts.CICondition != nil { 47 | pluginSet[condition.CIConditionPluginName] = &GRPCWrapper{ 48 | Type: condition.CIConditionPluginName, 49 | Impl: opts.CICondition(), 50 | } 51 | } 52 | 53 | if opts.ChangelogGenerator != nil { 54 | pluginSet[generator.ChangelogGeneratorPluginName] = &GRPCWrapper{ 55 | Type: generator.ChangelogGeneratorPluginName, 56 | Impl: opts.ChangelogGenerator(), 57 | } 58 | } 59 | 60 | if opts.Provider != nil { 61 | pluginSet[provider.PluginName] = &GRPCWrapper{ 62 | Type: provider.PluginName, 63 | Impl: opts.Provider(), 64 | } 65 | } 66 | 67 | if opts.FilesUpdater != nil { 68 | pluginSet[updater.FilesUpdaterPluginName] = &GRPCWrapper{ 69 | Type: updater.FilesUpdaterPluginName, 70 | Impl: opts.FilesUpdater(), 71 | } 72 | } 73 | 74 | if opts.Hooks != nil { 75 | pluginSet[hooks.PluginName] = &GRPCWrapper{ 76 | Type: hooks.PluginName, 77 | Impl: opts.Hooks(), 78 | } 79 | } 80 | 81 | plugin.Serve(&plugin.ServeConfig{ 82 | HandshakeConfig: Handshake, 83 | VersionedPlugins: map[int]plugin.PluginSet{ 84 | 1: pluginSet, 85 | }, 86 | GRPCServer: plugin.DefaultGRPCServer, 87 | }) 88 | } 89 | -------------------------------------------------------------------------------- /pkg/plugin/sysprocattr.go: -------------------------------------------------------------------------------- 1 | //go:build !linux 2 | // +build !linux 3 | 4 | package plugin 5 | 6 | import "syscall" 7 | 8 | func GetSysProcAttr() *syscall.SysProcAttr { 9 | return nil 10 | } 11 | -------------------------------------------------------------------------------- /pkg/plugin/sysprocattr_linux.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | // +build linux 3 | 4 | package plugin 5 | 6 | import "syscall" 7 | 8 | func GetSysProcAttr() *syscall.SysProcAttr { 9 | return &syscall.SysProcAttr{ 10 | Pdeathsig: syscall.SIGKILL, 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /pkg/provider/provider.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "github.com/go-semantic-release/semantic-release/v2/pkg/semrel" 5 | ) 6 | 7 | type Provider interface { 8 | Init(map[string]string) error 9 | Name() string 10 | Version() string 11 | GetInfo() (*RepositoryInfo, error) 12 | GetCommits(fromSha, toSha string) ([]*semrel.RawCommit, error) 13 | GetReleases(re string) ([]*semrel.Release, error) 14 | CreateRelease(*CreateReleaseConfig) error 15 | } 16 | -------------------------------------------------------------------------------- /pkg/provider/provider.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option go_package = "github.com/go-semantic-release/semantic-release/v2/pkg/provider"; 3 | 4 | import "pkg/semrel/structs.proto"; 5 | 6 | message RepositoryInfo { 7 | string owner = 1; 8 | string repo = 2; 9 | string default_branch = 3; 10 | bool private = 4; 11 | } 12 | 13 | message CreateReleaseConfig { 14 | string changelog = 1; 15 | string new_version = 2; 16 | bool prerelease = 3; 17 | string branch = 4; 18 | string SHA = 5; 19 | } 20 | 21 | message ProviderInit { 22 | message Request { 23 | map config = 1; 24 | } 25 | message Response { 26 | string error = 1; 27 | } 28 | } 29 | 30 | message ProviderName { 31 | message Request {} 32 | message Response { 33 | string name = 1; 34 | } 35 | } 36 | 37 | message ProviderVersion { 38 | message Request {} 39 | message Response { 40 | string version = 1; 41 | } 42 | } 43 | 44 | message GetInfo { 45 | message Request {} 46 | message Response { 47 | RepositoryInfo info = 1; 48 | string error = 2; 49 | } 50 | } 51 | 52 | message GetCommits { 53 | message Request { 54 | string to_sha = 1; 55 | string from_sha = 2; 56 | } 57 | message Response { 58 | repeated RawCommit raw_commits = 1; 59 | string error = 2; 60 | } 61 | } 62 | 63 | message GetReleases { 64 | message Request { 65 | string regexp = 1; 66 | } 67 | message Response { 68 | repeated Release releases = 1; 69 | string error = 2; 70 | } 71 | } 72 | 73 | message CreateRelease { 74 | message Request { 75 | CreateReleaseConfig config = 1; 76 | } 77 | message Response { 78 | string error = 1; 79 | } 80 | } 81 | 82 | service ProviderPlugin { 83 | rpc Init(ProviderInit.Request) returns (ProviderInit.Response); 84 | rpc Name(ProviderName.Request) returns (ProviderName.Response); 85 | rpc Version(ProviderVersion.Request) returns (ProviderVersion.Response); 86 | rpc GetInfo(GetInfo.Request) returns (GetInfo.Response); 87 | rpc GetCommits(GetCommits.Request) returns (GetCommits.Response); 88 | rpc GetReleases(GetReleases.Request) returns (GetReleases.Response); 89 | rpc CreateRelease(CreateRelease.Request) returns (CreateRelease.Response); 90 | } 91 | -------------------------------------------------------------------------------- /pkg/provider/provider_grpc.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT. 2 | // versions: 3 | // - protoc-gen-go-grpc v1.3.0 4 | // - protoc v4.24.4 5 | // source: pkg/provider/provider.proto 6 | 7 | package provider 8 | 9 | import ( 10 | context "context" 11 | grpc "google.golang.org/grpc" 12 | codes "google.golang.org/grpc/codes" 13 | status "google.golang.org/grpc/status" 14 | ) 15 | 16 | // This is a compile-time assertion to ensure that this generated file 17 | // is compatible with the grpc package it is being compiled against. 18 | // Requires gRPC-Go v1.32.0 or later. 19 | const _ = grpc.SupportPackageIsVersion7 20 | 21 | const ( 22 | ProviderPlugin_Init_FullMethodName = "/ProviderPlugin/Init" 23 | ProviderPlugin_Name_FullMethodName = "/ProviderPlugin/Name" 24 | ProviderPlugin_Version_FullMethodName = "/ProviderPlugin/Version" 25 | ProviderPlugin_GetInfo_FullMethodName = "/ProviderPlugin/GetInfo" 26 | ProviderPlugin_GetCommits_FullMethodName = "/ProviderPlugin/GetCommits" 27 | ProviderPlugin_GetReleases_FullMethodName = "/ProviderPlugin/GetReleases" 28 | ProviderPlugin_CreateRelease_FullMethodName = "/ProviderPlugin/CreateRelease" 29 | ) 30 | 31 | // ProviderPluginClient is the client API for ProviderPlugin service. 32 | // 33 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. 34 | type ProviderPluginClient interface { 35 | Init(ctx context.Context, in *ProviderInit_Request, opts ...grpc.CallOption) (*ProviderInit_Response, error) 36 | Name(ctx context.Context, in *ProviderName_Request, opts ...grpc.CallOption) (*ProviderName_Response, error) 37 | Version(ctx context.Context, in *ProviderVersion_Request, opts ...grpc.CallOption) (*ProviderVersion_Response, error) 38 | GetInfo(ctx context.Context, in *GetInfo_Request, opts ...grpc.CallOption) (*GetInfo_Response, error) 39 | GetCommits(ctx context.Context, in *GetCommits_Request, opts ...grpc.CallOption) (*GetCommits_Response, error) 40 | GetReleases(ctx context.Context, in *GetReleases_Request, opts ...grpc.CallOption) (*GetReleases_Response, error) 41 | CreateRelease(ctx context.Context, in *CreateRelease_Request, opts ...grpc.CallOption) (*CreateRelease_Response, error) 42 | } 43 | 44 | type providerPluginClient struct { 45 | cc grpc.ClientConnInterface 46 | } 47 | 48 | func NewProviderPluginClient(cc grpc.ClientConnInterface) ProviderPluginClient { 49 | return &providerPluginClient{cc} 50 | } 51 | 52 | func (c *providerPluginClient) Init(ctx context.Context, in *ProviderInit_Request, opts ...grpc.CallOption) (*ProviderInit_Response, error) { 53 | out := new(ProviderInit_Response) 54 | err := c.cc.Invoke(ctx, ProviderPlugin_Init_FullMethodName, in, out, opts...) 55 | if err != nil { 56 | return nil, err 57 | } 58 | return out, nil 59 | } 60 | 61 | func (c *providerPluginClient) Name(ctx context.Context, in *ProviderName_Request, opts ...grpc.CallOption) (*ProviderName_Response, error) { 62 | out := new(ProviderName_Response) 63 | err := c.cc.Invoke(ctx, ProviderPlugin_Name_FullMethodName, in, out, opts...) 64 | if err != nil { 65 | return nil, err 66 | } 67 | return out, nil 68 | } 69 | 70 | func (c *providerPluginClient) Version(ctx context.Context, in *ProviderVersion_Request, opts ...grpc.CallOption) (*ProviderVersion_Response, error) { 71 | out := new(ProviderVersion_Response) 72 | err := c.cc.Invoke(ctx, ProviderPlugin_Version_FullMethodName, in, out, opts...) 73 | if err != nil { 74 | return nil, err 75 | } 76 | return out, nil 77 | } 78 | 79 | func (c *providerPluginClient) GetInfo(ctx context.Context, in *GetInfo_Request, opts ...grpc.CallOption) (*GetInfo_Response, error) { 80 | out := new(GetInfo_Response) 81 | err := c.cc.Invoke(ctx, ProviderPlugin_GetInfo_FullMethodName, in, out, opts...) 82 | if err != nil { 83 | return nil, err 84 | } 85 | return out, nil 86 | } 87 | 88 | func (c *providerPluginClient) GetCommits(ctx context.Context, in *GetCommits_Request, opts ...grpc.CallOption) (*GetCommits_Response, error) { 89 | out := new(GetCommits_Response) 90 | err := c.cc.Invoke(ctx, ProviderPlugin_GetCommits_FullMethodName, in, out, opts...) 91 | if err != nil { 92 | return nil, err 93 | } 94 | return out, nil 95 | } 96 | 97 | func (c *providerPluginClient) GetReleases(ctx context.Context, in *GetReleases_Request, opts ...grpc.CallOption) (*GetReleases_Response, error) { 98 | out := new(GetReleases_Response) 99 | err := c.cc.Invoke(ctx, ProviderPlugin_GetReleases_FullMethodName, in, out, opts...) 100 | if err != nil { 101 | return nil, err 102 | } 103 | return out, nil 104 | } 105 | 106 | func (c *providerPluginClient) CreateRelease(ctx context.Context, in *CreateRelease_Request, opts ...grpc.CallOption) (*CreateRelease_Response, error) { 107 | out := new(CreateRelease_Response) 108 | err := c.cc.Invoke(ctx, ProviderPlugin_CreateRelease_FullMethodName, in, out, opts...) 109 | if err != nil { 110 | return nil, err 111 | } 112 | return out, nil 113 | } 114 | 115 | // ProviderPluginServer is the server API for ProviderPlugin service. 116 | // All implementations must embed UnimplementedProviderPluginServer 117 | // for forward compatibility 118 | type ProviderPluginServer interface { 119 | Init(context.Context, *ProviderInit_Request) (*ProviderInit_Response, error) 120 | Name(context.Context, *ProviderName_Request) (*ProviderName_Response, error) 121 | Version(context.Context, *ProviderVersion_Request) (*ProviderVersion_Response, error) 122 | GetInfo(context.Context, *GetInfo_Request) (*GetInfo_Response, error) 123 | GetCommits(context.Context, *GetCommits_Request) (*GetCommits_Response, error) 124 | GetReleases(context.Context, *GetReleases_Request) (*GetReleases_Response, error) 125 | CreateRelease(context.Context, *CreateRelease_Request) (*CreateRelease_Response, error) 126 | mustEmbedUnimplementedProviderPluginServer() 127 | } 128 | 129 | // UnimplementedProviderPluginServer must be embedded to have forward compatible implementations. 130 | type UnimplementedProviderPluginServer struct { 131 | } 132 | 133 | func (UnimplementedProviderPluginServer) Init(context.Context, *ProviderInit_Request) (*ProviderInit_Response, error) { 134 | return nil, status.Errorf(codes.Unimplemented, "method Init not implemented") 135 | } 136 | func (UnimplementedProviderPluginServer) Name(context.Context, *ProviderName_Request) (*ProviderName_Response, error) { 137 | return nil, status.Errorf(codes.Unimplemented, "method Name not implemented") 138 | } 139 | func (UnimplementedProviderPluginServer) Version(context.Context, *ProviderVersion_Request) (*ProviderVersion_Response, error) { 140 | return nil, status.Errorf(codes.Unimplemented, "method Version not implemented") 141 | } 142 | func (UnimplementedProviderPluginServer) GetInfo(context.Context, *GetInfo_Request) (*GetInfo_Response, error) { 143 | return nil, status.Errorf(codes.Unimplemented, "method GetInfo not implemented") 144 | } 145 | func (UnimplementedProviderPluginServer) GetCommits(context.Context, *GetCommits_Request) (*GetCommits_Response, error) { 146 | return nil, status.Errorf(codes.Unimplemented, "method GetCommits not implemented") 147 | } 148 | func (UnimplementedProviderPluginServer) GetReleases(context.Context, *GetReleases_Request) (*GetReleases_Response, error) { 149 | return nil, status.Errorf(codes.Unimplemented, "method GetReleases not implemented") 150 | } 151 | func (UnimplementedProviderPluginServer) CreateRelease(context.Context, *CreateRelease_Request) (*CreateRelease_Response, error) { 152 | return nil, status.Errorf(codes.Unimplemented, "method CreateRelease not implemented") 153 | } 154 | func (UnimplementedProviderPluginServer) mustEmbedUnimplementedProviderPluginServer() {} 155 | 156 | // UnsafeProviderPluginServer may be embedded to opt out of forward compatibility for this service. 157 | // Use of this interface is not recommended, as added methods to ProviderPluginServer will 158 | // result in compilation errors. 159 | type UnsafeProviderPluginServer interface { 160 | mustEmbedUnimplementedProviderPluginServer() 161 | } 162 | 163 | func RegisterProviderPluginServer(s grpc.ServiceRegistrar, srv ProviderPluginServer) { 164 | s.RegisterService(&ProviderPlugin_ServiceDesc, srv) 165 | } 166 | 167 | func _ProviderPlugin_Init_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 168 | in := new(ProviderInit_Request) 169 | if err := dec(in); err != nil { 170 | return nil, err 171 | } 172 | if interceptor == nil { 173 | return srv.(ProviderPluginServer).Init(ctx, in) 174 | } 175 | info := &grpc.UnaryServerInfo{ 176 | Server: srv, 177 | FullMethod: ProviderPlugin_Init_FullMethodName, 178 | } 179 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 180 | return srv.(ProviderPluginServer).Init(ctx, req.(*ProviderInit_Request)) 181 | } 182 | return interceptor(ctx, in, info, handler) 183 | } 184 | 185 | func _ProviderPlugin_Name_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 186 | in := new(ProviderName_Request) 187 | if err := dec(in); err != nil { 188 | return nil, err 189 | } 190 | if interceptor == nil { 191 | return srv.(ProviderPluginServer).Name(ctx, in) 192 | } 193 | info := &grpc.UnaryServerInfo{ 194 | Server: srv, 195 | FullMethod: ProviderPlugin_Name_FullMethodName, 196 | } 197 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 198 | return srv.(ProviderPluginServer).Name(ctx, req.(*ProviderName_Request)) 199 | } 200 | return interceptor(ctx, in, info, handler) 201 | } 202 | 203 | func _ProviderPlugin_Version_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 204 | in := new(ProviderVersion_Request) 205 | if err := dec(in); err != nil { 206 | return nil, err 207 | } 208 | if interceptor == nil { 209 | return srv.(ProviderPluginServer).Version(ctx, in) 210 | } 211 | info := &grpc.UnaryServerInfo{ 212 | Server: srv, 213 | FullMethod: ProviderPlugin_Version_FullMethodName, 214 | } 215 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 216 | return srv.(ProviderPluginServer).Version(ctx, req.(*ProviderVersion_Request)) 217 | } 218 | return interceptor(ctx, in, info, handler) 219 | } 220 | 221 | func _ProviderPlugin_GetInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 222 | in := new(GetInfo_Request) 223 | if err := dec(in); err != nil { 224 | return nil, err 225 | } 226 | if interceptor == nil { 227 | return srv.(ProviderPluginServer).GetInfo(ctx, in) 228 | } 229 | info := &grpc.UnaryServerInfo{ 230 | Server: srv, 231 | FullMethod: ProviderPlugin_GetInfo_FullMethodName, 232 | } 233 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 234 | return srv.(ProviderPluginServer).GetInfo(ctx, req.(*GetInfo_Request)) 235 | } 236 | return interceptor(ctx, in, info, handler) 237 | } 238 | 239 | func _ProviderPlugin_GetCommits_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 240 | in := new(GetCommits_Request) 241 | if err := dec(in); err != nil { 242 | return nil, err 243 | } 244 | if interceptor == nil { 245 | return srv.(ProviderPluginServer).GetCommits(ctx, in) 246 | } 247 | info := &grpc.UnaryServerInfo{ 248 | Server: srv, 249 | FullMethod: ProviderPlugin_GetCommits_FullMethodName, 250 | } 251 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 252 | return srv.(ProviderPluginServer).GetCommits(ctx, req.(*GetCommits_Request)) 253 | } 254 | return interceptor(ctx, in, info, handler) 255 | } 256 | 257 | func _ProviderPlugin_GetReleases_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 258 | in := new(GetReleases_Request) 259 | if err := dec(in); err != nil { 260 | return nil, err 261 | } 262 | if interceptor == nil { 263 | return srv.(ProviderPluginServer).GetReleases(ctx, in) 264 | } 265 | info := &grpc.UnaryServerInfo{ 266 | Server: srv, 267 | FullMethod: ProviderPlugin_GetReleases_FullMethodName, 268 | } 269 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 270 | return srv.(ProviderPluginServer).GetReleases(ctx, req.(*GetReleases_Request)) 271 | } 272 | return interceptor(ctx, in, info, handler) 273 | } 274 | 275 | func _ProviderPlugin_CreateRelease_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 276 | in := new(CreateRelease_Request) 277 | if err := dec(in); err != nil { 278 | return nil, err 279 | } 280 | if interceptor == nil { 281 | return srv.(ProviderPluginServer).CreateRelease(ctx, in) 282 | } 283 | info := &grpc.UnaryServerInfo{ 284 | Server: srv, 285 | FullMethod: ProviderPlugin_CreateRelease_FullMethodName, 286 | } 287 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 288 | return srv.(ProviderPluginServer).CreateRelease(ctx, req.(*CreateRelease_Request)) 289 | } 290 | return interceptor(ctx, in, info, handler) 291 | } 292 | 293 | // ProviderPlugin_ServiceDesc is the grpc.ServiceDesc for ProviderPlugin service. 294 | // It's only intended for direct use with grpc.RegisterService, 295 | // and not to be introspected or modified (even as a copy) 296 | var ProviderPlugin_ServiceDesc = grpc.ServiceDesc{ 297 | ServiceName: "ProviderPlugin", 298 | HandlerType: (*ProviderPluginServer)(nil), 299 | Methods: []grpc.MethodDesc{ 300 | { 301 | MethodName: "Init", 302 | Handler: _ProviderPlugin_Init_Handler, 303 | }, 304 | { 305 | MethodName: "Name", 306 | Handler: _ProviderPlugin_Name_Handler, 307 | }, 308 | { 309 | MethodName: "Version", 310 | Handler: _ProviderPlugin_Version_Handler, 311 | }, 312 | { 313 | MethodName: "GetInfo", 314 | Handler: _ProviderPlugin_GetInfo_Handler, 315 | }, 316 | { 317 | MethodName: "GetCommits", 318 | Handler: _ProviderPlugin_GetCommits_Handler, 319 | }, 320 | { 321 | MethodName: "GetReleases", 322 | Handler: _ProviderPlugin_GetReleases_Handler, 323 | }, 324 | { 325 | MethodName: "CreateRelease", 326 | Handler: _ProviderPlugin_CreateRelease_Handler, 327 | }, 328 | }, 329 | Streams: []grpc.StreamDesc{}, 330 | Metadata: "pkg/provider/provider.proto", 331 | } 332 | -------------------------------------------------------------------------------- /pkg/provider/wrapper.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "github.com/go-semantic-release/semantic-release/v2/pkg/semrel" 8 | ) 9 | 10 | const PluginName = "provider" 11 | 12 | type Server struct { 13 | Impl Provider 14 | UnimplementedProviderPluginServer 15 | } 16 | 17 | func (s *Server) Init(_ context.Context, request *ProviderInit_Request) (*ProviderInit_Response, error) { 18 | err := s.Impl.Init(request.Config) 19 | res := &ProviderInit_Response{} 20 | if err != nil { 21 | res.Error = err.Error() 22 | } 23 | return res, nil 24 | } 25 | 26 | func (s *Server) Name(_ context.Context, _ *ProviderName_Request) (*ProviderName_Response, error) { 27 | return &ProviderName_Response{Name: s.Impl.Name()}, nil 28 | } 29 | 30 | func (s *Server) Version(_ context.Context, _ *ProviderVersion_Request) (*ProviderVersion_Response, error) { 31 | return &ProviderVersion_Response{Version: s.Impl.Version()}, nil 32 | } 33 | 34 | func (s *Server) GetInfo(_ context.Context, _ *GetInfo_Request) (*GetInfo_Response, error) { 35 | info, err := s.Impl.GetInfo() 36 | if err != nil { 37 | return &GetInfo_Response{Error: err.Error()}, nil 38 | } 39 | return &GetInfo_Response{Info: info}, nil 40 | } 41 | 42 | func (s *Server) GetCommits(_ context.Context, request *GetCommits_Request) (*GetCommits_Response, error) { 43 | commits, err := s.Impl.GetCommits(request.FromSha, request.ToSha) 44 | if err != nil { 45 | return &GetCommits_Response{Error: err.Error()}, nil 46 | } 47 | return &GetCommits_Response{RawCommits: commits}, nil 48 | } 49 | 50 | func (s *Server) GetReleases(_ context.Context, request *GetReleases_Request) (*GetReleases_Response, error) { 51 | releases, err := s.Impl.GetReleases(request.Regexp) 52 | if err != nil { 53 | return &GetReleases_Response{Error: err.Error()}, nil 54 | } 55 | return &GetReleases_Response{Releases: releases}, nil 56 | } 57 | 58 | func (s *Server) CreateRelease(_ context.Context, request *CreateRelease_Request) (*CreateRelease_Response, error) { 59 | err := s.Impl.CreateRelease(request.Config) 60 | res := &CreateRelease_Response{} 61 | if err != nil { 62 | res.Error = err.Error() 63 | } 64 | return res, nil 65 | } 66 | 67 | type Client struct { 68 | Impl ProviderPluginClient 69 | } 70 | 71 | func (c *Client) Init(m map[string]string) error { 72 | res, err := c.Impl.Init(context.Background(), &ProviderInit_Request{ 73 | Config: m, 74 | }) 75 | if err != nil { 76 | return err 77 | } 78 | if res.Error != "" { 79 | return errors.New(res.Error) 80 | } 81 | return nil 82 | } 83 | 84 | func (c *Client) GetInfo() (*RepositoryInfo, error) { 85 | res, err := c.Impl.GetInfo(context.Background(), &GetInfo_Request{}) 86 | if err != nil { 87 | return nil, err 88 | } 89 | if res.Error != "" { 90 | return nil, errors.New(res.Error) 91 | } 92 | return res.Info, nil 93 | } 94 | 95 | func (c *Client) GetCommits(fromSha, toSha string) ([]*semrel.RawCommit, error) { 96 | res, err := c.Impl.GetCommits(context.Background(), &GetCommits_Request{ 97 | FromSha: fromSha, 98 | ToSha: toSha, 99 | }) 100 | if err != nil { 101 | return nil, err 102 | } 103 | if res.Error != "" { 104 | return nil, errors.New(res.Error) 105 | } 106 | return res.RawCommits, nil 107 | } 108 | 109 | func (c *Client) GetReleases(re string) ([]*semrel.Release, error) { 110 | res, err := c.Impl.GetReleases(context.Background(), &GetReleases_Request{ 111 | Regexp: re, 112 | }) 113 | if err != nil { 114 | return nil, err 115 | } 116 | if res.Error != "" { 117 | return nil, errors.New(res.Error) 118 | } 119 | return res.Releases, nil 120 | } 121 | 122 | func (c *Client) CreateRelease(config *CreateReleaseConfig) error { 123 | res, err := c.Impl.CreateRelease(context.Background(), &CreateRelease_Request{ 124 | Config: config, 125 | }) 126 | if err != nil { 127 | return err 128 | } 129 | if res.Error != "" { 130 | return errors.New(res.Error) 131 | } 132 | return nil 133 | } 134 | 135 | func (c *Client) Name() string { 136 | res, err := c.Impl.Name(context.Background(), &ProviderName_Request{}) 137 | if err != nil { 138 | panic(err) 139 | } 140 | return res.Name 141 | } 142 | 143 | func (c *Client) Version() string { 144 | res, err := c.Impl.Version(context.Background(), &ProviderVersion_Request{}) 145 | if err != nil { 146 | panic(err) 147 | } 148 | return res.Version 149 | } 150 | -------------------------------------------------------------------------------- /pkg/semrel/releases.go: -------------------------------------------------------------------------------- 1 | package semrel 2 | 3 | import ( 4 | "sort" 5 | "strings" 6 | 7 | "github.com/Masterminds/semver/v3" 8 | ) 9 | 10 | type releases []*Release 11 | 12 | func (r releases) Len() int { 13 | return len(r) 14 | } 15 | 16 | func (r releases) Less(i, j int) bool { 17 | return semver.MustParse(r[j].Version).LessThan(semver.MustParse(r[i].Version)) 18 | } 19 | 20 | func (r releases) Swap(i, j int) { 21 | r[i], r[j] = r[j], r[i] 22 | } 23 | 24 | func (r releases) GetLatestRelease(vrange string) (*Release, error) { 25 | if len(r) == 0 { 26 | return &Release{SHA: "", Version: "0.0.0"}, nil 27 | } 28 | 29 | sort.Sort(r) 30 | 31 | var lastRelease *Release 32 | for _, r := range r { 33 | if semver.MustParse(r.Version).Prerelease() == "" { 34 | lastRelease = r 35 | break 36 | } 37 | } 38 | 39 | if vrange == "" { 40 | if lastRelease != nil { 41 | return lastRelease, nil 42 | } 43 | return &Release{SHA: "", Version: "0.0.0"}, nil 44 | } 45 | 46 | constraint, err := semver.NewConstraint(vrange) 47 | if err != nil { 48 | return nil, err 49 | } 50 | for _, r := range r { 51 | if constraint.Check(semver.MustParse(r.Version)) { 52 | return r, nil 53 | } 54 | } 55 | 56 | nver, err := semver.NewVersion(vrange) 57 | if err != nil { 58 | return nil, err 59 | } 60 | 61 | splitPre := strings.SplitN(vrange, "-", 2) 62 | if len(splitPre) == 1 { 63 | return &Release{SHA: lastRelease.SHA, Version: nver.String()}, nil 64 | } 65 | 66 | npver, err := nver.SetPrerelease(splitPre[1]) 67 | if err != nil { 68 | return nil, err 69 | } 70 | return &Release{SHA: lastRelease.SHA, Version: npver.String()}, nil 71 | } 72 | 73 | func GetLatestReleaseFromReleases(rawReleases []*Release, vrange string) (*Release, error) { 74 | return releases(rawReleases).GetLatestRelease(vrange) 75 | } 76 | -------------------------------------------------------------------------------- /pkg/semrel/releases_test.go: -------------------------------------------------------------------------------- 1 | package semrel 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestReleases(t *testing.T) { 11 | testCases := []struct { 12 | Releases releases 13 | VRange string 14 | LatestVersion string 15 | }{ 16 | { 17 | releases{}, 18 | "", 19 | "0.0.0", 20 | }, 21 | { 22 | releases{ 23 | {SHA: "a", Version: "0.1.0"}, 24 | }, 25 | "", 26 | "0.1.0", 27 | }, 28 | { 29 | releases{ 30 | {SHA: "a", Version: "1.0.0"}, 31 | {SHA: "b", Version: "1.1.0"}, 32 | {SHA: "c", Version: "2.0.0-beta"}, 33 | {SHA: "d", Version: "0.1.0"}, 34 | }, 35 | "", 36 | "1.1.0", 37 | }, 38 | { 39 | releases{ 40 | {SHA: "a", Version: "1.0.0"}, 41 | {SHA: "b", Version: "1.1.0"}, 42 | {SHA: "c", Version: "2.0.0"}, 43 | {SHA: "c", Version: "2.1.0-beta"}, 44 | {SHA: "d", Version: "0.1.0"}, 45 | }, 46 | "2-beta", 47 | "2.1.0-beta", 48 | }, 49 | { 50 | releases{ 51 | {SHA: "a", Version: "1.0.0"}, 52 | {SHA: "b", Version: "1.1.0"}, 53 | {SHA: "c", Version: "3.0.0-rc.1"}, 54 | {SHA: "c", Version: "3.0.0-rc.2"}, 55 | {SHA: "d", Version: "0.1.0"}, 56 | }, 57 | "3-rc", 58 | "3.0.0-rc.2", 59 | }, 60 | { 61 | releases{ 62 | {SHA: "a", Version: "1.0.0"}, 63 | {SHA: "b", Version: "1.1.0"}, 64 | {SHA: "c", Version: "3.0.0-rc.1"}, 65 | {SHA: "c", Version: "3.0.0-rc.2"}, 66 | {SHA: "d", Version: "0.1.0"}, 67 | }, 68 | "4-alpha", 69 | "4.0.0-alpha", 70 | }, 71 | } 72 | for i, tc := range testCases { 73 | t.Run(fmt.Sprintf("TestReleases: %d, LV: %s", i, tc.LatestVersion), func(t *testing.T) { 74 | lr, err := tc.Releases.GetLatestRelease(tc.VRange) 75 | require.NoError(t, err) 76 | require.Equal(t, tc.LatestVersion, lr.Version) 77 | }) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /pkg/semrel/semrel.go: -------------------------------------------------------------------------------- 1 | package semrel 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | 8 | "github.com/Masterminds/semver/v3" 9 | "github.com/go-semantic-release/semantic-release/v2/pkg/config" 10 | ) 11 | 12 | func calculateChange(commits []*Commit, latestRelease *Release) *Change { 13 | change := &Change{} 14 | for _, commit := range commits { 15 | if latestRelease.SHA == commit.SHA { 16 | break 17 | } 18 | change.Major = change.Major || commit.Change.Major 19 | change.Minor = change.Minor || commit.Change.Minor 20 | change.Patch = change.Patch || commit.Change.Patch 21 | } 22 | return change 23 | } 24 | 25 | //gocyclo:ignore 26 | func applyChange(rawVersion string, rawChange *Change, allowInitialDevelopmentVersions, forceBumpPatchVersion bool) string { 27 | version := semver.MustParse(rawVersion) 28 | change := &Change{ 29 | Major: rawChange.Major, 30 | Minor: rawChange.Minor, 31 | Patch: rawChange.Patch, 32 | } 33 | if !allowInitialDevelopmentVersions && version.Major() == 0 { 34 | change.Major = true 35 | } 36 | 37 | // never allow major version changed if allowInitialDevelopmentVersions is set 38 | if allowInitialDevelopmentVersions && version.Major() == 0 && change.Major { 39 | change.Major = false 40 | change.Minor = true 41 | } 42 | 43 | if allowInitialDevelopmentVersions && version.Major() == 0 && version.Minor() == 0 { 44 | change.Minor = true 45 | } 46 | if !change.Major && !change.Minor && !change.Patch { 47 | if forceBumpPatchVersion { 48 | change.Patch = true 49 | } else { 50 | return "" 51 | } 52 | } 53 | var newVersion semver.Version 54 | preRel := version.Prerelease() 55 | if preRel == "" { 56 | switch { 57 | case change.Major: 58 | newVersion = version.IncMajor() 59 | case change.Minor: 60 | newVersion = version.IncMinor() 61 | case change.Patch: 62 | newVersion = version.IncPatch() 63 | } 64 | return newVersion.String() 65 | } 66 | preRelVer := strings.Split(preRel, ".") 67 | if len(preRelVer) > 1 { 68 | idx, err := strconv.ParseInt(preRelVer[1], 10, 32) 69 | if err != nil { 70 | idx = 0 71 | } 72 | preRel = fmt.Sprintf("%s.%d", preRelVer[0], idx+1) 73 | } else { 74 | preRel += ".1" 75 | } 76 | newVersion, _ = version.SetPrerelease(preRel) 77 | return newVersion.String() 78 | } 79 | 80 | func GetNewVersion(conf *config.Config, commits []*Commit, latestRelease *Release) string { 81 | return applyChange(latestRelease.Version, calculateChange(commits, latestRelease), conf.AllowInitialDevelopmentVersions, conf.ForceBumpPatchVersion) 82 | } 83 | -------------------------------------------------------------------------------- /pkg/semrel/semrel_test.go: -------------------------------------------------------------------------------- 1 | package semrel 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/Masterminds/semver/v3" 8 | "github.com/go-semantic-release/semantic-release/v2/pkg/config" 9 | ) 10 | 11 | func TestCalculateChange(t *testing.T) { 12 | commits := []*Commit{ 13 | {SHA: "a", Change: &Change{Major: true, Minor: false, Patch: false}}, 14 | {SHA: "b", Change: &Change{Major: false, Minor: true, Patch: false}}, 15 | {SHA: "c", Change: &Change{Major: false, Minor: false, Patch: true}}, 16 | } 17 | change := calculateChange(commits, &Release{}) 18 | if !change.Major || !change.Minor || !change.Patch { 19 | t.Fail() 20 | } 21 | change = calculateChange(commits, &Release{SHA: "a"}) 22 | if change.Major || change.Minor || change.Patch { 23 | t.Fail() 24 | } 25 | newVersion := GetNewVersion(&config.Config{}, commits, &Release{SHA: "b", Version: "1.0.0"}) 26 | if newVersion != "2.0.0" { 27 | t.Fail() 28 | } 29 | } 30 | 31 | func TestApplyChange(t *testing.T) { 32 | NoChange := &Change{Major: false, Minor: false, Patch: false} 33 | PatchChange := &Change{Major: false, Minor: false, Patch: true} 34 | MinorChange := &Change{Major: false, Minor: true, Patch: true} 35 | MajorChange := &Change{Major: true, Minor: true, Patch: true} 36 | 37 | testCases := []struct { 38 | currentVersion string 39 | change *Change 40 | expectedVersion string 41 | allowInitialDevelopmentVersions bool 42 | forceBumpPatchVersion bool 43 | }{ 44 | // No Previous releases 45 | {"0.0.0", NoChange, "1.0.0", false, false}, 46 | {"0.0.0", PatchChange, "1.0.0", false, false}, 47 | {"0.0.0", MinorChange, "1.0.0", false, false}, 48 | {"0.0.0", MajorChange, "1.0.0", false, false}, 49 | 50 | {"0.0.0", NoChange, "0.1.0", true, false}, 51 | {"0.0.0", PatchChange, "0.1.0", true, false}, 52 | {"0.0.0", MinorChange, "0.1.0", true, false}, 53 | {"0.0.0", MajorChange, "0.1.0", true, false}, 54 | 55 | {"1.0.0", NoChange, "", false, false}, 56 | {"1.0.0", NoChange, "1.0.1", false, true}, 57 | {"1.0.0", PatchChange, "1.0.1", false, false}, 58 | {"1.0.0", MinorChange, "1.1.0", false, false}, 59 | {"1.0.0", MajorChange, "2.0.0", false, false}, 60 | {"0.1.0", NoChange, "1.0.0", false, false}, 61 | {"0.1.0", NoChange, "", true, false}, 62 | 63 | {"2.0.0-beta", MajorChange, "2.0.0-beta.1", false, false}, 64 | {"2.0.0-beta.2", MajorChange, "2.0.0-beta.3", false, false}, 65 | {"2.0.0-beta.1.1", MajorChange, "2.0.0-beta.2", false, false}, 66 | 67 | {"0.1.0", MajorChange, "0.2.0", true, false}, 68 | {"1.0.0", MajorChange, "2.0.0", true, false}, 69 | {"0.1.0", MinorChange, "0.2.0", true, false}, 70 | {"0.1.0", NoChange, "0.1.1", true, true}, 71 | } 72 | 73 | for _, tc := range testCases { 74 | t.Run(fmt.Sprintf("Version: %s, Change: %v, Expected: %s", tc.currentVersion, tc.change, tc.expectedVersion), func(t *testing.T) { 75 | current, err := semver.NewVersion(tc.currentVersion) 76 | if err != nil { 77 | t.Errorf("failed to create version: %v", err) 78 | } 79 | 80 | actual := applyChange(current.String(), tc.change, tc.allowInitialDevelopmentVersions, tc.forceBumpPatchVersion) 81 | 82 | // Handle no new version case 83 | if actual != "" && tc.expectedVersion != "" { 84 | if actual != tc.expectedVersion { 85 | t.Errorf("expected: %s, got: %s", tc.expectedVersion, actual) 86 | } 87 | } 88 | }) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /pkg/semrel/structs.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option go_package = "github.com/go-semantic-release/semantic-release/v2/pkg/semrel"; 3 | 4 | message RawCommit { 5 | string SHA = 1; 6 | string raw_message = 2; 7 | map annotations = 3; 8 | } 9 | 10 | message Change { 11 | bool major = 1; 12 | bool minor = 2; 13 | bool patch = 3; 14 | map annotations = 4; 15 | } 16 | 17 | message Commit { 18 | string SHA = 1; 19 | repeated string raw = 2; 20 | string type = 3; 21 | string scope = 4; 22 | string message = 5; 23 | Change change = 6; 24 | map annotations = 7; 25 | } 26 | 27 | message Release { 28 | string SHA = 1; 29 | string version = 2; 30 | map annotations = 3; 31 | } 32 | -------------------------------------------------------------------------------- /pkg/updater/updater.go: -------------------------------------------------------------------------------- 1 | package updater 2 | 3 | import ( 4 | "fmt" 5 | "path" 6 | "regexp" 7 | ) 8 | 9 | type FilesUpdater interface { 10 | Init(map[string]string) error 11 | Name() string 12 | Version() string 13 | ForFiles() string 14 | Apply(file, newVersion string) error 15 | } 16 | 17 | type ChainedUpdater struct { 18 | Updaters []FilesUpdater 19 | } 20 | 21 | func (u *ChainedUpdater) Init(conf map[string]string) error { 22 | for _, fu := range u.Updaters { 23 | err := fu.Init(conf) 24 | if err != nil { 25 | return err 26 | } 27 | } 28 | return nil 29 | } 30 | 31 | func (u *ChainedUpdater) GetNameVersionPairs() []string { 32 | ret := make([]string, len(u.Updaters)) 33 | for i, fu := range u.Updaters { 34 | ret[i] = fmt.Sprintf("%s@%s", fu.Name(), fu.Version()) 35 | } 36 | return ret 37 | } 38 | 39 | func (u *ChainedUpdater) Apply(file, newVersion string) error { 40 | for _, fu := range u.Updaters { 41 | re, err := regexp.Compile(fu.ForFiles()) 42 | if err != nil { 43 | return err 44 | } 45 | if !re.MatchString(path.Base(file)) { 46 | continue 47 | } 48 | if err := fu.Apply(file, newVersion); err != nil { 49 | return err 50 | } 51 | } 52 | return nil 53 | } 54 | -------------------------------------------------------------------------------- /pkg/updater/updater.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option go_package = "github.com/go-semantic-release/semantic-release/v2/pkg/updater"; 3 | 4 | message FilesUpdaterInit { 5 | message Request { 6 | map config = 1; 7 | } 8 | message Response { 9 | string error = 1; 10 | } 11 | } 12 | 13 | message FilesUpdaterName { 14 | message Request {} 15 | message Response { 16 | string name = 1; 17 | } 18 | } 19 | 20 | message FilesUpdaterVersion { 21 | message Request {} 22 | message Response { 23 | string version = 1; 24 | } 25 | } 26 | 27 | message FilesUpdaterForFiles { 28 | message Request {} 29 | message Response { 30 | string files = 1; 31 | } 32 | } 33 | 34 | message FilesUpdaterApply { 35 | message Request { 36 | string file = 1; 37 | string new_version = 2; 38 | } 39 | message Response { 40 | string error = 1; 41 | } 42 | } 43 | 44 | service FilesUpdaterPlugin { 45 | rpc Init(FilesUpdaterInit.Request) returns (FilesUpdaterInit.Response); 46 | rpc Name(FilesUpdaterName.Request) returns (FilesUpdaterName.Response); 47 | rpc Version(FilesUpdaterVersion.Request) returns (FilesUpdaterVersion.Response); 48 | rpc ForFiles(FilesUpdaterForFiles.Request) returns (FilesUpdaterForFiles.Response); 49 | rpc Apply(FilesUpdaterApply.Request) returns (FilesUpdaterApply.Response); 50 | } 51 | -------------------------------------------------------------------------------- /pkg/updater/updater_grpc.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT. 2 | // versions: 3 | // - protoc-gen-go-grpc v1.3.0 4 | // - protoc v4.24.4 5 | // source: pkg/updater/updater.proto 6 | 7 | package updater 8 | 9 | import ( 10 | context "context" 11 | grpc "google.golang.org/grpc" 12 | codes "google.golang.org/grpc/codes" 13 | status "google.golang.org/grpc/status" 14 | ) 15 | 16 | // This is a compile-time assertion to ensure that this generated file 17 | // is compatible with the grpc package it is being compiled against. 18 | // Requires gRPC-Go v1.32.0 or later. 19 | const _ = grpc.SupportPackageIsVersion7 20 | 21 | const ( 22 | FilesUpdaterPlugin_Init_FullMethodName = "/FilesUpdaterPlugin/Init" 23 | FilesUpdaterPlugin_Name_FullMethodName = "/FilesUpdaterPlugin/Name" 24 | FilesUpdaterPlugin_Version_FullMethodName = "/FilesUpdaterPlugin/Version" 25 | FilesUpdaterPlugin_ForFiles_FullMethodName = "/FilesUpdaterPlugin/ForFiles" 26 | FilesUpdaterPlugin_Apply_FullMethodName = "/FilesUpdaterPlugin/Apply" 27 | ) 28 | 29 | // FilesUpdaterPluginClient is the client API for FilesUpdaterPlugin service. 30 | // 31 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. 32 | type FilesUpdaterPluginClient interface { 33 | Init(ctx context.Context, in *FilesUpdaterInit_Request, opts ...grpc.CallOption) (*FilesUpdaterInit_Response, error) 34 | Name(ctx context.Context, in *FilesUpdaterName_Request, opts ...grpc.CallOption) (*FilesUpdaterName_Response, error) 35 | Version(ctx context.Context, in *FilesUpdaterVersion_Request, opts ...grpc.CallOption) (*FilesUpdaterVersion_Response, error) 36 | ForFiles(ctx context.Context, in *FilesUpdaterForFiles_Request, opts ...grpc.CallOption) (*FilesUpdaterForFiles_Response, error) 37 | Apply(ctx context.Context, in *FilesUpdaterApply_Request, opts ...grpc.CallOption) (*FilesUpdaterApply_Response, error) 38 | } 39 | 40 | type filesUpdaterPluginClient struct { 41 | cc grpc.ClientConnInterface 42 | } 43 | 44 | func NewFilesUpdaterPluginClient(cc grpc.ClientConnInterface) FilesUpdaterPluginClient { 45 | return &filesUpdaterPluginClient{cc} 46 | } 47 | 48 | func (c *filesUpdaterPluginClient) Init(ctx context.Context, in *FilesUpdaterInit_Request, opts ...grpc.CallOption) (*FilesUpdaterInit_Response, error) { 49 | out := new(FilesUpdaterInit_Response) 50 | err := c.cc.Invoke(ctx, FilesUpdaterPlugin_Init_FullMethodName, in, out, opts...) 51 | if err != nil { 52 | return nil, err 53 | } 54 | return out, nil 55 | } 56 | 57 | func (c *filesUpdaterPluginClient) Name(ctx context.Context, in *FilesUpdaterName_Request, opts ...grpc.CallOption) (*FilesUpdaterName_Response, error) { 58 | out := new(FilesUpdaterName_Response) 59 | err := c.cc.Invoke(ctx, FilesUpdaterPlugin_Name_FullMethodName, in, out, opts...) 60 | if err != nil { 61 | return nil, err 62 | } 63 | return out, nil 64 | } 65 | 66 | func (c *filesUpdaterPluginClient) Version(ctx context.Context, in *FilesUpdaterVersion_Request, opts ...grpc.CallOption) (*FilesUpdaterVersion_Response, error) { 67 | out := new(FilesUpdaterVersion_Response) 68 | err := c.cc.Invoke(ctx, FilesUpdaterPlugin_Version_FullMethodName, in, out, opts...) 69 | if err != nil { 70 | return nil, err 71 | } 72 | return out, nil 73 | } 74 | 75 | func (c *filesUpdaterPluginClient) ForFiles(ctx context.Context, in *FilesUpdaterForFiles_Request, opts ...grpc.CallOption) (*FilesUpdaterForFiles_Response, error) { 76 | out := new(FilesUpdaterForFiles_Response) 77 | err := c.cc.Invoke(ctx, FilesUpdaterPlugin_ForFiles_FullMethodName, in, out, opts...) 78 | if err != nil { 79 | return nil, err 80 | } 81 | return out, nil 82 | } 83 | 84 | func (c *filesUpdaterPluginClient) Apply(ctx context.Context, in *FilesUpdaterApply_Request, opts ...grpc.CallOption) (*FilesUpdaterApply_Response, error) { 85 | out := new(FilesUpdaterApply_Response) 86 | err := c.cc.Invoke(ctx, FilesUpdaterPlugin_Apply_FullMethodName, in, out, opts...) 87 | if err != nil { 88 | return nil, err 89 | } 90 | return out, nil 91 | } 92 | 93 | // FilesUpdaterPluginServer is the server API for FilesUpdaterPlugin service. 94 | // All implementations must embed UnimplementedFilesUpdaterPluginServer 95 | // for forward compatibility 96 | type FilesUpdaterPluginServer interface { 97 | Init(context.Context, *FilesUpdaterInit_Request) (*FilesUpdaterInit_Response, error) 98 | Name(context.Context, *FilesUpdaterName_Request) (*FilesUpdaterName_Response, error) 99 | Version(context.Context, *FilesUpdaterVersion_Request) (*FilesUpdaterVersion_Response, error) 100 | ForFiles(context.Context, *FilesUpdaterForFiles_Request) (*FilesUpdaterForFiles_Response, error) 101 | Apply(context.Context, *FilesUpdaterApply_Request) (*FilesUpdaterApply_Response, error) 102 | mustEmbedUnimplementedFilesUpdaterPluginServer() 103 | } 104 | 105 | // UnimplementedFilesUpdaterPluginServer must be embedded to have forward compatible implementations. 106 | type UnimplementedFilesUpdaterPluginServer struct { 107 | } 108 | 109 | func (UnimplementedFilesUpdaterPluginServer) Init(context.Context, *FilesUpdaterInit_Request) (*FilesUpdaterInit_Response, error) { 110 | return nil, status.Errorf(codes.Unimplemented, "method Init not implemented") 111 | } 112 | func (UnimplementedFilesUpdaterPluginServer) Name(context.Context, *FilesUpdaterName_Request) (*FilesUpdaterName_Response, error) { 113 | return nil, status.Errorf(codes.Unimplemented, "method Name not implemented") 114 | } 115 | func (UnimplementedFilesUpdaterPluginServer) Version(context.Context, *FilesUpdaterVersion_Request) (*FilesUpdaterVersion_Response, error) { 116 | return nil, status.Errorf(codes.Unimplemented, "method Version not implemented") 117 | } 118 | func (UnimplementedFilesUpdaterPluginServer) ForFiles(context.Context, *FilesUpdaterForFiles_Request) (*FilesUpdaterForFiles_Response, error) { 119 | return nil, status.Errorf(codes.Unimplemented, "method ForFiles not implemented") 120 | } 121 | func (UnimplementedFilesUpdaterPluginServer) Apply(context.Context, *FilesUpdaterApply_Request) (*FilesUpdaterApply_Response, error) { 122 | return nil, status.Errorf(codes.Unimplemented, "method Apply not implemented") 123 | } 124 | func (UnimplementedFilesUpdaterPluginServer) mustEmbedUnimplementedFilesUpdaterPluginServer() {} 125 | 126 | // UnsafeFilesUpdaterPluginServer may be embedded to opt out of forward compatibility for this service. 127 | // Use of this interface is not recommended, as added methods to FilesUpdaterPluginServer will 128 | // result in compilation errors. 129 | type UnsafeFilesUpdaterPluginServer interface { 130 | mustEmbedUnimplementedFilesUpdaterPluginServer() 131 | } 132 | 133 | func RegisterFilesUpdaterPluginServer(s grpc.ServiceRegistrar, srv FilesUpdaterPluginServer) { 134 | s.RegisterService(&FilesUpdaterPlugin_ServiceDesc, srv) 135 | } 136 | 137 | func _FilesUpdaterPlugin_Init_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 138 | in := new(FilesUpdaterInit_Request) 139 | if err := dec(in); err != nil { 140 | return nil, err 141 | } 142 | if interceptor == nil { 143 | return srv.(FilesUpdaterPluginServer).Init(ctx, in) 144 | } 145 | info := &grpc.UnaryServerInfo{ 146 | Server: srv, 147 | FullMethod: FilesUpdaterPlugin_Init_FullMethodName, 148 | } 149 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 150 | return srv.(FilesUpdaterPluginServer).Init(ctx, req.(*FilesUpdaterInit_Request)) 151 | } 152 | return interceptor(ctx, in, info, handler) 153 | } 154 | 155 | func _FilesUpdaterPlugin_Name_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 156 | in := new(FilesUpdaterName_Request) 157 | if err := dec(in); err != nil { 158 | return nil, err 159 | } 160 | if interceptor == nil { 161 | return srv.(FilesUpdaterPluginServer).Name(ctx, in) 162 | } 163 | info := &grpc.UnaryServerInfo{ 164 | Server: srv, 165 | FullMethod: FilesUpdaterPlugin_Name_FullMethodName, 166 | } 167 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 168 | return srv.(FilesUpdaterPluginServer).Name(ctx, req.(*FilesUpdaterName_Request)) 169 | } 170 | return interceptor(ctx, in, info, handler) 171 | } 172 | 173 | func _FilesUpdaterPlugin_Version_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 174 | in := new(FilesUpdaterVersion_Request) 175 | if err := dec(in); err != nil { 176 | return nil, err 177 | } 178 | if interceptor == nil { 179 | return srv.(FilesUpdaterPluginServer).Version(ctx, in) 180 | } 181 | info := &grpc.UnaryServerInfo{ 182 | Server: srv, 183 | FullMethod: FilesUpdaterPlugin_Version_FullMethodName, 184 | } 185 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 186 | return srv.(FilesUpdaterPluginServer).Version(ctx, req.(*FilesUpdaterVersion_Request)) 187 | } 188 | return interceptor(ctx, in, info, handler) 189 | } 190 | 191 | func _FilesUpdaterPlugin_ForFiles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 192 | in := new(FilesUpdaterForFiles_Request) 193 | if err := dec(in); err != nil { 194 | return nil, err 195 | } 196 | if interceptor == nil { 197 | return srv.(FilesUpdaterPluginServer).ForFiles(ctx, in) 198 | } 199 | info := &grpc.UnaryServerInfo{ 200 | Server: srv, 201 | FullMethod: FilesUpdaterPlugin_ForFiles_FullMethodName, 202 | } 203 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 204 | return srv.(FilesUpdaterPluginServer).ForFiles(ctx, req.(*FilesUpdaterForFiles_Request)) 205 | } 206 | return interceptor(ctx, in, info, handler) 207 | } 208 | 209 | func _FilesUpdaterPlugin_Apply_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 210 | in := new(FilesUpdaterApply_Request) 211 | if err := dec(in); err != nil { 212 | return nil, err 213 | } 214 | if interceptor == nil { 215 | return srv.(FilesUpdaterPluginServer).Apply(ctx, in) 216 | } 217 | info := &grpc.UnaryServerInfo{ 218 | Server: srv, 219 | FullMethod: FilesUpdaterPlugin_Apply_FullMethodName, 220 | } 221 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 222 | return srv.(FilesUpdaterPluginServer).Apply(ctx, req.(*FilesUpdaterApply_Request)) 223 | } 224 | return interceptor(ctx, in, info, handler) 225 | } 226 | 227 | // FilesUpdaterPlugin_ServiceDesc is the grpc.ServiceDesc for FilesUpdaterPlugin service. 228 | // It's only intended for direct use with grpc.RegisterService, 229 | // and not to be introspected or modified (even as a copy) 230 | var FilesUpdaterPlugin_ServiceDesc = grpc.ServiceDesc{ 231 | ServiceName: "FilesUpdaterPlugin", 232 | HandlerType: (*FilesUpdaterPluginServer)(nil), 233 | Methods: []grpc.MethodDesc{ 234 | { 235 | MethodName: "Init", 236 | Handler: _FilesUpdaterPlugin_Init_Handler, 237 | }, 238 | { 239 | MethodName: "Name", 240 | Handler: _FilesUpdaterPlugin_Name_Handler, 241 | }, 242 | { 243 | MethodName: "Version", 244 | Handler: _FilesUpdaterPlugin_Version_Handler, 245 | }, 246 | { 247 | MethodName: "ForFiles", 248 | Handler: _FilesUpdaterPlugin_ForFiles_Handler, 249 | }, 250 | { 251 | MethodName: "Apply", 252 | Handler: _FilesUpdaterPlugin_Apply_Handler, 253 | }, 254 | }, 255 | Streams: []grpc.StreamDesc{}, 256 | Metadata: "pkg/updater/updater.proto", 257 | } 258 | -------------------------------------------------------------------------------- /pkg/updater/updater_test.go: -------------------------------------------------------------------------------- 1 | package updater 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | type testFileUpdater struct { 10 | req *require.Assertions 11 | nVer string 12 | } 13 | 14 | func (tfu *testFileUpdater) Init(map[string]string) error { 15 | return nil 16 | } 17 | 18 | func (tfu *testFileUpdater) Name() string { 19 | return "test" 20 | } 21 | 22 | func (tfu *testFileUpdater) Version() string { 23 | return "1.0.0" 24 | } 25 | 26 | func (tfu *testFileUpdater) ForFiles() string { 27 | return "package\\.json" 28 | } 29 | 30 | func (tfu *testFileUpdater) Apply(_, newVersion string) error { 31 | tfu.req.Equal(newVersion, tfu.nVer, "invalid version") 32 | return nil 33 | } 34 | 35 | func TestChainedUpdater(t *testing.T) { 36 | require := require.New(t) 37 | nVer := "1.2.3" 38 | tfu := &testFileUpdater{require, nVer} 39 | updaters := &ChainedUpdater{Updaters: []FilesUpdater{tfu}} 40 | if err := updaters.Apply("../../test/package.json", nVer); err != nil { 41 | require.NoError(err) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /pkg/updater/updater_wrapper.go: -------------------------------------------------------------------------------- 1 | package updater 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | ) 7 | 8 | const FilesUpdaterPluginName = "files_updater" 9 | 10 | type FilesUpdaterServer struct { 11 | Impl FilesUpdater 12 | UnimplementedFilesUpdaterPluginServer 13 | } 14 | 15 | func (f *FilesUpdaterServer) Init(_ context.Context, request *FilesUpdaterInit_Request) (*FilesUpdaterInit_Response, error) { 16 | err := f.Impl.Init(request.Config) 17 | res := &FilesUpdaterInit_Response{} 18 | if err != nil { 19 | res.Error = err.Error() 20 | } 21 | return res, nil 22 | } 23 | 24 | func (f *FilesUpdaterServer) Name(_ context.Context, _ *FilesUpdaterName_Request) (*FilesUpdaterName_Response, error) { 25 | return &FilesUpdaterName_Response{Name: f.Impl.Name()}, nil 26 | } 27 | 28 | func (f *FilesUpdaterServer) Version(_ context.Context, _ *FilesUpdaterVersion_Request) (*FilesUpdaterVersion_Response, error) { 29 | return &FilesUpdaterVersion_Response{Version: f.Impl.Version()}, nil 30 | } 31 | 32 | func (f *FilesUpdaterServer) ForFiles(_ context.Context, _ *FilesUpdaterForFiles_Request) (*FilesUpdaterForFiles_Response, error) { 33 | return &FilesUpdaterForFiles_Response{Files: f.Impl.ForFiles()}, nil 34 | } 35 | 36 | func (f *FilesUpdaterServer) Apply(_ context.Context, request *FilesUpdaterApply_Request) (*FilesUpdaterApply_Response, error) { 37 | err := f.Impl.Apply(request.File, request.NewVersion) 38 | res := &FilesUpdaterApply_Response{} 39 | if err != nil { 40 | res.Error = err.Error() 41 | } 42 | return res, nil 43 | } 44 | 45 | type FilesUpdaterClient struct { 46 | Impl FilesUpdaterPluginClient 47 | } 48 | 49 | func (f *FilesUpdaterClient) Init(m map[string]string) error { 50 | res, err := f.Impl.Init(context.Background(), &FilesUpdaterInit_Request{ 51 | Config: m, 52 | }) 53 | if err != nil { 54 | return err 55 | } 56 | if res.Error != "" { 57 | return errors.New(res.Error) 58 | } 59 | return nil 60 | } 61 | 62 | func (f *FilesUpdaterClient) Name() string { 63 | res, err := f.Impl.Name(context.Background(), &FilesUpdaterName_Request{}) 64 | if err != nil { 65 | panic(err) 66 | } 67 | return res.Name 68 | } 69 | 70 | func (f *FilesUpdaterClient) Version() string { 71 | res, err := f.Impl.Version(context.Background(), &FilesUpdaterVersion_Request{}) 72 | if err != nil { 73 | panic(err) 74 | } 75 | return res.Version 76 | } 77 | 78 | func (f *FilesUpdaterClient) ForFiles() string { 79 | res, err := f.Impl.ForFiles(context.Background(), &FilesUpdaterForFiles_Request{}) 80 | if err != nil { 81 | panic(err) 82 | } 83 | return res.Files 84 | } 85 | 86 | func (f *FilesUpdaterClient) Apply(file, newVersion string) error { 87 | res, err := f.Impl.Apply(context.Background(), &FilesUpdaterApply_Request{ 88 | File: file, 89 | NewVersion: newVersion, 90 | }) 91 | if err != nil { 92 | return err 93 | } 94 | if res.Error != "" { 95 | return errors.New(res.Error) 96 | } 97 | return nil 98 | } 99 | -------------------------------------------------------------------------------- /scripts/generate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | for f in $(find ./pkg -name "*.proto") ; do 6 | echo "generating $f" 7 | protoc -I ./ $f --go_opt=paths=source_relative --go_out=./ --go-grpc_opt=paths=source_relative --go-grpc_out=./ 8 | done 9 | --------------------------------------------------------------------------------