├── pkg ├── strategy │ ├── semantic │ │ ├── testdata │ │ │ ├── .gitignore │ │ │ └── git-repo.tar.gz │ │ ├── semantic_test.go │ │ └── semantic.go │ ├── fromtag │ │ ├── testdata │ │ │ ├── .gitignore │ │ │ ├── git-repo.tar.gz │ │ │ └── git-repo-origin.tar.gz │ │ ├── from_tag.go │ │ └── from_tag_test.go │ ├── fromfile │ │ ├── testdata │ │ │ ├── gradle.properties │ │ │ ├── gradle │ │ │ │ ├── gradle.properties │ │ │ │ └── build.gradle │ │ │ ├── Makefile │ │ │ ├── configure.ac │ │ │ ├── package.json │ │ │ ├── CMakeLists.txt │ │ │ ├── setup-oneline.py │ │ │ ├── Chart.yaml │ │ │ ├── setup.py │ │ │ ├── setup-double-quotes.py │ │ │ ├── setup-nested.py │ │ │ ├── pom.xml │ │ │ ├── build.gradle │ │ │ └── build.gradle.kts │ │ ├── helm_chart.go │ │ ├── js_package.go │ │ ├── cmake.go │ │ ├── makefile.go │ │ ├── automake.go │ │ ├── gradle.go │ │ ├── helm_chart_test.go │ │ ├── python.go │ │ ├── python_test.go │ │ ├── maven_pom.go │ │ ├── from_file.go │ │ └── from_file_test.go │ ├── auto │ │ ├── testdata │ │ │ ├── .gitignore │ │ │ ├── git-repo.tar.gz │ │ │ ├── empty-git-repo.tar.gz │ │ │ └── repo-with-merge-commit.tar.gz │ │ ├── auto.go │ │ └── auto_test.go │ ├── strategy.go │ ├── manual │ │ └── manual.go │ └── increment │ │ ├── increment.go │ │ └── increment_test.go └── tag │ ├── tag_test.go │ └── tag.go ├── OWNERS_ALIASES ├── OWNERS ├── hack ├── github-actions-entrypoint.sh └── changelog-header.md ├── .pre-commit-config.yaml ├── Dockerfile ├── .golangci.yml ├── .sonarcloud.properties ├── .gitignore ├── .lighthouse └── jenkins-x │ ├── triggers.yaml │ ├── lint.yaml │ ├── pullrequest.yaml │ └── release.yaml ├── .jx └── updatebot.yaml ├── .goreleaser.yml ├── Makefile ├── action.yml ├── go.mod ├── main.go ├── LICENSE ├── README.md └── go.sum /pkg/strategy/semantic/testdata/.gitignore: -------------------------------------------------------------------------------- 1 | /git-repo -------------------------------------------------------------------------------- /pkg/strategy/fromtag/testdata/.gitignore: -------------------------------------------------------------------------------- 1 | /git-repo 2 | /git-repo-origin -------------------------------------------------------------------------------- /pkg/strategy/fromfile/testdata/gradle.properties: -------------------------------------------------------------------------------- 1 | version = '1.2.7' 2 | 3 | -------------------------------------------------------------------------------- /OWNERS_ALIASES: -------------------------------------------------------------------------------- 1 | foreignAliases: 2 | - name: jx-community 3 | org: jenkins-x 4 | -------------------------------------------------------------------------------- /pkg/strategy/fromfile/testdata/gradle/gradle.properties: -------------------------------------------------------------------------------- 1 | version = '1.2.7' 2 | 3 | -------------------------------------------------------------------------------- /pkg/strategy/fromfile/testdata/Makefile: -------------------------------------------------------------------------------- 1 | NAME := jx-release-version 2 | VERSION := 1.2.4 -------------------------------------------------------------------------------- /OWNERS: -------------------------------------------------------------------------------- 1 | approvers: 2 | - maintainers 3 | - vbehar 4 | reviewers: 5 | - maintainers 6 | - vbehar 7 | -------------------------------------------------------------------------------- /pkg/strategy/auto/testdata/.gitignore: -------------------------------------------------------------------------------- 1 | /git-repo 2 | /empty-git-repo 3 | /repo-with-merge-commit -------------------------------------------------------------------------------- /pkg/strategy/fromfile/testdata/configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT(jx-release-version, 1.2.5, someone@example.com) -------------------------------------------------------------------------------- /pkg/strategy/fromfile/testdata/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jx-release-version", 3 | "version": "1.2.10" 4 | } -------------------------------------------------------------------------------- /hack/github-actions-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -le 2 | 3 | version=$(jx-release-version) 4 | echo "version=$version" >> $GITHUB_OUTPUT 5 | -------------------------------------------------------------------------------- /pkg/strategy/fromfile/testdata/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | project(jx-release-version VERSION 1.2.6 LANGUAGES GO) -------------------------------------------------------------------------------- /pkg/strategy/auto/testdata/git-repo.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkins-x-plugins/jx-release-version/HEAD/pkg/strategy/auto/testdata/git-repo.tar.gz -------------------------------------------------------------------------------- /pkg/strategy/fromtag/testdata/git-repo.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkins-x-plugins/jx-release-version/HEAD/pkg/strategy/fromtag/testdata/git-repo.tar.gz -------------------------------------------------------------------------------- /pkg/strategy/auto/testdata/empty-git-repo.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkins-x-plugins/jx-release-version/HEAD/pkg/strategy/auto/testdata/empty-git-repo.tar.gz -------------------------------------------------------------------------------- /pkg/strategy/semantic/testdata/git-repo.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkins-x-plugins/jx-release-version/HEAD/pkg/strategy/semantic/testdata/git-repo.tar.gz -------------------------------------------------------------------------------- /pkg/strategy/fromtag/testdata/git-repo-origin.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkins-x-plugins/jx-release-version/HEAD/pkg/strategy/fromtag/testdata/git-repo-origin.tar.gz -------------------------------------------------------------------------------- /pkg/strategy/auto/testdata/repo-with-merge-commit.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkins-x-plugins/jx-release-version/HEAD/pkg/strategy/auto/testdata/repo-with-merge-commit.tar.gz -------------------------------------------------------------------------------- /pkg/strategy/fromfile/testdata/setup-oneline.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup(name='jx-release-version', version='1.2.13', description='A test setup.py script for testing',) -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: git@github.com:Yelp/detect-secrets 3 | rev: v0.12.4 4 | hooks: 5 | - id: detect-secrets 6 | args: ['--baseline', '.secrets.baseline'] 7 | exclude: .*/tests/.* -------------------------------------------------------------------------------- /pkg/strategy/fromfile/testdata/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: my-chart 3 | description: whatever 4 | version: 1.2.3 5 | appVersion: latest 6 | dependencies: 7 | - name: some-dep 8 | version: 1.0.42 9 | repository: stable -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.17 2 | 3 | COPY ./build/linux/jx-release-version /usr/bin/jx-release-version 4 | COPY ./hack/github-actions-entrypoint.sh /usr/bin/github-actions-entrypoint.sh 5 | 6 | ENTRYPOINT ["jx-release-version"] 7 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | # See https://golangci-lint.run/usage/configuration/#config-file 2 | 3 | run: 4 | timeout: 10m 5 | 6 | # print all issues that are found 7 | issues: 8 | max-issues-per-linter: 0 9 | max-same-issues: 0 10 | -------------------------------------------------------------------------------- /.sonarcloud.properties: -------------------------------------------------------------------------------- 1 | # See https://sonarcloud.io/documentation/analysis/automatic-analysis/ 2 | 3 | # Exclusions for copy-paste detection 4 | # We don't care about a bit of duplication in our unit tests 5 | sonar.cpd.exclusions=**/*_test.go 6 | -------------------------------------------------------------------------------- /pkg/strategy/fromfile/testdata/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | # Red Herring 4 | version='1.0.0' 5 | 6 | setup( 7 | name='jx-release-version', 8 | version = '1.2.11', 9 | description='A test setup.py script for testing', 10 | ) -------------------------------------------------------------------------------- /pkg/strategy/fromfile/testdata/setup-double-quotes.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | # Red Herring 4 | version="1.0.0" 5 | 6 | setup( 7 | name="jx-release-version", 8 | version = "1.2.14", 9 | description="A test setup.py script for testing", 10 | ) -------------------------------------------------------------------------------- /pkg/strategy/fromfile/testdata/setup-nested.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | 4 | def call_setup(): 5 | setup( 6 | name='jx-release-version', 7 | version='1.2.12', 8 | description='A test setup.py script for testing', 9 | ) 10 | 11 | call_setup() -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | .idea 3 | bin/ 4 | dist/ 5 | build/ 6 | .DS_Store 7 | coverage.out 8 | /strategy 9 | 10 | # syft 11 | bin/syft 12 | 13 | # image tar files 14 | image.tar 15 | 16 | # docker credential binaries 17 | docker-credential-* 18 | 19 | # sbom json created by syft 20 | sbom.json 21 | -------------------------------------------------------------------------------- /pkg/strategy/strategy.go: -------------------------------------------------------------------------------- 1 | package strategy 2 | 3 | import ( 4 | "github.com/Masterminds/semver/v3" 5 | ) 6 | 7 | type VersionReader interface { 8 | ReadVersion() (*semver.Version, error) 9 | } 10 | 11 | type VersionBumper interface { 12 | BumpVersion(previous semver.Version) (*semver.Version, error) 13 | } 14 | -------------------------------------------------------------------------------- /hack/changelog-header.md: -------------------------------------------------------------------------------- 1 | ### Linux 2 | 3 | ```shell 4 | curl -L https://github.com/jenkins-x-plugins/jx-release-version/releases/download/v{{.Version}}/jx-release-version-linux-amd64.tar.gz | tar xzv 5 | sudo mv jx-release-version /usr/local/bin 6 | ``` 7 | 8 | ### macOS 9 | 10 | ```shell 11 | curl -L https://github.com/jenkins-x-plugins/jx-release-version/releases/download/v{{.Version}}/jx-release-version-darwin-amd64.tar.gz | tar xzv 12 | sudo mv jx-release-version /usr/local/bin 13 | ``` 14 | 15 | -------------------------------------------------------------------------------- /pkg/strategy/manual/manual.go: -------------------------------------------------------------------------------- 1 | package manual 2 | 3 | import ( 4 | "github.com/Masterminds/semver/v3" 5 | "github.com/jenkins-x/jx-logging/v3/pkg/log" 6 | ) 7 | 8 | type Strategy struct { 9 | Version string 10 | } 11 | 12 | func (s Strategy) ReadVersion() (*semver.Version, error) { 13 | log.Logger().Debugf("Using manual version %s", s.Version) 14 | return semver.NewVersion(s.Version) 15 | } 16 | 17 | func (s Strategy) BumpVersion(_ semver.Version) (*semver.Version, error) { 18 | log.Logger().Debugf("Using manual version %s", s.Version) 19 | return semver.NewVersion(s.Version) 20 | } 21 | -------------------------------------------------------------------------------- /pkg/strategy/fromfile/testdata/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | org.sonatype.oss 6 | oss-parent 7 | 9 8 | 9 | 10 | com.example 11 | jx-release-version 12 | 1.2.9 13 | pom 14 | -------------------------------------------------------------------------------- /.lighthouse/jenkins-x/triggers.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: config.lighthouse.jenkins-x.io/v1alpha1 2 | kind: TriggerConfig 3 | spec: 4 | presubmits: 5 | - name: pr 6 | context: "pr" 7 | always_run: true 8 | optional: false 9 | trigger: (?m)^/test( all| pr),?(s+|$) 10 | rerun_command: /test pr 11 | source: "pullrequest.yaml" 12 | - name: lint 13 | context: "lint" 14 | always_run: true 15 | optional: false 16 | trigger: (?m)^/test( all| lint),?(s+|$) 17 | rerun_command: /test lint 18 | source: "lint.yaml" 19 | postsubmits: 20 | - name: release 21 | context: "release" 22 | source: "release.yaml" 23 | ignore_changes: '^(action\.yml)|(README\.md)$' 24 | branches: 25 | - ^main$ 26 | - ^master$ 27 | -------------------------------------------------------------------------------- /pkg/strategy/increment/increment.go: -------------------------------------------------------------------------------- 1 | package increment 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/Masterminds/semver/v3" 7 | "github.com/jenkins-x/jx-logging/v3/pkg/log" 8 | ) 9 | 10 | type Strategy struct { 11 | ComponentToIncrement string 12 | } 13 | 14 | func (s Strategy) BumpVersion(previous semver.Version) (*semver.Version, error) { 15 | var next semver.Version 16 | switch strings.ToLower(s.ComponentToIncrement) { 17 | case "major": 18 | log.Logger().Debug("Incrementing major component") 19 | next = previous.IncMajor() 20 | case "minor": 21 | log.Logger().Debug("Incrementing minor component") 22 | next = previous.IncMinor() 23 | default: 24 | log.Logger().Debug("Incrementing patch component") 25 | next = previous.IncPatch() 26 | } 27 | return &next, nil 28 | } 29 | -------------------------------------------------------------------------------- /.jx/updatebot.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: updatebot.jenkins-x.io/v1alpha1 2 | kind: UpdateConfig 3 | spec: 4 | rules: 5 | - urls: 6 | - https://github.com/jenkins-x/jx3-pipeline-catalog 7 | - https://github.com/jenkins-x/jx 8 | - https://github.com/jenkins-x-plugins/jx-admin 9 | - https://github.com/jenkins-x/terraform-aws-eks-jx 10 | - https://github.com/jenkins-x/tekton-dashboard-helm-chart 11 | - https://github.com/jenkins-x/jx-git-operator 12 | - https://github.com/jenkins-x/cosign-image 13 | - https://github.com/jenkins-x/trivydb 14 | - https://github.com/jenkins-x/jx-cli 15 | changes: 16 | - regex: 17 | pattern: "jenkins-x/jx-release-version:(.*)" 18 | files: 19 | - "**/*.yaml" 20 | -------------------------------------------------------------------------------- /.lighthouse/jenkins-x/lint.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: PipelineRun 3 | metadata: 4 | creationTimestamp: null 5 | name: lint 6 | spec: 7 | pipelineSpec: 8 | tasks: 9 | - name: jx-pipeline-lint 10 | resources: {} 11 | taskSpec: 12 | metadata: {} 13 | stepTemplate: 14 | image: uses:jenkins-x/jx3-pipeline-catalog/tasks/go/pullrequest.yaml@versionStream 15 | name: "" 16 | resources: {} 17 | workingDir: /workspace/source 18 | steps: 19 | - image: uses:jenkins-x/jx3-pipeline-catalog/tasks/git-clone/git-clone-pr.yaml@versionStream 20 | name: "" 21 | resources: {} 22 | - name: make-lint 23 | resources: {} 24 | podTemplate: {} 25 | serviceAccountName: tekton-bot 26 | timeout: 30m0s 27 | status: {} 28 | -------------------------------------------------------------------------------- /pkg/strategy/fromfile/helm_chart.go: -------------------------------------------------------------------------------- 1 | package fromfile 2 | 3 | import ( 4 | "os" 5 | 6 | "gopkg.in/yaml.v3" 7 | ) 8 | 9 | type HelmChartVersionReader struct { 10 | } 11 | 12 | func (r HelmChartVersionReader) String() string { 13 | return "helm-chart" 14 | } 15 | 16 | func (r HelmChartVersionReader) SupportedFiles() []string { 17 | return []string{ 18 | "Chart.yaml", 19 | } 20 | } 21 | 22 | func (r HelmChartVersionReader) ReadFileVersion(filePath string) (string, error) { 23 | f, err := os.Open(filePath) 24 | if err != nil { 25 | return "", err 26 | } 27 | defer f.Close() 28 | 29 | var chart HelmChart 30 | err = yaml.NewDecoder(f).Decode(&chart) 31 | if err != nil { 32 | return "", err 33 | } 34 | 35 | if chart.Version == "" { 36 | return "", ErrFileHasNoVersion 37 | } 38 | 39 | return chart.Version, nil 40 | } 41 | 42 | type HelmChart struct { 43 | Version string `yaml:"version"` 44 | } 45 | -------------------------------------------------------------------------------- /pkg/strategy/fromfile/js_package.go: -------------------------------------------------------------------------------- 1 | package fromfile 2 | 3 | import ( 4 | "encoding/json" 5 | "os" 6 | ) 7 | 8 | type JsPackageVersionReader struct { 9 | } 10 | 11 | func (r JsPackageVersionReader) String() string { 12 | return "javascript-package.json" 13 | } 14 | 15 | func (r JsPackageVersionReader) SupportedFiles() []string { 16 | return []string{ 17 | "package.json", 18 | } 19 | } 20 | 21 | func (r JsPackageVersionReader) ReadFileVersion(filePath string) (string, error) { 22 | f, err := os.Open(filePath) 23 | if err != nil { 24 | return "", err 25 | } 26 | defer f.Close() 27 | 28 | var pkg JsPackage 29 | err = json.NewDecoder(f).Decode(&pkg) 30 | if err != nil { 31 | return "", err 32 | } 33 | 34 | if pkg.Version == "" { 35 | return "", ErrFileHasNoVersion 36 | } 37 | 38 | return pkg.Version, nil 39 | } 40 | 41 | type JsPackage struct { 42 | Version string `json:"version"` 43 | } 44 | -------------------------------------------------------------------------------- /.lighthouse/jenkins-x/pullrequest.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: PipelineRun 3 | metadata: 4 | creationTimestamp: null 5 | name: pullrequest 6 | spec: 7 | pipelineSpec: 8 | tasks: 9 | - name: from-build-pack 10 | resources: {} 11 | taskSpec: 12 | metadata: {} 13 | stepTemplate: 14 | image: uses:jenkins-x/jx3-pipeline-catalog/tasks/go-plugin/pullrequest.yaml@versionStream 15 | name: "" 16 | resources: {} 17 | workingDir: /workspace/source 18 | steps: 19 | - image: uses:jenkins-x/jx3-pipeline-catalog/tasks/git-clone/git-clone-pr.yaml@versionStream 20 | name: "" 21 | resources: {} 22 | - name: jx-variables 23 | resources: {} 24 | - name: build-make-test 25 | resources: {} 26 | - name: build-make-linux 27 | resources: {} 28 | - name: build-container-build 29 | resources: {} 30 | podTemplate: {} 31 | serviceAccountName: tekton-bot 32 | timeout: 240h0m0s 33 | status: {} 34 | -------------------------------------------------------------------------------- /pkg/strategy/fromfile/cmake.go: -------------------------------------------------------------------------------- 1 | package fromfile 2 | 3 | import ( 4 | "bufio" 5 | "os" 6 | "regexp" 7 | "strings" 8 | ) 9 | 10 | var ( 11 | cmakeRegexp = regexp.MustCompile(`project\s*(([^\s]+)\s+VERSION\s+([.\d]+(-\w+)?).*)`) 12 | ) 13 | 14 | type CMakeVersionReader struct { 15 | } 16 | 17 | func (r CMakeVersionReader) String() string { 18 | return "cmake" 19 | } 20 | 21 | func (r CMakeVersionReader) SupportedFiles() []string { 22 | return []string{ 23 | "CMakeLists.txt", 24 | } 25 | } 26 | 27 | func (r CMakeVersionReader) ReadFileVersion(filePath string) (string, error) { 28 | f, err := os.Open(filePath) 29 | if err != nil { 30 | return "", err 31 | } 32 | defer f.Close() 33 | 34 | scanner := bufio.NewScanner(f) 35 | for scanner.Scan() { 36 | if strings.Contains(scanner.Text(), " VERSION ") { 37 | matched := cmakeRegexp.FindStringSubmatch(scanner.Text()) 38 | if len(matched) < 4 { 39 | continue 40 | } 41 | v := strings.TrimSpace(matched[3]) 42 | if v != "" { 43 | return v, nil 44 | } 45 | } 46 | } 47 | 48 | return "", ErrFileHasNoVersion 49 | } 50 | -------------------------------------------------------------------------------- /pkg/strategy/fromfile/makefile.go: -------------------------------------------------------------------------------- 1 | package fromfile 2 | 3 | import ( 4 | "bufio" 5 | "os" 6 | "strings" 7 | ) 8 | 9 | type MakefileVersionReader struct { 10 | } 11 | 12 | func (r MakefileVersionReader) String() string { 13 | return "makefile" 14 | } 15 | 16 | func (r MakefileVersionReader) SupportedFiles() []string { 17 | return []string{ 18 | "Makefile", 19 | } 20 | } 21 | 22 | func (r MakefileVersionReader) ReadFileVersion(filePath string) (string, error) { 23 | f, err := os.Open(filePath) 24 | if err != nil { 25 | return "", err 26 | } 27 | defer f.Close() 28 | 29 | scanner := bufio.NewScanner(f) 30 | for scanner.Scan() { 31 | if strings.HasPrefix(scanner.Text(), "VERSION") || strings.HasPrefix(scanner.Text(), "VERSION ") || strings.HasPrefix(scanner.Text(), "VERSION:") || strings.HasPrefix(scanner.Text(), "VERSION=") { 32 | parts := strings.Split(scanner.Text(), "=") 33 | if len(parts) < 2 { 34 | continue 35 | } 36 | 37 | v := strings.TrimSpace(parts[1]) 38 | if v != "" { 39 | return v, nil 40 | } 41 | } 42 | } 43 | 44 | return "", ErrFileHasNoVersion 45 | } 46 | -------------------------------------------------------------------------------- /pkg/strategy/fromfile/testdata/gradle/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * This generated file contains a sample Java Library project to get you started. 5 | * For more details take a look at the Java Libraries chapter in the Gradle 6 | * User Manual available at https://docs.gradle.org/6.1.1/userguide/java_library_plugin.html 7 | */ 8 | 9 | plugins { 10 | // Apply the java-library plugin to add support for Java Library 11 | id 'java-library' 12 | } 13 | 14 | repositories { 15 | // Use jcenter for resolving dependencies. 16 | // You can declare any Maven/Ivy/file repository here. 17 | jcenter() 18 | } 19 | 20 | dependencies { 21 | // This dependency is exported to consumers, that is to say found on their compile classpath. 22 | api 'org.apache.commons:commons-math3:3.6.1' 23 | 24 | // This dependency is used internally, and not exposed to consumers on their own compile classpath. 25 | implementation 'com.google.guava:guava:28.1-jre' 26 | 27 | // Use JUnit test framework 28 | testImplementation 'junit:junit:4.12' 29 | } -------------------------------------------------------------------------------- /pkg/strategy/fromfile/automake.go: -------------------------------------------------------------------------------- 1 | package fromfile 2 | 3 | import ( 4 | "bufio" 5 | "os" 6 | "regexp" 7 | "strings" 8 | ) 9 | 10 | var ( 11 | configureRegexp = regexp.MustCompile(`AC_INIT\s*\(([^\s]+),\s*([.\d]+(-\w+)?).*\)`) 12 | ) 13 | 14 | type AutomakeVersionReader struct { 15 | } 16 | 17 | func (r AutomakeVersionReader) String() string { 18 | return "automake" 19 | } 20 | 21 | func (r AutomakeVersionReader) SupportedFiles() []string { 22 | return []string{ 23 | "configure.ac", 24 | } 25 | } 26 | 27 | func (r AutomakeVersionReader) ReadFileVersion(filePath string) (string, error) { 28 | f, err := os.Open(filePath) 29 | if err != nil { 30 | return "", err 31 | } 32 | defer f.Close() 33 | 34 | scanner := bufio.NewScanner(f) 35 | for scanner.Scan() { 36 | if strings.Contains(scanner.Text(), "AC_INIT") { 37 | matched := configureRegexp.FindStringSubmatch(scanner.Text()) 38 | if len(matched) < 3 { 39 | continue 40 | } 41 | 42 | v := strings.TrimSpace(matched[2]) 43 | if v != "" { 44 | return v, nil 45 | } 46 | } 47 | } 48 | 49 | return "", ErrFileHasNoVersion 50 | } 51 | -------------------------------------------------------------------------------- /pkg/strategy/fromfile/testdata/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * This generated file contains a sample Java Library project to get you started. 5 | * For more details take a look at the Java Libraries chapter in the Gradle 6 | * User Manual available at https://docs.gradle.org/6.1.1/userguide/java_library_plugin.html 7 | */ 8 | 9 | plugins { 10 | // Apply the java-library plugin to add support for Java Library 11 | id 'java-library' 12 | } 13 | 14 | version = '1.2.7' 15 | 16 | repositories { 17 | // Use jcenter for resolving dependencies. 18 | // You can declare any Maven/Ivy/file repository here. 19 | jcenter() 20 | } 21 | 22 | dependencies { 23 | // This dependency is exported to consumers, that is to say found on their compile classpath. 24 | api 'org.apache.commons:commons-math3:3.6.1' 25 | 26 | // This dependency is used internally, and not exposed to consumers on their own compile classpath. 27 | implementation 'com.google.guava:guava:28.1-jre' 28 | 29 | // Use JUnit test framework 30 | testImplementation 'junit:junit:4.12' 31 | } -------------------------------------------------------------------------------- /pkg/strategy/fromfile/gradle.go: -------------------------------------------------------------------------------- 1 | package fromfile 2 | 3 | import ( 4 | "bufio" 5 | "os" 6 | "regexp" 7 | "strings" 8 | ) 9 | 10 | var ( 11 | gradleRegexp = regexp.MustCompile(`^version\s*=\s*['"]([.\d]+(-\w+)?)['"]`) 12 | ) 13 | 14 | type GradleVersionReader struct { 15 | } 16 | 17 | func (r GradleVersionReader) String() string { 18 | return "gradle" 19 | } 20 | 21 | func (r GradleVersionReader) SupportedFiles() []string { 22 | return []string{ 23 | "build.gradle", // groovy syntax 24 | "build.gradle.kts", // kotlin syntax 25 | "gradle.properties", // gradle properties syntax 26 | } 27 | } 28 | 29 | func (r GradleVersionReader) ReadFileVersion(filePath string) (string, error) { 30 | f, err := os.Open(filePath) 31 | if err != nil { 32 | return "", err 33 | } 34 | defer f.Close() 35 | 36 | scanner := bufio.NewScanner(f) 37 | for scanner.Scan() { 38 | if strings.Contains(scanner.Text(), "version") { 39 | matched := gradleRegexp.FindStringSubmatch(scanner.Text()) 40 | if len(matched) < 2 { 41 | continue 42 | } 43 | 44 | v := strings.TrimSpace(matched[1]) 45 | if v != "" { 46 | return v, nil 47 | } 48 | } 49 | } 50 | 51 | return "", ErrFileHasNoVersion 52 | } 53 | -------------------------------------------------------------------------------- /pkg/strategy/fromfile/testdata/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * This generated file contains a sample Kotlin application project to get you started. 5 | */ 6 | 7 | plugins { 8 | // Apply the Kotlin JVM plugin to add support for Kotlin. 9 | id("org.jetbrains.kotlin.jvm") version "1.3.61" 10 | 11 | // Apply the application plugin to add support for building a CLI application. 12 | application 13 | } 14 | 15 | version = "1.2.8" 16 | 17 | repositories { 18 | // Use jcenter for resolving dependencies. 19 | // You can declare any Maven/Ivy/file repository here. 20 | jcenter() 21 | } 22 | 23 | dependencies { 24 | // Align versions of all Kotlin components 25 | implementation(platform("org.jetbrains.kotlin:kotlin-bom")) 26 | 27 | // Use the Kotlin JDK 8 standard library. 28 | implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") 29 | 30 | // Use the Kotlin test library. 31 | testImplementation("org.jetbrains.kotlin:kotlin-test") 32 | 33 | // Use the Kotlin JUnit integration. 34 | testImplementation("org.jetbrains.kotlin:kotlin-test-junit") 35 | } 36 | 37 | application { 38 | // Define the main class for the application. 39 | mainClassName = "demo.AppKt" 40 | } -------------------------------------------------------------------------------- /pkg/strategy/fromfile/helm_chart_test.go: -------------------------------------------------------------------------------- 1 | package fromfile 2 | 3 | import ( 4 | "path/filepath" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestHelmChartVersionReader(t *testing.T) { 12 | t.Parallel() 13 | 14 | tests := []struct { 15 | name string 16 | filePath string 17 | expected string 18 | expectedErrorMsg string 19 | }{ 20 | { 21 | name: "valid file", 22 | filePath: "Chart.yaml", 23 | expected: "1.2.3", 24 | }, 25 | { 26 | name: "file does not exists", 27 | filePath: "does-not-exists.yaml", 28 | expectedErrorMsg: "open testdata/does-not-exists.yaml: no such file or directory", 29 | }, 30 | { 31 | name: "invalid file", 32 | filePath: "setup.py", 33 | expectedErrorMsg: "yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `from se...` into fromfile.HelmChart", 34 | }, 35 | } 36 | 37 | reader := HelmChartVersionReader{} 38 | for i := range tests { 39 | test := tests[i] 40 | t.Run(test.name, func(t *testing.T) { 41 | t.Parallel() 42 | 43 | actual, err := reader.ReadFileVersion(filepath.Join("testdata", test.filePath)) 44 | if len(test.expectedErrorMsg) > 0 { 45 | require.EqualError(t, err, test.expectedErrorMsg) 46 | assert.Empty(t, actual) 47 | } else { 48 | require.NoError(t, err) 49 | assert.Equal(t, test.expected, actual) 50 | } 51 | }) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /pkg/strategy/fromfile/python.go: -------------------------------------------------------------------------------- 1 | package fromfile 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "regexp" 7 | "strings" 8 | ) 9 | 10 | var ( 11 | // pythonRegexp is used to find the call to `setup(..., version='1.2.3', ...)` 12 | pythonSetupRegexp = regexp.MustCompile(`setup\((.|\n)*version\s*=\s*['"]{1}(\d|\.)*['"]{1}([^\)]|\n)*\)`) 13 | // pythonVersionRegexp is used to find the argument `version='1.2.3'` or `version="1.2.3"` 14 | pythonVersionRegexp = regexp.MustCompile(`version\s*=\s*['"]{1}(\d*|\.)*['"]{1}`) 15 | ) 16 | 17 | type PythonVersionReader struct { 18 | } 19 | 20 | func (r PythonVersionReader) String() string { 21 | return "python" 22 | } 23 | 24 | func (r PythonVersionReader) SupportedFiles() []string { 25 | return []string{ 26 | "setup.py", 27 | } 28 | } 29 | 30 | func (r PythonVersionReader) ReadFileVersion(filePath string) (string, error) { 31 | content, err := os.ReadFile(filePath) 32 | if err != nil { 33 | return "", err 34 | } 35 | 36 | setupCall := pythonSetupRegexp.Find(content) 37 | if len(setupCall) == 0 { 38 | return "", fmt.Errorf("setup call not found in file %s", filePath) 39 | } 40 | 41 | version := string(pythonVersionRegexp.Find(setupCall)) 42 | 43 | parts := strings.Split(strings.Replace(version, " ", "", -1), "=") 44 | if len(parts) < 2 { 45 | return "", fmt.Errorf("version value not found in file %s", filePath) 46 | } 47 | 48 | v := strings.TrimPrefix(strings.TrimSuffix(parts[1], "'"), "'") 49 | v = strings.TrimPrefix(strings.TrimSuffix(v, "\""), "\"") 50 | if v == "" { 51 | return "", fmt.Errorf("empty version found in file %s", filePath) 52 | } 53 | 54 | return v, nil 55 | } 56 | -------------------------------------------------------------------------------- /pkg/tag/tag_test.go: -------------------------------------------------------------------------------- 1 | package tag 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "testing" 7 | 8 | "github.com/go-git/go-git/v5" 9 | "github.com/go-git/go-git/v5/plumbing/object" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | const GitUserName string = "test" 14 | const GitUserEmail string = "test@example.com" 15 | 16 | func TestTag(t *testing.T) { 17 | 18 | // setup test 19 | dir := t.TempDir() 20 | 21 | r, err := git.PlainInit(dir, false) 22 | assert.NoError(t, err) 23 | 24 | // Configure git user 25 | cfg, err := r.Config() 26 | assert.NoError(t, err) 27 | 28 | cfg.User.Name = GitUserName 29 | cfg.User.Email = GitUserEmail 30 | 31 | err = r.SetConfig(cfg) 32 | assert.NoError(t, err) 33 | 34 | w, err := r.Worktree() 35 | assert.NoError(t, err) 36 | 37 | filename := filepath.Join(dir, "foo") 38 | err = os.WriteFile(filename, []byte("bar!"), 0644) 39 | assert.NoError(t, err) 40 | 41 | co := &git.CommitOptions{ 42 | All: true, 43 | Author: &object.Signature{Name: GitUserName, Email: GitUserEmail}, 44 | Committer: &object.Signature{Name: GitUserName, Email: GitUserEmail}, 45 | AllowEmptyCommits: true, 46 | } 47 | 48 | _, err = w.Commit("foo\n", co) 49 | assert.NoError(t, err) 50 | 51 | // now run the create tag code 52 | tagOptions := Tag{ 53 | Dir: dir, 54 | PushTag: false, 55 | FormattedVersion: "1.2.3", 56 | } 57 | 58 | err = tagOptions.TagRemote() 59 | assert.NoError(t, err) 60 | 61 | tags, err := r.TagObjects() 62 | assert.NoError(t, err) 63 | 64 | tag, err := tags.Next() 65 | assert.NoError(t, err) 66 | 67 | assert.Equal(t, "1.2.3", tag.Name) 68 | } 69 | -------------------------------------------------------------------------------- /pkg/strategy/auto/auto.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/Masterminds/semver/v3" 7 | "github.com/jenkins-x-plugins/jx-release-version/v2/pkg/strategy/fromtag" 8 | "github.com/jenkins-x-plugins/jx-release-version/v2/pkg/strategy/semantic" 9 | "github.com/jenkins-x/jx-logging/v3/pkg/log" 10 | ) 11 | 12 | type Strategy struct { 13 | FromTagStrategy fromtag.Strategy 14 | SemanticStrategy semantic.Strategy 15 | } 16 | 17 | func (s Strategy) ReadVersion() (*semver.Version, error) { 18 | log.Logger().Debug("Trying to read the previous version from the git tags first...") 19 | v, err := s.FromTagStrategy.ReadVersion() 20 | if err == nil { 21 | return v, nil 22 | } 23 | 24 | if err == fromtag.ErrNoTags || err == fromtag.ErrNoSemverTags { 25 | log.Logger().Debugf("Using fake version 0.0.0 because %s", err) 26 | return semver.MustParse("0.0.0"), nil 27 | } 28 | 29 | return nil, fmt.Errorf("failed to read previous version from tags: %w", err) 30 | } 31 | 32 | func (s Strategy) BumpVersion(previous semver.Version) (*semver.Version, error) { 33 | log.Logger().Debug("Trying to bump the version using semantic release first...") 34 | v, err := s.SemanticStrategy.BumpVersion(previous) 35 | if err == nil { 36 | return v, nil 37 | } 38 | 39 | if err == semantic.ErrPreviousVersionTagNotFound { 40 | log.Logger().Debugf("The git repository has no tag for the previous version %s - fallback to incrementing the patch component of the previous version", previous.String()) 41 | next := previous.IncPatch() 42 | return &next, nil 43 | } 44 | 45 | return nil, fmt.Errorf("failed to bump version %s using semantic strategy: %w", previous.String(), err) 46 | } 47 | -------------------------------------------------------------------------------- /pkg/strategy/fromfile/python_test.go: -------------------------------------------------------------------------------- 1 | package fromfile 2 | 3 | import ( 4 | "path/filepath" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestPythonVersionReader(t *testing.T) { 12 | t.Parallel() 13 | 14 | tests := []struct { 15 | name string 16 | filePath string 17 | expected string 18 | expectedErrorMsg string 19 | }{ 20 | { 21 | name: "standard file", 22 | filePath: "setup.py", 23 | expected: "1.2.11", 24 | }, 25 | { 26 | name: "nested file", 27 | filePath: "setup-nested.py", 28 | expected: "1.2.12", 29 | }, 30 | { 31 | name: "oneline file", 32 | filePath: "setup-oneline.py", 33 | expected: "1.2.13", 34 | }, 35 | { 36 | name: "double quotes", 37 | filePath: "setup-double-quotes.py", 38 | expected: "1.2.14", 39 | }, 40 | { 41 | name: "file does not exists", 42 | filePath: "does-not-exists.yaml", 43 | expectedErrorMsg: "open testdata/does-not-exists.yaml: no such file or directory", 44 | }, 45 | { 46 | name: "invalid file", 47 | filePath: "Chart.yaml", 48 | expectedErrorMsg: "setup call not found in file testdata/Chart.yaml", 49 | }, 50 | } 51 | 52 | reader := PythonVersionReader{} 53 | for i := range tests { 54 | test := tests[i] 55 | t.Run(test.name, func(t *testing.T) { 56 | t.Parallel() 57 | 58 | actual, err := reader.ReadFileVersion(filepath.Join("testdata", test.filePath)) 59 | if len(test.expectedErrorMsg) > 0 { 60 | require.EqualError(t, err, test.expectedErrorMsg) 61 | assert.Empty(t, actual) 62 | } else { 63 | require.NoError(t, err) 64 | assert.Equal(t, test.expected, actual) 65 | } 66 | }) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /pkg/strategy/fromfile/maven_pom.go: -------------------------------------------------------------------------------- 1 | package fromfile 2 | 3 | import ( 4 | "encoding/xml" 5 | "fmt" 6 | "os" 7 | "os/exec" 8 | 9 | "github.com/jenkins-x/jx-logging/v3/pkg/log" 10 | ) 11 | 12 | type MavenPOMVersionReader struct { 13 | } 14 | 15 | func (r MavenPOMVersionReader) String() string { 16 | return "maven POM" 17 | } 18 | 19 | func (r MavenPOMVersionReader) SupportedFiles() []string { 20 | return []string{ 21 | "pom.xml", 22 | } 23 | } 24 | 25 | func (r MavenPOMVersionReader) ReadFileVersion(filePath string) (string, error) { 26 | log.Logger().Debugf("using path %s", os.Getenv("PATH")) 27 | path, err := exec.LookPath("mvn") 28 | if err != nil { 29 | log.Logger().Debugf("Maven does not appear to be installed, reading directly from %s", filePath) 30 | return r.readDirectlyFromPom(filePath) 31 | } 32 | 33 | log.Logger().Debugf("Maven is installed into path %s", path) 34 | 35 | cmd := exec.Command("mvn", 36 | "-f", 37 | filePath, 38 | "-B", // batch mode 39 | "-ntp", // do not display transfer output 40 | "-q", // quiet 41 | "-DforceStdout", // force version to displayed 42 | "-Dexpression=project.version", 43 | "help:evaluate", 44 | ) 45 | 46 | out, err := cmd.CombinedOutput() 47 | if err != nil { 48 | return "", fmt.Errorf("unable to evaluate project.version %s", err) 49 | } 50 | 51 | return string(out), nil 52 | } 53 | 54 | func (r MavenPOMVersionReader) readDirectlyFromPom(filePath string) (string, error) { 55 | f, err := os.Open(filePath) 56 | if err != nil { 57 | return "", err 58 | } 59 | defer f.Close() 60 | 61 | var pom MavenPOM 62 | err = xml.NewDecoder(f).Decode(&pom) 63 | if err != nil { 64 | return "", err 65 | } 66 | 67 | if pom.Version == "" { 68 | return "", ErrFileHasNoVersion 69 | } 70 | 71 | return pom.Version, nil 72 | } 73 | 74 | type MavenPOM struct { 75 | Version string `xml:"version"` 76 | } 77 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | env: 4 | - GO111MODULE=on 5 | - CGO_ENABLED=0 6 | before: 7 | hooks: 8 | - go mod download 9 | 10 | builds: 11 | - id: jx-release-version 12 | # Path to main.go file or main package. 13 | # Default is `.`. 14 | main: ./main.go 15 | 16 | # Binary name. 17 | # Can be a path (e.g. `bin/app`) to wrap the binary in a directory. 18 | # Default is the name of the project directory. 19 | binary: jx-release-version 20 | 21 | # Custom ldflags templates. 22 | # Default is `-s -w -X main.version={{.Version}} -X main.commit={{.ShortCommit}} -X main.date={{.Date}} -X main.builtBy=goreleaser`. 23 | ldflags: 24 | - -X "main.Version={{.Env.VERSION}}" -X "main.Revision={{.Env.REV}}" -X "main.Date={{.Env.BUILDDATE}}" 25 | 26 | # GOOS list to build for. 27 | # For more info refer to: https://golang.org/doc/install/source#environment 28 | # Defaults are darwin and linux. 29 | goos: 30 | - windows 31 | - darwin 32 | - linux 33 | 34 | # GOARCH to build for. 35 | # For more info refer to: https://golang.org/doc/install/source#environment 36 | # Defaults are 386 and amd64. 37 | goarch: 38 | - amd64 39 | - arm64 40 | ignore: 41 | - goos: windows 42 | goarch: arm64 43 | archives: 44 | - name_template: "jx-release-version-{{ .Os }}-{{ .Arch }}" 45 | format_overrides: 46 | - goos: windows 47 | format: zip 48 | 49 | checksum: 50 | # You can change the name of the checksums file. 51 | # Default is `jx-cli_{{ .Version }}_checksums.txt`. 52 | name_template: "jx-release-version-checksums.txt" 53 | 54 | # Algorithm to be used. 55 | # Accepted options are sha256, sha512, sha1, crc32, md5, sha224 and sha384. 56 | # Default is sha256. 57 | algorithm: sha256 58 | changelog: 59 | disable: true 60 | release: 61 | # You can change the name of the GitHub release. 62 | # Default is `{{.Tag}}` 63 | name_template: "{{.Env.VERSION}}" 64 | 65 | sboms: 66 | - artifacts: archive 67 | -------------------------------------------------------------------------------- /pkg/strategy/increment/increment_test.go: -------------------------------------------------------------------------------- 1 | package increment 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/Masterminds/semver/v3" 7 | "github.com/stretchr/testify/assert" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestBumpVersion(t *testing.T) { 12 | t.Parallel() 13 | 14 | tests := []struct { 15 | name string 16 | componentToIncrement string 17 | previous semver.Version 18 | expected *semver.Version 19 | expectedErrorMsg string 20 | }{ 21 | { 22 | name: "increment major", 23 | componentToIncrement: "major", 24 | previous: *semver.MustParse("1.2.3"), 25 | expected: semver.MustParse("2.0.0"), 26 | }, 27 | { 28 | name: "increment minor", 29 | componentToIncrement: "minor", 30 | previous: *semver.MustParse("1.2.3"), 31 | expected: semver.MustParse("1.3.0"), 32 | }, 33 | { 34 | name: "increment patch", 35 | componentToIncrement: "patch", 36 | previous: *semver.MustParse("1.2.3"), 37 | expected: semver.MustParse("1.2.4"), 38 | }, 39 | { 40 | name: "increment patch by default", 41 | componentToIncrement: "", 42 | previous: *semver.MustParse("1.2.3"), 43 | expected: semver.MustParse("1.2.4"), 44 | }, 45 | { 46 | name: "case insensitive", 47 | componentToIncrement: "MiNoR", 48 | previous: *semver.MustParse("1.2.3"), 49 | expected: semver.MustParse("1.3.0"), 50 | }, 51 | } 52 | 53 | for i := range tests { 54 | test := tests[i] 55 | t.Run(test.name, func(t *testing.T) { 56 | t.Parallel() 57 | 58 | s := Strategy{ 59 | ComponentToIncrement: test.componentToIncrement, 60 | } 61 | actual, err := s.BumpVersion(test.previous) 62 | if len(test.expectedErrorMsg) > 0 { 63 | require.EqualError(t, err, test.expectedErrorMsg) 64 | assert.Nil(t, actual) 65 | } else { 66 | require.NoError(t, err) 67 | assert.Equal(t, test.expected, actual) 68 | } 69 | }) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NAME := jx-release-version 2 | ORG := jenkins-x-plugins 3 | ORG_REPO := $(ORG)/$(NAME) 4 | ROOT_PACKAGE := github.com/$(ORG_REPO)/v2 5 | MAIN_SRC_FILE := main.go 6 | 7 | GO := GO15VENDOREXPERIMENT=1 go 8 | CGO_ENABLED = 0 9 | 10 | PACKAGE_DIRS := $(shell $(GO) list ./... | grep -v /vendor/) 11 | FORMATTED := $(shell $(GO) fmt $(PACKAGE_DIRS)) 12 | 13 | # set dev version unless VERSION is explicitly set via environment 14 | VERSION ?= $(shell echo "$$(git for-each-ref refs/tags/ --count=1 --sort=-version:refname --format='%(refname:short)' 2>/dev/null)-dev+$(REV)" | sed 's/^v//') 15 | 16 | REV := $(shell git rev-parse --short HEAD 2> /dev/null || echo 'unknown') 17 | BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2> /dev/null || echo 'unknown') 18 | BUILD_DATE := $(shell date +%Y%m%d-%H:%M:%S) 19 | GO_VERSION := $(shell $(GO) version | sed -e 's/^[^0-9.]*\([0-9.]*\).*/\1/') 20 | 21 | GOOS ?= $(shell go env GOOS) 22 | GOARCH ?= $(shell go env GOARCH) 23 | BUILD_DIR ?= ./bin 24 | BUILDFLAGS := -ldflags \ 25 | " -X main.Version=$(VERSION)\ 26 | -X main.Revision='$(REV)'\ 27 | -X main.Date='$(BUILD_DATE)'" 28 | 29 | all: test build 30 | 31 | check: fmt test 32 | 33 | .PHONY: build 34 | build: 35 | CGO_ENABLED=0 GOARCH=amd64 go build $(BUILDFLAGS) -o $(BUILD_DIR)/$(NAME) $(MAIN_SRC_FILE) 36 | 37 | linux: ## Build for Linux 38 | CGO_ENABLED=$(CGO_ENABLED) GOOS=linux GOARCH=amd64 go build $(BUILDFLAGS) -o build/linux/$(NAME) $(MAIN_SRC_FILE) 39 | chmod +x build/linux/$(NAME) 40 | 41 | fmt: 42 | @FORMATTED=`$(GO) fmt $(PACKAGE_DIRS)` 43 | @([[ ! -z "$(FORMATTED)" ]] && printf "Fixed unformatted files:\n$(FORMATTED)") || true 44 | 45 | .PHONY: test 46 | test: 47 | go test -v ./... 48 | 49 | .PHONY: release 50 | release: clean test linux 51 | 52 | .PHONY: goreleaser 53 | goreleaser: 54 | step-go-releaser --organisation=$(ORG) --revision=$(REV) --branch=$(BRANCH) --build-date=$(BUILD_DATE) --go-version=$(GO_VERSION) --root-package=$(ROOT_PACKAGE) --version=$(VERSION) 55 | 56 | .PHONY: clean 57 | clean: 58 | rm -rf $(BUILD_DIR) 59 | rm -rf dist 60 | 61 | .PHONY: docker 62 | docker: $(BUILD_DIR)/$(NAME)-linux 63 | docker build -t "${ORG}/$(NAME):dev" . 64 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions 2 | name: 'jx-release-version' 3 | description: 'https://github.com/jenkins-x-plugins/jx-release-version' 4 | branding: 5 | icon: 'tag' 6 | color: 'blue' 7 | inputs: 8 | previous-version: 9 | description: 'The strategy to detect the previous version: auto, from-tag, from-file or manual' 10 | required: false 11 | default: 'auto' 12 | next-version: 13 | description: 'The strategy to calculate the next version: auto, semantic, from-file, increment or manual' 14 | required: false 15 | default: 'auto' 16 | output-format: 17 | description: 'The output format of the next version' 18 | required: false 19 | default: '{{.Major}}.{{.Minor}}.{{.Patch}}' 20 | tag: 21 | description: 'If enabled, a new tag will be created' 22 | required: false 23 | default: 'false' 24 | tag-prefix: 25 | description: 'The prefix for the new tag - prefixed before the output' 26 | required: false 27 | default: 'v' 28 | push-tag: 29 | description: 'If enabled, the new tag will be pushed to the `origin` remote' 30 | required: false 31 | default: 'true' 32 | github-token: 33 | description: 'The github token used to push the tag' 34 | required: false 35 | default: '' 36 | git-user: 37 | description: 'If you want to override the name of the author/committer of the tag' 38 | required: false 39 | default: '' 40 | git-email: 41 | description: 'If you want to override the email of the author/committer of the tag' 42 | required: false 43 | default: '' 44 | outputs: 45 | version: 46 | description: 'The next release version' 47 | runs: 48 | using: 'docker' 49 | image: 'docker://ghcr.io/jenkins-x/jx-release-version:2.7.10' 50 | entrypoint: 'github-actions-entrypoint.sh' 51 | env: 52 | PREVIOUS_VERSION: ${{ inputs.previous-version }} 53 | NEXT_VERSION: ${{ inputs.next-version }} 54 | OUTPUT_FORMAT: ${{ inputs.output-format }} 55 | TAG: ${{ inputs.tag }} 56 | TAG_PREFIX: ${{ inputs.tag-prefix }} 57 | PUSH_TAG: ${{ inputs.push-tag }} 58 | GIT_TOKEN: ${{ inputs.github-token }} 59 | GIT_NAME: ${{ inputs.git-user }} 60 | GIT_EMAIL: ${{ inputs.git-email }} -------------------------------------------------------------------------------- /pkg/tag/tag.go: -------------------------------------------------------------------------------- 1 | package tag 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "time" 8 | 9 | "github.com/go-git/go-git/v5" 10 | "github.com/go-git/go-git/v5/config" 11 | "github.com/go-git/go-git/v5/plumbing/object" 12 | "github.com/go-git/go-git/v5/plumbing/transport/http" 13 | "github.com/jenkins-x/jx-logging/v3/pkg/log" 14 | ) 15 | 16 | type Tag struct { 17 | FormattedVersion string 18 | Dir string 19 | PushTag bool 20 | GitName string 21 | GitEmail string 22 | } 23 | 24 | func (options Tag) TagRemote() error { 25 | var err error 26 | if options.Dir == "" { 27 | options.Dir, err = os.Getwd() 28 | if err != nil { 29 | return fmt.Errorf("failed to get current working directory: %w", err) 30 | } 31 | } 32 | if options.FormattedVersion == "" { 33 | return errors.New("no version to use for tag") 34 | } 35 | 36 | repo, err := git.PlainOpen(options.Dir) 37 | if err != nil { 38 | return fmt.Errorf("failed to open git repository at %q: %w", options.Dir, err) 39 | } 40 | 41 | h, err := repo.Head() 42 | if err != nil { 43 | return fmt.Errorf("failed to get the HEAD reference for git repository at %q: %w", options.Dir, err) 44 | } 45 | 46 | tagOptions := &git.CreateTagOptions{ 47 | Message: fmt.Sprintf("Release version %s", options.FormattedVersion), 48 | } 49 | 50 | // override default git config tagger info 51 | if options.GitName != "" && options.GitEmail != "" { 52 | log.Logger().Debugf("overriding default git tagger config with name %s: email: %s", options.GitName, options.GitEmail) 53 | tagOptions.Tagger = &object.Signature{ 54 | Name: options.GitName, 55 | Email: options.GitEmail, 56 | When: time.Now(), 57 | } 58 | } 59 | 60 | log.Logger().Debugf("git tag -a %s -m %q", options.FormattedVersion, tagOptions.Message) 61 | _, err = repo.CreateTag(options.FormattedVersion, h.Hash(), tagOptions) 62 | if err != nil { 63 | return fmt.Errorf("failed to create tag %q with message %q: %w", options.FormattedVersion, tagOptions.Message, err) 64 | } 65 | 66 | if options.PushTag { 67 | return pushTags(repo) 68 | } 69 | return nil 70 | } 71 | 72 | func pushTags(r *git.Repository) error { 73 | token := os.Getenv("GIT_TOKEN") 74 | 75 | po := &git.PushOptions{ 76 | RemoteName: "origin", 77 | Progress: os.Stderr, 78 | RefSpecs: []config.RefSpec{config.RefSpec("refs/tags/*:refs/tags/*")}, 79 | } 80 | 81 | if token != "" { 82 | user := os.Getenv("GIT_USER") 83 | if user == "" { 84 | user = "abc123" // yes, this can be anything except an empty string 85 | } 86 | po.Auth = &http.BasicAuth{ 87 | Username: user, 88 | Password: token, 89 | } 90 | } 91 | log.Logger().Debug("git push --tags") 92 | err := r.Push(po) 93 | 94 | if err != nil { 95 | if err == git.NoErrAlreadyUpToDate { 96 | log.Logger().Debug("origin remote was up to date, no push done") 97 | return nil 98 | } 99 | return fmt.Errorf("failed to push tags to origin: %w", err) 100 | } 101 | return nil 102 | } 103 | -------------------------------------------------------------------------------- /.lighthouse/jenkins-x/release.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: PipelineRun 3 | metadata: 4 | creationTimestamp: null 5 | name: release 6 | spec: 7 | pipelineSpec: 8 | tasks: 9 | - name: chart 10 | resources: {} 11 | taskSpec: 12 | metadata: {} 13 | stepTemplate: 14 | image: uses:jenkins-x/jx3-pipeline-catalog/tasks/go-plugin/release.yaml@versionStream 15 | name: "" 16 | resources: {} 17 | workingDir: /workspace/source 18 | steps: 19 | - image: uses:jenkins-x/jx3-pipeline-catalog/tasks/git-clone/git-clone.yaml@versionStream 20 | name: "" 21 | resources: {} 22 | - name: next-version 23 | resources: {} 24 | - name: jx-variables 25 | resources: {} 26 | - name: release-binary 27 | resources: {} 28 | - image: uses:jenkins-x/jx3-pipeline-catalog/tasks/build-scan-push/build-scan-push.yaml@versionStream 29 | name: build-container 30 | - image: uses:jenkins-x/jx3-pipeline-catalog/tasks/build-scan-push/build-scan-push.yaml@versionStream 31 | name: push-container 32 | - name: chart-docs 33 | resources: {} 34 | - image: ghcr.io/jenkins-x/jx-boot:3.10.73 35 | name: set-github-action-version 36 | resources: {} 37 | script: | 38 | #!/usr/bin/env sh 39 | source .jx/variables.sh 40 | sed -i -e "s/jx-release-version:[0-9\.]*/jx-release-version:$VERSION/" action.yml 41 | - name: changelog 42 | resources: {} 43 | - image: uses:jenkins-x/jx3-pipeline-catalog/tasks/supply-chain-security/task.yaml@versionStream 44 | name: download-syft 45 | - image: uses:jenkins-x/jx3-pipeline-catalog/tasks/supply-chain-security/task.yaml@versionStream 46 | name: build-and-push-sbom 47 | resources: {} 48 | - name: cleanup-image-tar 49 | image: alpine:3.16 50 | resources: {} 51 | script: | 52 | #!/bin/sh 53 | rm -f /workspace/source/image.tar 54 | - name: upload-binaries 55 | resources: {} 56 | - name: promote-release 57 | resources: {} 58 | - image: ghcr.io/jenkins-x/jx-boot:3.10.73 59 | name: promote-action 60 | resources: {} 61 | script: | 62 | #!/usr/bin/env sh 63 | source .jx/variables.sh 64 | 65 | git clone https://github.com/jenkins-x-plugins/$REPO_NAME update-action 66 | cd update-action 67 | sed -i -e "s|ghcr.io/jenkins-x/$REPO_NAME:.*|ghcr.io/jenkins-x/$REPO_NAME:$VERSION'|" action.yml 68 | sed -i -e "s|ghcr.io/jenkins-x/$REPO_NAME:.*|ghcr.io/jenkins-x/$REPO_NAME:$VERSION|" README.md 69 | sed -i -e "s|jenkins-x-plugins/$REPO_NAME@v.*|jenkins-x-plugins/$REPO_NAME@v$VERSION|g" README.md 70 | 71 | git add * || true 72 | git commit -a -m "chore: upgrade image to $VERSION" --allow-empty 73 | git push 74 | podTemplate: {} 75 | serviceAccountName: tekton-bot 76 | timeout: 1h0m0s 77 | status: {} 78 | -------------------------------------------------------------------------------- /pkg/strategy/fromtag/from_tag.go: -------------------------------------------------------------------------------- 1 | package fromtag 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "regexp" 8 | "sort" 9 | 10 | "github.com/Masterminds/semver/v3" 11 | "github.com/go-git/go-git/v5" 12 | "github.com/go-git/go-git/v5/config" 13 | "github.com/go-git/go-git/v5/plumbing" 14 | "github.com/jenkins-x/jx-logging/v3/pkg/log" 15 | ) 16 | 17 | var ( 18 | ErrNoTags = errors.New("the git repository has no tags") 19 | ErrNoSemverTags = errors.New("the git repository has no semver tags") 20 | ) 21 | 22 | type Strategy struct { 23 | Dir string 24 | TagPattern string 25 | FetchTags bool 26 | } 27 | 28 | func (s Strategy) ReadVersion() (*semver.Version, error) { 29 | var ( 30 | dir = s.Dir 31 | err error 32 | ) 33 | if dir == "" { 34 | dir, err = os.Getwd() 35 | if err != nil { 36 | return nil, fmt.Errorf("failed to get current working directory: %w", err) 37 | } 38 | } 39 | 40 | var tagRegexp *regexp.Regexp 41 | if len(s.TagPattern) > 0 { 42 | tagRegexp, err = regexp.Compile(s.TagPattern) 43 | if err != nil { 44 | return nil, fmt.Errorf("failed to compile tag pattern %q: %w", s.TagPattern, err) 45 | } 46 | } 47 | 48 | repo, err := git.PlainOpen(dir) 49 | if err != nil { 50 | return nil, fmt.Errorf("failed to open git repository at %q: %w", dir, err) 51 | } 52 | 53 | if s.FetchTags { 54 | log.Logger().Debug("Fetching tags from origin") 55 | err = repo.Fetch(&git.FetchOptions{ 56 | RemoteName: "origin", 57 | Progress: os.Stderr, 58 | RefSpecs: []config.RefSpec{config.RefSpec("refs/tags/*:refs/tags/*")}, 59 | }) 60 | if err != nil && !errors.Is(err, git.NoErrAlreadyUpToDate) { 61 | return nil, fmt.Errorf("failed to fetch tags from origin at %q: %w", dir, err) 62 | } 63 | } 64 | 65 | tagIterator, err := repo.Tags() 66 | if err != nil { 67 | return nil, fmt.Errorf("failed to list tags from git repository at %q: %w", dir, err) 68 | } 69 | 70 | var ( 71 | tags int 72 | versions []semver.Version 73 | ) 74 | err = tagIterator.ForEach(func(ref *plumbing.Reference) error { 75 | tags++ 76 | tag := ref.Name().Short() 77 | if tagRegexp != nil && !tagRegexp.MatchString(tag) { 78 | log.Logger().Debugf("Skipping tag %q not matching pattern %q", tag, s.TagPattern) 79 | return nil 80 | } 81 | v, err := semver.NewVersion(tag) 82 | if err != nil { 83 | log.Logger().Debugf("Skipping non-semver tag %q (%s)", tag, err) 84 | return nil 85 | } 86 | versions = append(versions, *v) 87 | return nil 88 | }) 89 | if err != nil { 90 | return nil, fmt.Errorf("failed to iterator over tags from git repository at %q: %w", dir, err) 91 | } 92 | if tags == 0 { 93 | return nil, ErrNoTags 94 | } 95 | if len(versions) == 0 && len(s.TagPattern) == 0 { 96 | return nil, ErrNoSemverTags 97 | } 98 | if len(versions) == 0 { 99 | return nil, fmt.Errorf("no semver tags with pattern %q found", s.TagPattern) 100 | } 101 | log.Logger().Debugf("Found %d semver tags with pattern %q", len(versions), s.TagPattern) 102 | 103 | sort.SliceStable(versions, func(i, j int) bool { 104 | return versions[i].GreaterThan(&versions[j]) 105 | }) 106 | 107 | return &versions[0], nil 108 | } 109 | -------------------------------------------------------------------------------- /pkg/strategy/fromtag/from_tag_test.go: -------------------------------------------------------------------------------- 1 | package fromtag 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "testing" 7 | 8 | "github.com/Masterminds/semver/v3" 9 | "github.com/go-git/go-git/v5" 10 | "github.com/go-git/go-git/v5/config" 11 | archiver "github.com/jm33-m0/arc/v2" 12 | "github.com/stretchr/testify/assert" 13 | "github.com/stretchr/testify/require" 14 | ) 15 | 16 | func TestReadVersion(t *testing.T) { 17 | t.Parallel() 18 | tests := []struct { 19 | name string 20 | strategy Strategy 21 | expected *semver.Version 22 | expectedErrorMsg string 23 | }{ 24 | { 25 | name: "no prefix", 26 | strategy: Strategy{ 27 | Dir: "testdata/git-repo", 28 | }, 29 | expected: semver.MustParse("v2.0.0"), 30 | }, 31 | { 32 | name: "fetch tags first", 33 | strategy: Strategy{ 34 | Dir: "testdata/git-repo", 35 | FetchTags: true, 36 | }, 37 | expected: semver.MustParse("v2.1.0"), 38 | }, 39 | { 40 | name: "v1 prefix", 41 | strategy: Strategy{ 42 | Dir: "testdata/git-repo", 43 | TagPattern: "v1", 44 | }, 45 | expected: semver.MustParse("v1.1.0"), 46 | }, 47 | { 48 | name: "v1.0 prefix", 49 | strategy: Strategy{ 50 | Dir: "testdata/git-repo", 51 | TagPattern: "v1.0", 52 | }, 53 | expected: semver.MustParse("v1.0.1"), 54 | }, 55 | { 56 | name: "v3 prefix", 57 | strategy: Strategy{ 58 | Dir: "testdata/git-repo", 59 | TagPattern: "v3", 60 | }, 61 | expectedErrorMsg: "no semver tags with pattern \"v3\" found", 62 | }, 63 | } 64 | 65 | // the git repo is stored as a tar.gz archive to make it easy to commit 66 | gitRepoPath := filepath.Join("testdata", "git-repo") 67 | err := os.RemoveAll(gitRepoPath) 68 | require.NoErrorf(t, err, "failed to delete %s", gitRepoPath) 69 | err = archiver.Unarchive(filepath.Join("testdata", "git-repo.tar.gz"), gitRepoPath) 70 | require.NoErrorf(t, err, "failed to decompress git repository at %s", gitRepoPath) 71 | // and it has an origin remote repo (with more tags) 72 | gitRepoOriginPath := filepath.Join("testdata", "git-repo-origin") 73 | err = os.RemoveAll(gitRepoOriginPath) 74 | require.NoErrorf(t, err, "failed to delete %s", gitRepoOriginPath) 75 | err = archiver.Unarchive(filepath.Join("testdata", "git-repo-origin.tar.gz"), gitRepoOriginPath) 76 | require.NoErrorf(t, err, "failed to decompress git repository at %s", gitRepoOriginPath) 77 | // link the 2 repos together 78 | repo, err := git.PlainOpen(gitRepoPath) 79 | require.NoErrorf(t, err, "failed to open git repo at %s", gitRepoPath) 80 | _, err = repo.CreateRemote(&config.RemoteConfig{ 81 | Name: "origin", 82 | URLs: []string{gitRepoOriginPath}, 83 | Fetch: []config.RefSpec{config.RefSpec("+refs/heads/*:refs/remotes/origin/*")}, 84 | }) 85 | require.NoError(t, err, "failed to create git remote") 86 | 87 | for i := range tests { 88 | test := tests[i] 89 | t.Run(test.name, func(t *testing.T) { 90 | t.Parallel() 91 | 92 | actual, err := test.strategy.ReadVersion() 93 | if len(test.expectedErrorMsg) > 0 { 94 | require.EqualError(t, err, test.expectedErrorMsg) 95 | assert.Nil(t, actual) 96 | } else { 97 | require.NoError(t, err) 98 | assert.Equal(t, test.expected, actual) 99 | } 100 | }) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/jenkins-x-plugins/jx-release-version/v2 2 | 3 | go 1.23.3 4 | 5 | toolchain go1.24.4 6 | 7 | require ( 8 | github.com/Masterminds/semver/v3 v3.4.0 9 | github.com/Masterminds/sprig/v3 v3.3.0 10 | github.com/go-git/go-git/v5 v5.16.2 11 | github.com/jenkins-x/jx-logging/v3 v3.1.3 12 | github.com/jm33-m0/arc/v2 v2.0.1 13 | github.com/stretchr/testify v1.10.0 14 | github.com/zbindenren/cc v0.4.4 15 | gopkg.in/yaml.v3 v3.0.1 16 | ) 17 | 18 | require ( 19 | dario.cat/mergo v1.0.2 // indirect 20 | github.com/Masterminds/goutils v1.1.1 // indirect 21 | github.com/Microsoft/go-winio v0.6.2 // indirect 22 | github.com/ProtonMail/go-crypto v1.3.0 // indirect 23 | github.com/STARRY-S/zip v0.2.2 // indirect 24 | github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3 // indirect 25 | github.com/bbuck/go-lexer v1.0.0 // indirect 26 | github.com/bodgit/plumbing v1.3.0 // indirect 27 | github.com/bodgit/sevenzip v1.6.0 // indirect 28 | github.com/bodgit/windows v1.0.1 // indirect 29 | github.com/cloudflare/circl v1.6.1 // indirect 30 | github.com/cyphar/filepath-securejoin v0.4.1 // indirect 31 | github.com/davecgh/go-spew v1.1.1 // indirect 32 | github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect 33 | github.com/emirpasic/gods v1.18.1 // indirect 34 | github.com/fatih/color v1.18.0 // indirect 35 | github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect 36 | github.com/go-git/go-billy/v5 v5.6.2 // indirect 37 | github.com/go-stack/stack v1.8.1 // indirect 38 | github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect 39 | github.com/google/uuid v1.6.0 // indirect 40 | github.com/hashicorp/errwrap v1.1.0 // indirect 41 | github.com/hashicorp/go-multierror v1.1.1 // indirect 42 | github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect 43 | github.com/huandu/xstrings v1.5.0 // indirect 44 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect 45 | github.com/jenkins-x/logrus-stackdriver-formatter v0.2.8 // indirect 46 | github.com/kevinburke/ssh_config v1.2.0 // indirect 47 | github.com/klauspost/compress v1.18.0 // indirect 48 | github.com/klauspost/pgzip v1.2.6 // indirect 49 | github.com/mattn/go-colorable v0.1.13 // indirect 50 | github.com/mattn/go-isatty v0.0.20 // indirect 51 | github.com/mholt/archives v0.1.0 // indirect 52 | github.com/mitchellh/copystructure v1.2.0 // indirect 53 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 54 | github.com/nwaples/rardecode/v2 v2.1.0 // indirect 55 | github.com/pierrec/lz4/v4 v4.1.22 // indirect 56 | github.com/pjbgf/sha1cd v0.4.0 // indirect 57 | github.com/pmezard/go-difflib v1.0.0 // indirect 58 | github.com/sergi/go-diff v1.4.0 // indirect 59 | github.com/shopspring/decimal v1.4.0 // indirect 60 | github.com/sirupsen/logrus v1.9.3 // indirect 61 | github.com/skeema/knownhosts v1.3.1 // indirect 62 | github.com/sorairolake/lzip-go v0.3.5 // indirect 63 | github.com/spf13/cast v1.9.2 // indirect 64 | github.com/therootcompany/xz v1.0.1 // indirect 65 | github.com/ulikunitz/xz v0.5.12 // indirect 66 | github.com/xanzy/ssh-agent v0.3.3 // indirect 67 | go4.org v0.0.0-20230225012048-214862532bf5 // indirect 68 | golang.org/x/crypto v0.40.0 // indirect 69 | golang.org/x/net v0.42.0 // indirect 70 | golang.org/x/sys v0.34.0 // indirect 71 | golang.org/x/text v0.27.0 // indirect 72 | gopkg.in/warnings.v0 v0.1.2 // indirect 73 | ) 74 | -------------------------------------------------------------------------------- /pkg/strategy/fromfile/from_file.go: -------------------------------------------------------------------------------- 1 | package fromfile 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | 10 | "github.com/Masterminds/semver/v3" 11 | "github.com/jenkins-x/jx-logging/v3/pkg/log" 12 | ) 13 | 14 | var ( 15 | ErrFileHasNoVersion = errors.New("the file has no version") 16 | ) 17 | 18 | type Strategy struct { 19 | Dir string 20 | FilePath string 21 | } 22 | 23 | func (s Strategy) ReadVersion() (*semver.Version, error) { 24 | var ( 25 | dir = s.Dir 26 | err error 27 | ) 28 | if dir == "" { 29 | dir, err = os.Getwd() 30 | if err != nil { 31 | return nil, fmt.Errorf("failed to get current working directory: %w", err) 32 | } 33 | } 34 | 35 | var ( 36 | reader FileVersionReader 37 | filePaths []string 38 | ) 39 | if len(s.FilePath) > 0 { 40 | filePath := filepath.Join(dir, s.FilePath) 41 | filePaths = append(filePaths, filePath) 42 | reader, err = s.getReader() 43 | } else { 44 | reader, filePaths, err = s.autoDetect(dir) 45 | } 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | var version string 51 | for _, filePath := range filePaths { 52 | log.Logger().Debugf("Reading version from file %s using reader %s", filePath, reader) 53 | version, err = reader.ReadFileVersion(filePath) 54 | if errors.Is(err, ErrFileHasNoVersion) { 55 | log.Logger().Debugf("File %s has no version", filePath) 56 | continue 57 | } 58 | if err != nil { 59 | return nil, err 60 | } 61 | if version != "" { 62 | break 63 | } 64 | } 65 | 66 | if version == "" { 67 | return nil, fmt.Errorf("could not find version from %s using reader %s", filePaths, reader) 68 | } 69 | 70 | log.Logger().Debugf("Found version %s", version) 71 | return semver.NewVersion(version) 72 | } 73 | 74 | func (s Strategy) BumpVersion(_ semver.Version) (*semver.Version, error) { 75 | return s.ReadVersion() 76 | } 77 | 78 | func (s Strategy) autoDetect(dir string) (FileVersionReader, []string, error) { 79 | for _, reader := range fileVersionReaders { 80 | var filePaths []string 81 | for _, fileName := range reader.SupportedFiles() { 82 | filePath := filepath.Join(dir, fileName) 83 | if _, err := os.Stat(filePath); err == nil { 84 | log.Logger().Debugf("Adding file %s as a candidate to read version using %s reader", filePath, reader.String()) 85 | filePaths = append(filePaths, filePath) 86 | } 87 | } 88 | if len(filePaths) > 0 { 89 | return reader, filePaths, nil 90 | } 91 | } 92 | 93 | return nil, nil, fmt.Errorf("could not find a file to read version from, in directory %s", dir) 94 | } 95 | 96 | func (s Strategy) getReader() (FileVersionReader, error) { 97 | for _, reader := range fileVersionReaders { 98 | for _, fileName := range reader.SupportedFiles() { 99 | if strings.HasSuffix(s.FilePath, fileName) { 100 | return reader, nil 101 | } 102 | } 103 | } 104 | 105 | return nil, fmt.Errorf("could not find a file version reader for %s", s.FilePath) 106 | } 107 | 108 | type FileVersionReader interface { 109 | ReadFileVersion(filePath string) (string, error) 110 | SupportedFiles() []string 111 | String() string 112 | } 113 | 114 | // fileVersionReaders is an ordered list of all readers to try 115 | // when auto-detecting the file to use 116 | var fileVersionReaders = []FileVersionReader{ 117 | HelmChartVersionReader{}, 118 | MakefileVersionReader{}, 119 | AutomakeVersionReader{}, 120 | CMakeVersionReader{}, 121 | PythonVersionReader{}, 122 | MavenPOMVersionReader{}, 123 | JsPackageVersionReader{}, 124 | GradleVersionReader{}, 125 | } 126 | -------------------------------------------------------------------------------- /pkg/strategy/semantic/semantic_test.go: -------------------------------------------------------------------------------- 1 | package semantic 2 | 3 | import ( 4 | archiver "github.com/jm33-m0/arc/v2" 5 | "os" 6 | "path/filepath" 7 | "testing" 8 | 9 | "github.com/Masterminds/semver/v3" 10 | "github.com/stretchr/testify/assert" 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | const RepositoryDirPath string = "testdata/git-repo" 15 | 16 | func TestBumpVersion(t *testing.T) { 17 | t.Parallel() 18 | tests := []struct { 19 | name string 20 | strategy Strategy 21 | previous semver.Version 22 | expected *semver.Version 23 | expectedErrorMsg string 24 | }{ 25 | { 26 | name: "feat commit", 27 | strategy: Strategy{ 28 | Dir: RepositoryDirPath, 29 | TagPrefix: "v", 30 | }, 31 | previous: *semver.MustParse("2.0.0"), 32 | expected: semver.MustParse("2.1.0"), 33 | }, 34 | { 35 | name: "breaking change", 36 | strategy: Strategy{ 37 | Dir: RepositoryDirPath, 38 | TagPrefix: "v", 39 | }, 40 | previous: *semver.MustParse("1.1.0"), 41 | expected: semver.MustParse("2.0.0"), 42 | }, 43 | { 44 | name: "feat from commit headline", 45 | strategy: Strategy{ 46 | CommitHeadlinesString: "feat: a feature", 47 | TagPrefix: "v", 48 | }, 49 | previous: *semver.MustParse("2.0.0"), 50 | expected: semver.MustParse("2.1.0"), 51 | }, 52 | { 53 | name: "feat from commit headlines", 54 | strategy: Strategy{ 55 | CommitHeadlinesString: `chore: a chore 56 | TagPrefix: "v", 57 | feat: a feature`, 58 | }, 59 | previous: *semver.MustParse("2.0.0"), 60 | expected: semver.MustParse("2.1.0"), 61 | }, 62 | { 63 | name: "breaking change from commit headline", 64 | strategy: Strategy{ 65 | CommitHeadlinesString: "feat!: a breaking feature", 66 | TagPrefix: "v", 67 | }, 68 | previous: *semver.MustParse("1.1.0"), 69 | expected: semver.MustParse("2.0.0"), 70 | }, 71 | { 72 | name: "breaking change from commit headlines", 73 | strategy: Strategy{ 74 | CommitHeadlinesString: `chore: a chore 75 | feat!: a breaking feature`, 76 | TagPrefix: "v", 77 | }, 78 | previous: *semver.MustParse("1.1.0"), 79 | expected: semver.MustParse("2.0.0"), 80 | }, 81 | { 82 | name: "patch from unrecognized commit headline", 83 | strategy: Strategy{ 84 | CommitHeadlinesString: "nothing", 85 | TagPrefix: "v", 86 | }, 87 | previous: *semver.MustParse("1.1.0"), 88 | expected: semver.MustParse("1.1.1"), 89 | }, 90 | { 91 | name: "feat commit with prefix", 92 | strategy: Strategy{ 93 | Dir: RepositoryDirPath, 94 | TagPrefix: "vprefix-", 95 | }, 96 | previous: *semver.MustParse("2.0.0"), 97 | expected: semver.MustParse("2.1.0"), 98 | }, 99 | { 100 | name: "breaking change with prefix", 101 | strategy: Strategy{ 102 | Dir: RepositoryDirPath, 103 | TagPrefix: "vprefix-", 104 | }, 105 | previous: *semver.MustParse("1.1.0"), 106 | expected: semver.MustParse("2.0.0"), 107 | }, 108 | } 109 | 110 | // the git repo is stored as a tar.gz archive to make it easy to commit 111 | gitRepoPath := filepath.Join("testdata", "git-repo") 112 | err := os.RemoveAll(gitRepoPath) 113 | require.NoErrorf(t, err, "failed to delete %s", gitRepoPath) 114 | err = archiver.Unarchive(filepath.Join("testdata", "git-repo.tar.gz"), gitRepoPath) 115 | require.NoErrorf(t, err, "failed to decompress git repository at %s", gitRepoPath) 116 | 117 | for i := range tests { 118 | test := tests[i] 119 | t.Run(test.name, func(t *testing.T) { 120 | t.Parallel() 121 | 122 | actual, err := test.strategy.BumpVersion(test.previous) 123 | if len(test.expectedErrorMsg) > 0 { 124 | require.EqualError(t, err, test.expectedErrorMsg) 125 | assert.Nil(t, actual) 126 | } else { 127 | require.NoError(t, err) 128 | assert.Equal(t, test.expected, actual) 129 | } 130 | }) 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /pkg/strategy/auto/auto_test.go: -------------------------------------------------------------------------------- 1 | package auto 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "testing" 8 | 9 | "github.com/Masterminds/semver/v3" 10 | "github.com/jenkins-x-plugins/jx-release-version/v2/pkg/strategy/fromtag" 11 | "github.com/jenkins-x-plugins/jx-release-version/v2/pkg/strategy/semantic" 12 | "github.com/jm33-m0/arc/v2" 13 | "github.com/stretchr/testify/assert" 14 | "github.com/stretchr/testify/require" 15 | ) 16 | 17 | func TestReadVersion(t *testing.T) { 18 | tests := []struct { 19 | name string 20 | strategy Strategy 21 | expected *semver.Version 22 | expectedErrorMsg string 23 | }{ 24 | { 25 | name: "empty git repo", 26 | strategy: Strategy{ 27 | FromTagStrategy: fromtag.Strategy{ 28 | Dir: "testdata/empty-git-repo", 29 | }, 30 | }, 31 | expected: semver.MustParse("0.0.0"), 32 | }, 33 | { 34 | name: "non-empty git repo", 35 | strategy: Strategy{ 36 | FromTagStrategy: fromtag.Strategy{ 37 | Dir: "testdata/git-repo", 38 | }, 39 | }, 40 | expected: semver.MustParse("v2.0.0"), 41 | }, 42 | { 43 | name: "repo with merge commit", 44 | strategy: Strategy{ 45 | FromTagStrategy: fromtag.Strategy{ 46 | Dir: "testdata/repo-with-merge-commit", 47 | }, 48 | }, 49 | expected: semver.MustParse("1.0.0"), 50 | }, 51 | } 52 | 53 | setupGitRepos(t) 54 | 55 | for i := range tests { 56 | test := tests[i] 57 | t.Run(test.name, func(t *testing.T) { 58 | t.Parallel() 59 | 60 | actual, err := test.strategy.ReadVersion() 61 | if len(test.expectedErrorMsg) > 0 { 62 | require.EqualError(t, err, test.expectedErrorMsg) 63 | assert.Nil(t, actual) 64 | } else { 65 | require.NoError(t, err) 66 | assert.Equal(t, test.expected, actual) 67 | } 68 | }) 69 | } 70 | } 71 | 72 | func TestBumpVersion(t *testing.T) { 73 | tests := []struct { 74 | name string 75 | strategy Strategy 76 | previous semver.Version 77 | expected *semver.Version 78 | expectedErrorMsg string 79 | }{ 80 | { 81 | name: "empty git repo", 82 | strategy: Strategy{ 83 | SemanticStrategy: semantic.Strategy{ 84 | Dir: "testdata/empty-git-repo", 85 | TagPrefix: "v", 86 | }, 87 | }, 88 | previous: *semver.MustParse("1.0.0"), 89 | expected: semver.MustParse("1.0.1"), 90 | }, 91 | { 92 | name: "non-empty git repo", 93 | strategy: Strategy{ 94 | SemanticStrategy: semantic.Strategy{ 95 | Dir: "testdata/git-repo", 96 | TagPrefix: "v", 97 | }, 98 | }, 99 | previous: *semver.MustParse("1.0.0"), 100 | expected: semver.MustParse("2.0.0"), 101 | }, 102 | { 103 | name: "repo with merge commit", 104 | strategy: Strategy{ 105 | SemanticStrategy: semantic.Strategy{ 106 | Dir: "testdata/repo-with-merge-commit", 107 | TagPrefix: "v", 108 | }, 109 | }, 110 | previous: *semver.MustParse("1.0.0"), 111 | expected: semver.MustParse("1.1.0"), 112 | }, 113 | } 114 | 115 | setupGitRepos(t) 116 | 117 | for i := range tests { 118 | test := tests[i] 119 | t.Run(test.name, func(t *testing.T) { 120 | //t.Parallel() 121 | 122 | actual, err := test.strategy.BumpVersion(test.previous) 123 | if len(test.expectedErrorMsg) > 0 { 124 | require.EqualError(t, err, test.expectedErrorMsg) 125 | assert.Nil(t, actual) 126 | } else { 127 | require.NoError(t, err) 128 | assert.Equal(t, test.expected, actual) 129 | } 130 | }) 131 | } 132 | } 133 | 134 | func setupGitRepos(t *testing.T) { 135 | // the git repos are stored as a tar.gz archive to make it easy to commit 136 | for _, repoName := range []string{"git-repo", "empty-git-repo", "repo-with-merge-commit"} { 137 | gitRepoPath := filepath.Join("testdata", repoName) 138 | err := os.RemoveAll(gitRepoPath) 139 | require.NoErrorf(t, err, "failed to delete %s", gitRepoPath) 140 | err = arc.Unarchive(filepath.Join("testdata", fmt.Sprintf("%s.tar.gz", repoName)), gitRepoPath) 141 | require.NoErrorf(t, err, "failed to decompress git repository at %s", gitRepoPath) 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /pkg/strategy/fromfile/from_file_test.go: -------------------------------------------------------------------------------- 1 | package fromfile 2 | 3 | import ( 4 | "path/filepath" 5 | "testing" 6 | 7 | "github.com/Masterminds/semver/v3" 8 | "github.com/stretchr/testify/assert" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestReadVersion(t *testing.T) { 13 | t.Parallel() 14 | 15 | tests := []struct { 16 | name string 17 | strategy Strategy 18 | expected *semver.Version 19 | expectedErrorMsg string 20 | }{ 21 | { 22 | name: "auto detect", 23 | strategy: Strategy{ 24 | Dir: "testdata", 25 | }, 26 | expected: semver.MustParse("1.2.3"), 27 | }, 28 | { 29 | name: "auto detect gradle", 30 | strategy: Strategy{ 31 | Dir: filepath.Join("testdata", "gradle"), 32 | }, 33 | expected: semver.MustParse("1.2.7"), 34 | }, 35 | { 36 | name: "Helm Chart", 37 | strategy: Strategy{ 38 | Dir: "testdata", 39 | FilePath: "Chart.yaml", 40 | }, 41 | expected: semver.MustParse("1.2.3"), 42 | }, 43 | { 44 | name: "Helm Chart with dir in filePath", 45 | strategy: Strategy{ 46 | Dir: "", 47 | FilePath: filepath.Join("testdata", "Chart.yaml"), 48 | }, 49 | expected: semver.MustParse("1.2.3"), 50 | }, 51 | { 52 | name: "Makefile", 53 | strategy: Strategy{ 54 | Dir: "testdata", 55 | FilePath: "Makefile", 56 | }, 57 | expected: semver.MustParse("1.2.4"), 58 | }, 59 | { 60 | name: "Automake", 61 | strategy: Strategy{ 62 | Dir: "testdata", 63 | FilePath: "configure.ac", 64 | }, 65 | expected: semver.MustParse("1.2.5"), 66 | }, 67 | { 68 | name: "CMake", 69 | strategy: Strategy{ 70 | Dir: "testdata", 71 | FilePath: "CMakeLists.txt", 72 | }, 73 | expected: semver.MustParse("1.2.6"), 74 | }, 75 | { 76 | name: "Python", 77 | strategy: Strategy{ 78 | Dir: "testdata", 79 | FilePath: "setup.py", 80 | }, 81 | expected: semver.MustParse("1.2.11"), 82 | }, 83 | { 84 | name: "Maven POM", 85 | strategy: Strategy{ 86 | Dir: "testdata", 87 | FilePath: "pom.xml", 88 | }, 89 | expected: semver.MustParse("1.2.9"), 90 | }, 91 | { 92 | name: "Javascript package.json", 93 | strategy: Strategy{ 94 | Dir: "testdata", 95 | FilePath: "package.json", 96 | }, 97 | expected: semver.MustParse("1.2.10"), 98 | }, 99 | { 100 | name: "Gradle (groovy)", 101 | strategy: Strategy{ 102 | Dir: "testdata", 103 | FilePath: "build.gradle", 104 | }, 105 | expected: semver.MustParse("1.2.7"), 106 | }, 107 | { 108 | name: "Gradle Properties", 109 | strategy: Strategy{ 110 | Dir: "testdata", 111 | FilePath: "gradle.properties", 112 | }, 113 | expected: semver.MustParse("1.2.7"), 114 | }, 115 | { 116 | name: "Gradle (kotlin)", 117 | strategy: Strategy{ 118 | Dir: "testdata", 119 | FilePath: "build.gradle.kts", 120 | }, 121 | expected: semver.MustParse("1.2.8"), 122 | }, 123 | { 124 | name: "unknown file", 125 | strategy: Strategy{ 126 | Dir: "testdata", 127 | FilePath: "something.else", 128 | }, 129 | expectedErrorMsg: "could not find a file version reader for something.else", 130 | }, 131 | } 132 | 133 | for i := range tests { 134 | test := tests[i] 135 | t.Run(test.name, func(t *testing.T) { 136 | t.Parallel() 137 | 138 | actual, err := test.strategy.ReadVersion() 139 | if len(test.expectedErrorMsg) > 0 { 140 | require.EqualError(t, err, test.expectedErrorMsg) 141 | assert.Nil(t, actual) 142 | } else { 143 | require.NoError(t, err) 144 | assert.Equal(t, test.expected, actual) 145 | } 146 | }) 147 | } 148 | } 149 | 150 | func TestAutoDetect(t *testing.T) { 151 | t.Parallel() 152 | 153 | tests := []struct { 154 | name string 155 | dir string 156 | expectedReader FileVersionReader 157 | expectedFilePaths []string 158 | expectedErrorMsg string 159 | }{ 160 | { 161 | name: "testdata", 162 | dir: "testdata", 163 | expectedReader: HelmChartVersionReader{}, 164 | expectedFilePaths: []string{"testdata/Chart.yaml"}, 165 | }, 166 | { 167 | name: "multiple matches", 168 | dir: "testdata/gradle", 169 | expectedReader: GradleVersionReader{}, 170 | expectedFilePaths: []string{ 171 | "testdata/gradle/build.gradle", 172 | "testdata/gradle/gradle.properties", 173 | }, 174 | }, 175 | { 176 | name: "no match", 177 | dir: ".", 178 | expectedReader: nil, 179 | expectedErrorMsg: "could not find a file to read version from, in directory .", 180 | }, 181 | } 182 | 183 | for i := range tests { 184 | test := tests[i] 185 | t.Run(test.name, func(t *testing.T) { 186 | t.Parallel() 187 | 188 | s := Strategy{ 189 | Dir: test.dir, 190 | } 191 | 192 | actualReader, actualFilePaths, err := s.autoDetect(test.dir) 193 | if len(test.expectedErrorMsg) > 0 { 194 | require.EqualError(t, err, test.expectedErrorMsg) 195 | assert.Nil(t, actualReader) 196 | assert.Empty(t, actualFilePaths) 197 | } else { 198 | require.NoError(t, err) 199 | assert.Equal(t, test.expectedReader, actualReader) 200 | assert.Equal(t, test.expectedFilePaths, actualFilePaths) 201 | } 202 | }) 203 | } 204 | 205 | } 206 | -------------------------------------------------------------------------------- /pkg/strategy/semantic/semantic.go: -------------------------------------------------------------------------------- 1 | package semantic 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io" 7 | "os" 8 | "regexp" 9 | 10 | "github.com/Masterminds/semver/v3" 11 | "github.com/go-git/go-git/v5" 12 | "github.com/go-git/go-git/v5/plumbing" 13 | "github.com/go-git/go-git/v5/plumbing/object" 14 | "github.com/jenkins-x/jx-logging/v3/pkg/log" 15 | "github.com/zbindenren/cc" 16 | ) 17 | 18 | var ( 19 | ErrPreviousVersionTagNotFound = errors.New("the git repository has no tag for the previous version") 20 | ) 21 | 22 | type Strategy struct { 23 | Dir string 24 | StripPrerelease bool 25 | CommitHeadlinesString string 26 | TagPrefix string 27 | } 28 | 29 | func (s Strategy) BumpVersion(previous semver.Version) (*semver.Version, error) { 30 | var ( 31 | dir = s.Dir 32 | err error 33 | commitHeadlinesString = s.CommitHeadlinesString 34 | summary *conventionalCommitsSummary 35 | ) 36 | if commitHeadlinesString != "" { 37 | summary, err = s.parseCommitHeadlines(commitHeadlinesString) 38 | if err != nil { 39 | return nil, err 40 | } 41 | } else { 42 | if dir == "" { 43 | dir, err = os.Getwd() 44 | if err != nil { 45 | return nil, fmt.Errorf("failed to get current working directory: %w", err) 46 | } 47 | } 48 | 49 | repo, err := git.PlainOpen(dir) 50 | if err != nil { 51 | return nil, fmt.Errorf("failed to open git repository at %q: %w", dir, err) 52 | } 53 | 54 | tagCommit, err := s.extractTagCommit(repo, previous.String()) 55 | if err != nil { 56 | return nil, err 57 | } 58 | 59 | summary, err = s.parseCommitsSince(repo, tagCommit) 60 | if err != nil { 61 | return nil, err 62 | } 63 | } 64 | 65 | if s.StripPrerelease { 66 | previous, err = previous.SetPrerelease("") 67 | if err != nil { 68 | return nil, err 69 | } 70 | } 71 | 72 | var version semver.Version 73 | switch { 74 | case summary.breakingChanges: 75 | log.Logger().Debug("Found breaking changes - incrementing major component") 76 | version = previous.IncMajor() 77 | case summary.types["feat"]: 78 | log.Logger().Debug("Found at least 1 new feature - incrementing minor component") 79 | version = previous.IncMinor() 80 | default: 81 | log.Logger().Debug("Incrementing patch component") 82 | version = previous.IncPatch() 83 | } 84 | 85 | return &version, nil 86 | } 87 | 88 | func (s Strategy) extractTagCommit(repo *git.Repository, tagName string) (*object.Commit, error) { 89 | var tagCommit *object.Commit 90 | 91 | previousTagRef, err := repo.Tag(tagName) 92 | if err == git.ErrTagNotFound { 93 | // let's try to prepend the prefix... 94 | tagName = s.TagPrefix + tagName 95 | previousTagRef, err = repo.Tag(tagName) 96 | if err == git.ErrTagNotFound { 97 | return nil, ErrPreviousVersionTagNotFound 98 | } 99 | } 100 | if err != nil { 101 | return nil, fmt.Errorf("failed to get tag %q: %w", tagName, err) 102 | } 103 | 104 | previousTag, err := repo.TagObject(previousTagRef.Hash()) 105 | if errors.Is(err, plumbing.ErrObjectNotFound) { 106 | // it's a lightweight tag, not an annotated tag 107 | tagCommit, err = repo.CommitObject(previousTagRef.Hash()) 108 | if err != nil { 109 | return nil, fmt.Errorf("failed to get the commit with hash %q (from tag %q): %w", previousTagRef.Hash().String(), tagName, err) 110 | } 111 | } 112 | if err != nil { 113 | return nil, fmt.Errorf("failed to get the annotated tag with hash %q (from tag name %q): %w", previousTagRef.Hash().String(), tagName, err) 114 | } 115 | 116 | if previousTag != nil { 117 | tagCommit, err = previousTag.Commit() 118 | if err != nil { 119 | return nil, fmt.Errorf("failed to get the commit from the annotated tag %q with hash %q: %w", previousTag.Name, previousTag.Hash.String(), err) 120 | } 121 | } 122 | 123 | if tagCommit == nil { 124 | return nil, fmt.Errorf("could not find a commit for tag %q", tagName) 125 | } 126 | 127 | log.Logger().Debugf("Previous version tag commit is %s", tagCommit.Hash) 128 | return tagCommit, nil 129 | } 130 | 131 | type conventionalCommitsSummary struct { 132 | conventionalCommitsCount int 133 | types map[string]bool 134 | breakingChanges bool 135 | } 136 | 137 | func (s Strategy) parseCommitsSince(repo *git.Repository, firstCommit *object.Commit) (*conventionalCommitsSummary, error) { 138 | summary := conventionalCommitsSummary{ 139 | types: map[string]bool{}, 140 | } 141 | 142 | log.Logger().Debugf("Iterating over all commits since %s", firstCommit.Committer.When) 143 | commitIterator, err := repo.Log(&git.LogOptions{ 144 | Since: &firstCommit.Committer.When, 145 | Order: git.LogOrderCommitterTime, 146 | }) 147 | if err != nil { 148 | return nil, fmt.Errorf("failed to list commits since %s (commit %q): %w", firstCommit.Committer.When, firstCommit.Hash.String(), err) 149 | } 150 | defer commitIterator.Close() 151 | 152 | for { 153 | commit, err := commitIterator.Next() 154 | if err == io.EOF { 155 | break 156 | } 157 | if err != nil { 158 | log.Logger().WithError(err).Debug("Skipping unretrievable commit") 159 | continue 160 | } 161 | 162 | log.Logger().Debugf("Checking commit %s with message %s", commit.Hash, commit.Message) 163 | if commit.Hash == firstCommit.Hash { 164 | log.Logger().Debugf("Found first commit %s and stopping iteration", commit.Hash) 165 | break 166 | } 167 | 168 | log.Logger().Debugf("Parsing commit %s", commit.Hash) 169 | c, err := cc.Parse(commit.Message) 170 | if err != nil { 171 | log.Logger().WithError(err).Debugf("Skipping non-conventional commit %s", commit.Hash) 172 | continue 173 | } 174 | 175 | summary.conventionalCommitsCount++ 176 | summary.types[c.Header.Type] = true 177 | if len(c.BreakingMessage()) > 0 { 178 | summary.breakingChanges = true 179 | } 180 | } 181 | 182 | log.Logger().Debugf("Summary of conventional commits since %s: %#v", firstCommit.Committer.When, summary) 183 | return &summary, nil 184 | } 185 | 186 | func (s Strategy) parseCommitHeadlines(commitHeadlinesString string) (*conventionalCommitsSummary, error) { 187 | summary := conventionalCommitsSummary{ 188 | types: map[string]bool{}, 189 | } 190 | 191 | log.Logger().Debugf("Iterating over all commits headline passed as a string") 192 | 193 | commitHeadlines := regexp.MustCompile("\r?\n").Split(commitHeadlinesString, -1) 194 | 195 | for index, commitHeadline := range commitHeadlines { 196 | log.Logger().Debugf("Parsing commit headline number %d with message %s", index, commitHeadline) 197 | c, err := cc.Parse(commitHeadline) 198 | if err != nil { 199 | log.Logger().WithError(err).Debugf("Skipping non-conventional commit headline number %d", index) 200 | continue 201 | } 202 | 203 | summary.conventionalCommitsCount++ 204 | summary.types[c.Header.Type] = true 205 | if len(c.BreakingMessage()) > 0 { 206 | summary.breakingChanges = true 207 | } 208 | } 209 | 210 | log.Logger().Debugf("Summary of conventional commits: %#v", summary) 211 | return &summary, nil 212 | } 213 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | "strings" 8 | "text/template" 9 | 10 | "github.com/Masterminds/semver/v3" 11 | "github.com/Masterminds/sprig/v3" 12 | "github.com/jenkins-x-plugins/jx-release-version/v2/pkg/strategy" 13 | "github.com/jenkins-x-plugins/jx-release-version/v2/pkg/strategy/auto" 14 | "github.com/jenkins-x-plugins/jx-release-version/v2/pkg/strategy/fromfile" 15 | "github.com/jenkins-x-plugins/jx-release-version/v2/pkg/strategy/fromtag" 16 | "github.com/jenkins-x-plugins/jx-release-version/v2/pkg/strategy/increment" 17 | "github.com/jenkins-x-plugins/jx-release-version/v2/pkg/strategy/manual" 18 | "github.com/jenkins-x-plugins/jx-release-version/v2/pkg/strategy/semantic" 19 | "github.com/jenkins-x-plugins/jx-release-version/v2/pkg/tag" 20 | "github.com/jenkins-x/jx-logging/v3/pkg/log" 21 | ) 22 | 23 | var ( 24 | // these are set at compile time through LD Flags 25 | Version = "dev" 26 | Revision = "unknown" 27 | Date = "now" 28 | 29 | // CLI flag options 30 | options struct { 31 | printVersion bool 32 | debug bool 33 | dir string 34 | previousVersion string 35 | commitHeadlines string 36 | nextVersion string 37 | outputFormat string 38 | tag bool 39 | tagPrefix string 40 | pushTag bool 41 | fetchTags bool 42 | gitName string 43 | gitEmail string 44 | } 45 | ) 46 | 47 | func init() { 48 | wd, _ := os.Getwd() 49 | flag.StringVar(&options.dir, "dir", wd, "The directory that contains the git repository. Default to the current working directory.") 50 | flag.StringVar(&options.previousVersion, "previous-version", getEnvWithDefault("PREVIOUS_VERSION", "auto"), "The strategy to detect the previous version: auto, from-tag, from-file or manual. Default to the PREVIOUS_VERSION env var.") 51 | flag.StringVar(&options.commitHeadlines, "commit-headlines", getEnvWithDefault("COMMIT_HEADLINES", ""), "The commit headline(s) to use for semantic next version instead of the commit()s of a repository. Default to empty.") 52 | flag.StringVar(&options.nextVersion, "next-version", getEnvWithDefault("NEXT_VERSION", "auto"), "The strategy to calculate the next version: auto, semantic, from-file, increment or manual. Default to the NEXT_VERSION env var.") 53 | flag.StringVar(&options.outputFormat, "output-format", getEnvWithDefault("OUTPUT_FORMAT", "{{.Major}}.{{.Minor}}.{{.Patch}}"), "The output format of the next version. Default to the OUTPUT_FORMAT env var.") 54 | flag.BoolVar(&options.debug, "debug", os.Getenv("JX_LOG_LEVEL") == "debug", "Print debug logs. Enabled by default if the JX_LOG_LEVEL env var is set to 'debug'.") 55 | flag.BoolVar(&options.printVersion, "version", false, "Just print the version and do nothing.") 56 | flag.BoolVar(&options.tag, "tag", os.Getenv("TAG") == "true", "Perform a git tag") 57 | flag.StringVar(&options.tagPrefix, "tag-prefix", getEnvWithDefault("TAG_PREFIX", "v"), "Prefix to use for the git tag") 58 | flag.BoolVar(&options.pushTag, "push-tag", getEnvWithDefault("PUSH_TAG", "true") == "true", "Use with tag flag, pushes a git tag to the remote branch") 59 | flag.BoolVar(&options.fetchTags, "fetch-tags", getEnvWithDefault("FETCH_TAGS", "") == "true", "Fetch tags from the remote origin before detecting the previous version") 60 | flag.StringVar(&options.gitName, "git-user", getEnvWithDefault("GIT_NAME", ""), "Name is the personal name of the author and the committer of a commit, use to override Git config") 61 | flag.StringVar(&options.gitEmail, "git-email", getEnvWithDefault("GIT_EMAIL", ""), "Email is the email of the author and the committer of a commit, use to override Git config") 62 | } 63 | 64 | func main() { 65 | flag.Parse() 66 | 67 | if options.printVersion { 68 | fmt.Printf("Version %s - Revision %s - Date %s", Version, Revision, Date) 69 | return 70 | } 71 | 72 | if options.debug { 73 | os.Setenv("JX_LOG_LEVEL", "debug") 74 | log.Logger().Debugf("jx-release-version %s running in debug mode in %s", Version, options.dir) 75 | } 76 | 77 | previousVersion, err := versionReader().ReadVersion() 78 | if err != nil { 79 | log.Logger().Fatalf("Failed to read previous version using %q: %v", options.previousVersion, err) 80 | } 81 | log.Logger().Debugf("Previous version: %s", previousVersion.String()) 82 | 83 | nextVersion, err := versionBumper().BumpVersion(*previousVersion) 84 | if err != nil { 85 | log.Logger().Fatalf("Failed to bump version using %q: %v", options.nextVersion, err) 86 | } 87 | log.Logger().Debugf("Next version: %s", nextVersion.String()) 88 | 89 | output, err := formatVersion(*nextVersion) 90 | if err != nil { 91 | log.Logger().Fatalf("Failed to format version %q with %q: %v", *nextVersion, options.outputFormat, err) 92 | } 93 | 94 | fmt.Print(output) 95 | 96 | if options.tag { 97 | tagOptions := tag.Tag{ 98 | FormattedVersion: options.tagPrefix + output, 99 | Dir: options.dir, 100 | PushTag: options.pushTag, 101 | GitName: options.gitName, 102 | GitEmail: options.gitEmail, 103 | } 104 | err = tagOptions.TagRemote() 105 | if err != nil { 106 | log.Logger().Fatalf("Failed to tag using version %s: %v", output, err) 107 | } 108 | } 109 | } 110 | 111 | func versionReader() strategy.VersionReader { 112 | var ( 113 | versionReader strategy.VersionReader 114 | strategyName, strategyArg string 115 | ) 116 | 117 | parts := strings.SplitN(options.previousVersion, ":", 2) 118 | strategyName = parts[0] 119 | if len(parts) > 1 { 120 | strategyArg = parts[1] 121 | } 122 | 123 | switch strategyName { 124 | case "auto", "": 125 | versionReader = auto.Strategy{ 126 | FromTagStrategy: fromtag.Strategy{ 127 | Dir: options.dir, 128 | FetchTags: options.fetchTags, 129 | }, 130 | } 131 | case "from-tag": 132 | versionReader = fromtag.Strategy{ 133 | Dir: options.dir, 134 | TagPattern: strategyArg, 135 | FetchTags: options.fetchTags, 136 | } 137 | case "from-file": 138 | versionReader = fromfile.Strategy{ 139 | Dir: options.dir, 140 | FilePath: strategyArg, 141 | } 142 | case "manual": 143 | versionReader = manual.Strategy{ 144 | Version: strategyArg, 145 | } 146 | default: 147 | versionReader = manual.Strategy{ 148 | Version: options.previousVersion, 149 | } 150 | } 151 | 152 | log.Logger().Debugf("Using %q version reader (with %q)", strategyName, strategyArg) 153 | return versionReader 154 | } 155 | 156 | func versionBumper() strategy.VersionBumper { 157 | var ( 158 | versionBumper strategy.VersionBumper 159 | strategyName, strategyArg string 160 | ) 161 | 162 | parts := strings.SplitN(options.nextVersion, ":", 2) 163 | strategyName = parts[0] 164 | if len(parts) > 1 { 165 | strategyArg = parts[1] 166 | } 167 | 168 | switch strategyName { 169 | case "auto", "": 170 | versionBumper = auto.Strategy{ 171 | SemanticStrategy: semantic.Strategy{ 172 | Dir: options.dir, 173 | StripPrerelease: strings.Contains(strategyArg, "strip-prerelease"), 174 | CommitHeadlinesString: options.commitHeadlines, 175 | TagPrefix: options.tagPrefix, 176 | }, 177 | } 178 | case "semantic": 179 | versionBumper = semantic.Strategy{ 180 | Dir: options.dir, 181 | StripPrerelease: strings.Contains(strategyArg, "strip-prerelease"), 182 | CommitHeadlinesString: options.commitHeadlines, 183 | TagPrefix: options.tagPrefix, 184 | } 185 | case "from-file": 186 | versionBumper = fromfile.Strategy{ 187 | Dir: options.dir, 188 | FilePath: strategyArg, 189 | } 190 | case "increment": 191 | versionBumper = increment.Strategy{ 192 | ComponentToIncrement: strategyArg, 193 | } 194 | case "manual": 195 | versionBumper = manual.Strategy{ 196 | Version: strategyArg, 197 | } 198 | default: 199 | versionBumper = manual.Strategy{ 200 | Version: options.previousVersion, 201 | } 202 | } 203 | 204 | log.Logger().Debugf("Using %q version bumper (with %q)", strategyName, strategyArg) 205 | return versionBumper 206 | } 207 | 208 | func formatVersion(version semver.Version) (string, error) { 209 | outputTemplate, err := template.New("output").Funcs(sprig.TxtFuncMap()).Parse(options.outputFormat) 210 | if err != nil { 211 | return "", err 212 | } 213 | 214 | output := new(strings.Builder) 215 | err = outputTemplate.Execute(output, version) 216 | if err != nil { 217 | return "", err 218 | } 219 | 220 | return output.String(), nil 221 | } 222 | 223 | func getEnvWithDefault(key string, defaultVal string) string { 224 | if val, found := os.LookupEnv(key); found { 225 | return val 226 | } 227 | return defaultVal 228 | } 229 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jenkins X Release Version 2 | 3 | **This is a simple and standalone binary that can be used in CD pipelines to calculate the next release version**. 4 | 5 | By default it: 6 | - retrieves the previous version - which is the latest git tag matching the [semver](https://semver.org/) spec 7 | - use [conventional commits](https://www.conventionalcommits.org/) to calculate the next release version 8 | - print the next release version to the standard output using a specific format 9 | 10 | But it also supports other strategies to read the previous version and calculate the next version. 11 | 12 | Optionally, you can also create a tag - and push it to a remote git repository. 13 | 14 | ## Usage 15 | 16 | Just run `jx-release-version` in your project's top directory, and it should just print the next release version. It won't write anything to disk. 17 | 18 | It accepts the following CLI flags: 19 | - `-dir`: the location on the filesystem of your project's top directory - default to the current working directory. 20 | - `-previous-version`: the [strategy to use to read the previous version](#reading-the-previous-version). Can also be set using the `PREVIOUS_VERSION` environment variable. Default to `auto`. 21 | - `-commit-headlines`: the [commit headlines to use to generate the next semantic version](#pass-commit-headlines). Can also be set using the `COMMIT_HEADLINES` environment variable. Default to ``. 22 | - `-next-version`: the [strategy to use to calculate the next version](#calculating—the-next-version). Can also be set using the `NEXT_VERSION` environment variable. Default to `auto`. 23 | - `-output-format`: the [output format of the next release version](#output-format). Can also be set using the `OUTPUT_FORMAT` environment variable. Default to `{{.Major}}.{{.Minor}}.{{.Patch}}`. 24 | - `-tag`: if enabled, [a new tag will be created](#tag). Can also be set using the `TAG` environment variable with the `"TRUE"` value. 25 | - `-tag-prefix`: the prefix for the new tag - prefixed before the output. Can also be set using the `TAG_PREFIX` environment variable. Default to `"v"`. 26 | - `-push-tag`: if enabled, the new tag will be pushed to the `origin` remote. Can also be set using the `PUSH_TAG` environment variable. Default to `true`. 27 | - `-fetch-tags`: if enabled, the tags will be fetched from the `origin` remote, before detecting the previous version. Can also be set using the `FETCH_TAGS` environment variable. 28 | - `-git-user`: the name of the author/committer used to create the git tag. Can also be set using the `GIT_NAME` environment variable. Default to the value set in the git config. 29 | - `-git-email`: the email of the author/committer used to create the git tag. Can also be set using the `GIT_EMAIL` environment variable. Default to the value set in the git config. 30 | - `-debug`: if enabled, will print debug logs to stdout in addition to the next version. It can also be enabled by setting the `JX_LOG_LEVEL` environment variable to `debug`. 31 | 32 | ### Features 33 | 34 | - standalone - no dependencies required. It uses an embedded [git implementation](https://github.com/go-git/go-git) to read the [Git](https://git-scm.com/) repository's information. 35 | - simple configuration through CLI flags or environment variables. 36 | - by default works even on an empty git repository. 37 | - multiple strategies to [read the previous version](#reading-the-previous-version) and/or [calculate the next version](#calculating—the-next-version). 38 | - [custom output format](#output-format). 39 | - [create (and push) a git tag for the new version](#tag). 40 | - [github action](#github-actions). 41 | 42 | ## Reading the previous version 43 | 44 | There are different ways to read the previous version: 45 | 46 | ### Auto 47 | 48 | The `auto` strategy is the default one. It tries to find the latest git tag, or if there are no git tags, it just use `0.0.0` as the previous version. 49 | 50 | **Usage**: 51 | - `jx-release-version -previous-version=auto` 52 | - `jx-release-version` - the `auto` strategy is already the default one 53 | 54 | ### From tag 55 | 56 | The `from-tag` strategy uses the latest git tag as the previous version. Note that it only uses tags which matches the [semver](https://semver.org/) spec - other tags are just ignored. 57 | 58 | Optionally, it can filter tags based on a given pattern: if you use `from-tag:v1` it will use the latest tag matching the `v1` pattern. Note that it uses [Go's stdlib regexp](https://golang.org/pkg/regexp/) - you can see the [syntax](https://golang.org/pkg/regexp/syntax/). 59 | This feature can be used to maintain 2 major versions in parallel: for each, you just configure the right pattern, so that `jx-release-version` retrieves the right previous version, and bump it as it should. 60 | 61 | Optionally, it can fetch the tags from a remote repository named `origin`, if you set the `-fetch-tags` flag, or the `FETCH_TAGS` environment variable to `true`. It will fetch the tags before trying to find the previous version. 62 | 63 | Note that if it can't find a tag, it will fail. 64 | 65 | **Usage**: 66 | - `jx-release-version -previous-version=from-tag` 67 | - `jx-release-version -previous-version=from-tag:v1` 68 | 69 | ### From file 70 | 71 | The `from-file` strategy will read the previous version from a file. Supported formats are: 72 | - **Helm Charts**, using the `Chart.yaml` file 73 | - **Makefile**, using the `Makefile` file 74 | - **Automake**, using the `configure.ac` file 75 | - **CMake**, using the `CMakeLists.txt` file 76 | - **Python**, using the `setup.py` file 77 | - **Maven**, using the `pom.xml` file 78 | - **Javascript**, using the `package.json` file 79 | - **Gradle**, using the `build.gradle`, `build.gradle.kts` or `gradle.properties` file 80 | 81 | **Usage**: 82 | - if you use `jx-release-version -previous-version=from-file` it will auto detect which file to use, trying the supported formats in the order in which they are listed. If a "format" supports multiple files (such as Gradle), it will try to read the version from each file - in the order in which they are listed. 83 | - if you specify a file, it will use it to find the previous version. For example: 84 | - `jx-release-version -previous-version=from-file:pom.xml` 85 | - `jx-release-version -previous-version=from-file:charts/my-chart/Chart.yaml` 86 | - `jx-release-version -previous-version=from-file:Chart.yaml -dir=charts/my-chart` 87 | 88 | ### Manual 89 | 90 | The `manual` strategy can be used if you already know the previous version, and just want `jx-release-version` to use it. 91 | 92 | **Usage**: 93 | - `jx-release-version -previous-version=manual:1.2.3` 94 | - `jx-release-version -previous-version=1.2.3` - the `manual` prefix is optional 95 | 96 | ## Calculating the next version 97 | 98 | There are different ways to calculate the next version: 99 | 100 | ### Auto 101 | 102 | The `auto` strategy is the default one. It tries to use the `semantic` strategy, but if it can't find a tag for the previous version, it will fallback to incrementing the patch component. 103 | 104 | **Usage**: 105 | - `jx-release-version -next-version=auto` 106 | - `jx-release-version` - the `auto` strategy is already the default one 107 | 108 | ### Semantic release 109 | 110 | The `semantic` strategy finds all commits between the previous version's git tag and the current HEAD, and then uses [conventional commits](https://www.conventionalcommits.org/) to parse them. If it finds: 111 | - at least 1 commit with a `BREAKING CHANGE: ` footer, then it will bump the major component of the version 112 | - at least 1 commit with a `feat:` prefix, then it will bump the minor component of the version 113 | - otherwise it will bump the patch component of the version 114 | 115 | Note that if it can't find a tag for the previous version, it will fail, except if you use the `-commit-headlines` flags to generate semantic next version from a single/multiline string instead of repository commits/tags. 116 | 117 | **Usage**: 118 | - `jx-release-version -next-version=semantic` 119 | - if you want to strip any prerelease information from the build before performing the version bump you can use: 120 | - `jx-release-version -next-version=semantic:strip-prerelease` 121 | 122 | #### Pass commit headlines 123 | If you want to retrieve a semantic version without using tags or commits from a repository, you can manually set the previous version and the commit headlines to use: 124 | - `jx-release-version -previous-version=1.2.3 -commit-headlines="feat: a feature"` 125 | 126 | 127 | ### From file 128 | 129 | The `from-file` strategy will read the next version from a file. Supported formats are: 130 | - **Helm Charts**, using the `Chart.yaml` file 131 | - **Makefile**, using the `Makefile` file 132 | - **Automake**, using the `configure.ac` file 133 | - **CMake**, using the `CMakeLists.txt` file 134 | - **Python**, using the `setup.py` file 135 | - **Maven**, using the `pom.xml` file 136 | - **Javascript**, using the `package.json` file 137 | - **Gradle**, using the `build.gradle`, `build.gradle.kts` or `gradle.properties` file 138 | 139 | **Usage**: 140 | - if you use `jx-release-version -next-version=from-file` it will auto detect which file to use, trying the supported formats in the order in which they are listed. If a "format" supports multiple files (such as Gradle), it will try to read the version from each file - in the order in which they are listed. 141 | - if you specify a file, it will use it to find the next version. For example: 142 | - `jx-release-version -next-version=from-file:pom.xml` 143 | - `jx-release-version -next-version=from-file:charts/my-chart/Chart.yaml` 144 | - `jx-release-version -next-version=from-file:Chart.yaml -dir=charts/my-chart` 145 | 146 | ### Increment 147 | 148 | The `increment` strategy can be used if you want to increment a specific component of the version. 149 | 150 | **Usage**: 151 | - `jx-release-version -next-version=increment:major` 152 | - `jx-release-version -next-version=increment:minor` 153 | - `jx-release-version -next-version=increment:patch` 154 | - `jx-release-version -next-version=increment` - by default it will increment the patch component 155 | 156 | ### Manual 157 | 158 | The `manual` strategy can be used if you already know the next version, and just want `jx-release-version` to use it. 159 | 160 | **Usage**: 161 | - `jx-release-version -next-version=manual:1.2.3` 162 | - `jx-release-version -next-version=1.2.3` - the `manual` prefix is optional 163 | 164 | ## Output format 165 | 166 | The output format of the next release version can be defined using a [Go template](https://golang.org/pkg/text/template/): 167 | - the template has access to the [Version object](https://pkg.go.dev/github.com/Masterminds/semver/v3#pkg-index) - so you can use fields such as: 168 | - [Major](https://pkg.go.dev/github.com/Masterminds/semver/v3#Version.Major) 169 | - [Minor](https://pkg.go.dev/github.com/Masterminds/semver/v3#Version.Minor) 170 | - [Patch](https://pkg.go.dev/github.com/Masterminds/semver/v3#Version.Patch) 171 | - [Prerelease](https://pkg.go.dev/github.com/Masterminds/semver/v3#Version.Prerelease) 172 | - [Metadata](https://pkg.go.dev/github.com/Masterminds/semver/v3#Version.Metadata) 173 | - [String](https://pkg.go.dev/github.com/Masterminds/semver/v3#Version.String) 174 | - [Original](https://pkg.go.dev/github.com/Masterminds/semver/v3#Version.Original) 175 | - the default format is: `{{.Major}}.{{.Minor}}.{{.Patch}}` 176 | - you can also use the [sprig functions](http://masterminds.github.io/sprig/) 177 | 178 | **Usage**: 179 | - `jx-release-version -output-format=v{{.Major}}.{{.Minor}}` - if you only want major/minor 180 | - `jx-release-version -output-format={{.String}}` - if you want the full version with prerelease / metadata information, if these are set in a file for example 181 | 182 | ## Tag 183 | 184 | Most of the time, you'll be using the `jx-release-version` tool as part of your CD pipelines, so you'll want to do something with the "next version", such as creating (and pushing) a git tag. This behavior is disabled by default, but can easily be enabled by setting the `-tag` CLI flag - or alternatively setting the `TAG` environment variable to `"true"`. 185 | 186 | If the next version is `1.2.3` for example, by default a new tag named `v1.2.3` will be created. You can control the prefix using the `-tag-prefix` CLI flag - or alternatively by setting the `TAG_PREFIX` environment variable. 187 | 188 | If you want to override the name/email of the author/committer used to create the git tag, you can set the `-git-user` / `-git-email` CLI flags, or alternatively the `GIT_NAME` / `GIT_EMAIL` environment variables. 189 | 190 | ### Pushing 191 | 192 | Creating a new (local) tag is great, but for it to be useful, you will also need to push it to a remote git repository. By default, when the new tag is created, it will also be pushed automatically to the `origin` remote. 193 | 194 | Note that this operation might requires authentication - which you can provide using the `GIT_TOKEN` environment variable. 195 | 196 | ## Integrations 197 | 198 | ### Tekton Pipelines 199 | 200 | If you want to use `jx-release-version` in your [Tekton](https://tekton.dev/) pipeline, you can add a step in your Task which writes the output of the `jx-release-version` command to a file, such as: 201 | 202 | ``` 203 | steps: 204 | - image: ghcr.io/jenkins-x/jx-release-version:2.7.10 205 | name: next-version 206 | script: | 207 | #!/usr/bin/env sh 208 | jx-release-version > VERSION 209 | ``` 210 | 211 | ### GitHub Actions 212 | 213 | If you want to use `jx-release-version` in your [GitHub Workflow](https://github.com/features/actions), you can add the following to your workflow file: 214 | 215 | ``` 216 | jobs: 217 | yourjob: 218 | runs-on: ubuntu-20.04 219 | steps: 220 | - uses: actions/checkout@v2 221 | with: 222 | fetch-depth: 0 223 | token: ${{ secrets.GIT_BOT_TOKEN }} 224 | - run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* 225 | 226 | - id: nextversion 227 | name: next release version 228 | uses: jenkins-x-plugins/jx-release-version@v2.7.10 229 | 230 | - name: do something with the next version 231 | run: echo next version is $VERSION 232 | env: 233 | VERSION: ${{ steps.nextversion.outputs.version }} 234 | ``` 235 | 236 | Or to create a new tag and push it, you can: 237 | - use the [fregante/setup-git-user](https://github.com/fregante/setup-git-user) action to setup the git name/email to the [github-actions bot](https://github.com/apps/github-actions) 238 | - if you want to use a specific user, you can set the `git-user` and `git-email` parameters 239 | - set the `tag` and `github-token` parameters 240 | 241 | ``` 242 | jobs: 243 | yourjob: 244 | runs-on: ubuntu-20.04 245 | steps: 246 | - uses: actions/checkout@v2 247 | with: 248 | fetch-depth: 0 249 | token: ${{ secrets.GIT_BOT_TOKEN }} 250 | - run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* 251 | - uses: fregante/setup-git-user@v1 252 | 253 | - name: tag 254 | id: tag 255 | uses: jenkins-x-plugins/jx-release-version@v2.7.10 256 | with: 257 | tag: true 258 | github-token: ${{ secrets.GIT_BOT_TOKEN }} 259 | 260 | - name: do something with the next version 261 | run: echo next version is $VERSION 262 | env: 263 | VERSION: ${{ steps.tag.outputs.version }} 264 | ``` 265 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 8 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 9 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 10 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 11 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 12 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 13 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 14 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 15 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 16 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 17 | dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= 18 | dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= 19 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 20 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 21 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 22 | github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= 23 | github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= 24 | github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= 25 | github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= 26 | github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= 27 | github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= 28 | github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= 29 | github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= 30 | github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= 31 | github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= 32 | github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= 33 | github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= 34 | github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= 35 | github.com/STARRY-S/zip v0.2.2 h1:8QeCbIi1Z9U5MgoDARJR1ClbBo9RD46SmVy+dl0woCk= 36 | github.com/STARRY-S/zip v0.2.2/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk= 37 | github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= 38 | github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= 39 | github.com/TV4/logrus-stackdriver-formatter v0.1.0 h1:nFea8RiX7ecTnWPM+9FIqwZYJdcGo58CHMGIVdYzMXg= 40 | github.com/TV4/logrus-stackdriver-formatter v0.1.0/go.mod h1:wwS7hOiBvP6SBD0UXCa767+VhHkaXrfX0MzUojYcN0Q= 41 | github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= 42 | github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= 43 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 44 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 45 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 46 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 47 | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= 48 | github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3 h1:8PmGpDEZl9yDpcdEr6Odf23feCxK3LNUNMxjXg41pZQ= 49 | github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= 50 | github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= 51 | github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= 52 | github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= 53 | github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= 54 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 55 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 56 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 57 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= 58 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= 59 | github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= 60 | github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= 61 | github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= 62 | github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= 63 | github.com/bbuck/go-lexer v1.0.0 h1:ZwzxWHnxQslJ/5I01nlhZwE7MtmvXbtEyRwrgQ70Qew= 64 | github.com/bbuck/go-lexer v1.0.0/go.mod h1:JOt4Q0nNqWxYEy+spld4SJGe9r8G8suXd1dukur9O90= 65 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 66 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 67 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 68 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 69 | github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU= 70 | github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs= 71 | github.com/bodgit/sevenzip v1.6.0 h1:a4R0Wu6/P1o1pP/3VV++aEOcyeBxeO/xE2Y9NSTrr6A= 72 | github.com/bodgit/sevenzip v1.6.0/go.mod h1:zOBh9nJUof7tcrlqJFv1koWRrhz3LbDbUNngkuZxLMc= 73 | github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4= 74 | github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM= 75 | github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= 76 | github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= 77 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 78 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 79 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 80 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 81 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 82 | github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= 83 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 84 | github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= 85 | github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= 86 | github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= 87 | github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= 88 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 89 | github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 90 | github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 91 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 92 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 93 | github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= 94 | github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= 95 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 96 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 97 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 98 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 99 | github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4= 100 | github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= 101 | github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= 102 | github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 103 | github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= 104 | github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= 105 | github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= 106 | github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= 107 | github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= 108 | github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= 109 | github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= 110 | github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= 111 | github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= 112 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 113 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 114 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 115 | github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= 116 | github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= 117 | github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= 118 | github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= 119 | github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= 120 | github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= 121 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 122 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 123 | github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= 124 | github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= 125 | github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= 126 | github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= 127 | github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= 128 | github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= 129 | github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= 130 | github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= 131 | github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM= 132 | github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= 133 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 134 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 135 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 136 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 137 | github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= 138 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 139 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 140 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 141 | github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 142 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 143 | github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= 144 | github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= 145 | github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= 146 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 147 | github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 148 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 149 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 150 | github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 151 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 152 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 153 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 154 | github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= 155 | github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= 156 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 157 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 158 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 159 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 160 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 161 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 162 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 163 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 164 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 165 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 166 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 167 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 168 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 169 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 170 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 171 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 172 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 173 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 174 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 175 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 176 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 177 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 178 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 179 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 180 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 181 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 182 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 183 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 184 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 185 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 186 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 187 | github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 188 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 189 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 190 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 191 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 192 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 193 | github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= 194 | github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 195 | github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 196 | github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 197 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 198 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 199 | github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 200 | github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= 201 | github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 202 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 203 | github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= 204 | github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 205 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 206 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 207 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 208 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 209 | github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= 210 | github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= 211 | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 212 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 213 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 214 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 215 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 216 | github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 217 | github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 218 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 219 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 220 | github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= 221 | github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= 222 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 223 | github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= 224 | github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 225 | github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 226 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 227 | github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= 228 | github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= 229 | github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= 230 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 231 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 232 | github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= 233 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= 234 | github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= 235 | github.com/jenkins-x/jx-logging/v3 v3.1.3 h1:PYvD2mKFljts3OXrIRWgTN0yu+OfAbFWgmdLigstepQ= 236 | github.com/jenkins-x/jx-logging/v3 v3.1.3/go.mod h1:Np/jrT7hD36sznHjhp1HFv0zjm8znNibVhi6Z53DheU= 237 | github.com/jenkins-x/logrus-stackdriver-formatter v0.2.8 h1:71qqOqutjjNFIg3LdQaeoed1/u99ulKUQRzl82j6MTc= 238 | github.com/jenkins-x/logrus-stackdriver-formatter v0.2.8/go.mod h1:p3MydyTFEaIvhgtuZ/OFGxRpzUT1eTKoaD5CI7E7qOg= 239 | github.com/jm33-m0/arc/v2 v2.0.1 h1:vEFs1706MRpQ430O78qS9h+wYzp9oSjlM6c70X9l+Ug= 240 | github.com/jm33-m0/arc/v2 v2.0.1/go.mod h1:Dzf3wxVdcaFZla5y3aZwdNUkE6Im2H+ubCGSRP45Obs= 241 | github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= 242 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 243 | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 244 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 245 | github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 246 | github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 247 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 248 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 249 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 250 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 251 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 252 | github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 253 | github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= 254 | github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= 255 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 256 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 257 | github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= 258 | github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= 259 | github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= 260 | github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= 261 | github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= 262 | github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= 263 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 264 | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 265 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 266 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 267 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 268 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 269 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 270 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 271 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 272 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 273 | github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= 274 | github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= 275 | github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= 276 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 277 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 278 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 279 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 280 | github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 281 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 282 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 283 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 284 | github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 285 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 286 | github.com/mholt/archives v0.1.0 h1:FacgJyrjiuyomTuNA92X5GyRBRZjE43Y/lrzKIlF35Q= 287 | github.com/mholt/archives v0.1.0/go.mod h1:j/Ire/jm42GN7h90F5kzj6hf6ZFzEH66de+hmjEKu+I= 288 | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 289 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 290 | github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= 291 | github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= 292 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 293 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 294 | github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 295 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 296 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 297 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 298 | github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= 299 | github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 300 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 301 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 302 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 303 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 304 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 305 | github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 306 | github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= 307 | github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= 308 | github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= 309 | github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= 310 | github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= 311 | github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= 312 | github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= 313 | github.com/nwaples/rardecode/v2 v2.1.0 h1:JQl9ZoBPDy+nIZGb1mx8+anfHp/LV3NE2MjMiv0ct/U= 314 | github.com/nwaples/rardecode/v2 v2.1.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw= 315 | github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= 316 | github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= 317 | github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= 318 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 319 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 320 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 321 | github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= 322 | github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= 323 | github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= 324 | github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= 325 | github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= 326 | github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= 327 | github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= 328 | github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= 329 | github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= 330 | github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= 331 | github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= 332 | github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= 333 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 334 | github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= 335 | github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= 336 | github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= 337 | github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= 338 | github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= 339 | github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= 340 | github.com/pjbgf/sha1cd v0.4.0 h1:NXzbL1RvjTUi6kgYZCX3fPwwl27Q1LJndxtUDVfJGRY= 341 | github.com/pjbgf/sha1cd v0.4.0/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= 342 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 343 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 344 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 345 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 346 | github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= 347 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 348 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 349 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 350 | github.com/postfinance/flash v0.2.0/go.mod h1:XpRoWKDcSRWrIooOVXmavT3u5Y5zVhxecJPyVgN3e9Q= 351 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 352 | github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= 353 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 354 | github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= 355 | github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= 356 | github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU= 357 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 358 | github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 359 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 360 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 361 | github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 362 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 363 | github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 364 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 365 | github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= 366 | github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= 367 | github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= 368 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 369 | github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 370 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 371 | github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= 372 | github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 373 | github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 374 | github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= 375 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 376 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 377 | github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= 378 | github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= 379 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 380 | github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= 381 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 382 | github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= 383 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 384 | github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= 385 | github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= 386 | github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= 387 | github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= 388 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 389 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 390 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 391 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 392 | github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 393 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 394 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 395 | github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= 396 | github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= 397 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 398 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 399 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 400 | github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= 401 | github.com/sorairolake/lzip-go v0.3.5 h1:ms5Xri9o1JBIWvOFAorYtUNik6HI3HgBTkISiqu0Cwg= 402 | github.com/sorairolake/lzip-go v0.3.5/go.mod h1:N0KYq5iWrMXI0ZEXKXaS9hCyOjZUQdBDEIbXfoUwbdk= 403 | github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE= 404 | github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= 405 | github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= 406 | github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 407 | github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= 408 | github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= 409 | github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= 410 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 411 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 412 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 413 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 414 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 415 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 416 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 417 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 418 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 419 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 420 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 421 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 422 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 423 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 424 | github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= 425 | github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY= 426 | github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk= 427 | github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk= 428 | github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 429 | github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 430 | github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= 431 | github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 432 | github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= 433 | github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 434 | github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= 435 | github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= 436 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 437 | github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= 438 | github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= 439 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 440 | github.com/zbindenren/cc v0.4.4 h1:Xu27UaUEDRTBT5Y7Hq0klUDA8gdhoYjGsIxfh9LI6XE= 441 | github.com/zbindenren/cc v0.4.4/go.mod h1:8mksZKlnrCCxJEIsS/Xrw/Te/XydKaO956pnWE79H4c= 442 | go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 443 | go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= 444 | go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= 445 | go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= 446 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 447 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 448 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 449 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 450 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 451 | go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 452 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 453 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 454 | go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= 455 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 456 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 457 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 458 | go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= 459 | go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= 460 | go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc= 461 | go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU= 462 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 463 | golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 464 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 465 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 466 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 467 | golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 468 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 469 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 470 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 471 | golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 472 | golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= 473 | golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= 474 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 475 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 476 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 477 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 478 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 479 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 480 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 481 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 482 | golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= 483 | golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= 484 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 485 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 486 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 487 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 488 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 489 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 490 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 491 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 492 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 493 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 494 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 495 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 496 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 497 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 498 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 499 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 500 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 501 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 502 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 503 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 504 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 505 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 506 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 507 | golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 508 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 509 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 510 | golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 511 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 512 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 513 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 514 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 515 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 516 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 517 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 518 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 519 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 520 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 521 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 522 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 523 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 524 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 525 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 526 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 527 | golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 528 | golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= 529 | golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= 530 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 531 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 532 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 533 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 534 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 535 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 536 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 537 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 538 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 539 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 540 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 541 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 542 | golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= 543 | golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 544 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 545 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 546 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 547 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 548 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 549 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 550 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 551 | golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 552 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 553 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 554 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 555 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 556 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 557 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 558 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 559 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 560 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 561 | golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 562 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 563 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 564 | golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 565 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 566 | golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 567 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 568 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 569 | golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 570 | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 571 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 572 | golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 573 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 574 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 575 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 576 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 577 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 578 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 579 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 580 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 581 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 582 | golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= 583 | golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 584 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 585 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 586 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 587 | golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= 588 | golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= 589 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 590 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 591 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 592 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 593 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 594 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 595 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 596 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 597 | golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= 598 | golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= 599 | golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 600 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 601 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 602 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 603 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 604 | golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 605 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 606 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 607 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 608 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 609 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 610 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 611 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 612 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 613 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 614 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 615 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 616 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 617 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 618 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 619 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 620 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 621 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 622 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 623 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 624 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 625 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 626 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 627 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 628 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 629 | golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 630 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 631 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 632 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 633 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 634 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 635 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 636 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 637 | google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= 638 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 639 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 640 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 641 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 642 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 643 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 644 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 645 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 646 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 647 | google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 648 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 649 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 650 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 651 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 652 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 653 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 654 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 655 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 656 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 657 | google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= 658 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 659 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 660 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 661 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 662 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 663 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 664 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 665 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 666 | google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= 667 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 668 | google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= 669 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 670 | google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 671 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 672 | google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 673 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 674 | google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 675 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 676 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 677 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 678 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 679 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 680 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 681 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 682 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 683 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 684 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 685 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 686 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 687 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 688 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 689 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 690 | gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= 691 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 692 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 693 | gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= 694 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 695 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 696 | gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= 697 | gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= 698 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 699 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 700 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 701 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 702 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 703 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 704 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 705 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 706 | gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 707 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 708 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 709 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 710 | gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= 711 | honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 712 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 713 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 714 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 715 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 716 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 717 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 718 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 719 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 720 | sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= 721 | sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= 722 | --------------------------------------------------------------------------------