Compare commits

...

14 Commits

Author SHA1 Message Date
lukaszraczylo 49a46a74c1 Add signing of the builds. 2025-12-15 00:43:42 +00:00
lukaszraczylo 3a48a67c75 Improve calculation logic, add ability to strip prefixes. 2025-12-10 14:37:38 +00:00
lukaszraczylo 18b9b474e0 fix: remove unnecessary verified parameter from homebrew cask
The verified parameter is only needed when the URL domain differs
from the homepage domain. Since both point to github.com, Homebrew
audit flags this as unnecessary.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 02:06:43 +00:00
lukaszraczylo f5a118fd1a Use shared PR workflow. 2025-12-08 02:06:43 +00:00
lukaszraczylo a1c4133e94 Update go.mod and go.sum (#57) 2025-12-08 01:22:53 +00:00
lukaszraczylo 2a51752663 Trigger autoupdate. 2025-12-08 01:14:40 +00:00
lukaszraczylo 0bc848f6f4 fixup! fixup! Update binary confusion. 2025-12-07 16:29:21 +00:00
lukaszraczylo 8590963822 fixup! Update binary confusion. 2025-12-07 16:27:57 +00:00
lukaszraczylo f4285403f7 Update binary confusion. 2025-12-07 16:27:04 +00:00
lukaszraczylo b3d104f0a8 fixup! fixup! fixup! fixup! fixup! fixup! Move updater to REST api. 2025-12-07 16:23:19 +00:00
lukaszraczylo 7a70ed6614 fixup! fixup! fixup! fixup! fixup! Move updater to REST api. 2025-12-07 16:09:36 +00:00
lukaszraczylo f9a18995d0 fixup! fixup! fixup! fixup! Move updater to REST api. 2025-12-07 15:59:21 +00:00
lukaszraczylo 66756b6772 fixup! fixup! fixup! Move updater to REST api. 2025-12-07 15:42:15 +00:00
lukaszraczylo a0f1ab6930 fixup! fixup! Move updater to REST api. 2025-12-07 15:36:03 +00:00
21 changed files with 495 additions and 212 deletions
+3
View File
@@ -8,6 +8,7 @@ on:
permissions:
contents: write
actions: write
pull-requests: write
jobs:
autoupdate:
@@ -15,3 +16,5 @@ jobs:
with:
go-version: "1.24"
release-workflow: "release.yaml"
secrets:
pat-token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
+16
View File
@@ -0,0 +1,16 @@
name: Pull Request
on:
pull_request:
branches:
- main
push:
branches:
- "**"
- "!main"
jobs:
pr-checks:
uses: lukaszraczylo/shared-actions/.github/workflows/go-pr.yaml@main
with:
go-version: "1.24"
+2 -2
View File
@@ -11,6 +11,7 @@ on:
- main
permissions:
id-token: write
contents: write
packages: write
@@ -22,5 +23,4 @@ jobs:
docker-enabled: true
rolling-release-tag: "v1"
semver-config: "config-release.yaml"
secrets:
homebrew-tap-token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
secrets: inherit
+36 -53
View File
@@ -7,7 +7,7 @@ before:
builds:
- id: semver-gen
main: .
binary: semver-gen
binary: semver-generator
env:
- CGO_ENABLED=0
goos:
@@ -24,7 +24,7 @@ builds:
archives:
- id: semver-gen
formats: [tar.gz]
name_template: "semver-gen-{{ .Version }}-{{ .Os }}-{{ .Arch }}"
name_template: "semver-generator-{{ .Os }}-{{ .Arch }}"
format_overrides:
- goos: windows
formats: [zip]
@@ -34,7 +34,7 @@ archives:
- config.yaml
checksum:
name_template: "semver-gen-{{ .Version }}-checksums.txt"
name_template: "semver-generator-checksums.txt"
algorithm: sha256
changelog:
@@ -55,59 +55,24 @@ release:
draft: false
prerelease: auto
dockers:
- id: semver-gen-amd64
goos: linux
goarch: amd64
ids:
- semver-gen
image_templates:
- "ghcr.io/lukaszraczylo/semver-generator:{{ .Version }}-amd64"
- "ghcr.io/lukaszraczylo/semver-generator:latest-amd64"
- "ghcr.io/lukaszraczylo/semver-generator:v1-amd64"
dockers_v2:
- images:
- "ghcr.io/lukaszraczylo/semver-generator"
tags:
- "{{ .Version }}"
- "latest"
- "v1"
platforms:
- linux/amd64
- linux/arm64
dockerfile: Dockerfile.goreleaser
use: buildx
build_flag_templates:
- "--platform=linux/amd64"
extra_files:
- config-release.yaml
- entrypoint.sh
- id: semver-gen-arm64
goos: linux
goarch: arm64
ids:
- semver-gen
image_templates:
- "ghcr.io/lukaszraczylo/semver-generator:{{ .Version }}-arm64"
- "ghcr.io/lukaszraczylo/semver-generator:latest-arm64"
- "ghcr.io/lukaszraczylo/semver-generator:v1-arm64"
dockerfile: Dockerfile.goreleaser
use: buildx
build_flag_templates:
- "--platform=linux/arm64"
extra_files:
- config-release.yaml
- entrypoint.sh
docker_manifests:
- name_template: "ghcr.io/lukaszraczylo/semver-generator:{{ .Version }}"
image_templates:
- "ghcr.io/lukaszraczylo/semver-generator:{{ .Version }}-amd64"
- "ghcr.io/lukaszraczylo/semver-generator:{{ .Version }}-arm64"
- name_template: "ghcr.io/lukaszraczylo/semver-generator:latest"
image_templates:
- "ghcr.io/lukaszraczylo/semver-generator:latest-amd64"
- "ghcr.io/lukaszraczylo/semver-generator:latest-arm64"
- name_template: "ghcr.io/lukaszraczylo/semver-generator:v1"
image_templates:
- "ghcr.io/lukaszraczylo/semver-generator:v1-amd64"
- "ghcr.io/lukaszraczylo/semver-generator:v1-arm64"
homebrew_casks:
- repository:
- name: semver-generator
repository:
owner: lukaszraczylo
name: homebrew-taps
token: "{{ .Env.HOMEBREW_TAP_TOKEN }}"
@@ -115,12 +80,30 @@ homebrew_casks:
homepage: https://github.com/lukaszraczylo/semver-generator
description: "Automatic semantic version generator based on git commit messages"
license: MIT
url:
verified: github.com/lukaszraczylo/semver-generator
hooks:
post:
install: |
if OS.mac?
system_command "/usr/bin/xattr",
args: ["-dr", "com.apple.quarantine", "#{staged_path}/semver-gen"]
args: ["-dr", "com.apple.quarantine", "#{staged_path}/semver-generator"]
end
signs:
- cmd: cosign
signature: "${artifact}.sigstore.json"
args:
- sign-blob
- "--bundle=${signature}"
- "${artifact}"
- "--yes"
artifacts: checksum
output: true
docker_signs:
- cmd: cosign
artifacts: manifests
output: true
args:
- sign
- "${artifact}@${digest}"
- "--yes"
+2 -1
View File
@@ -1,5 +1,6 @@
FROM ubuntu:jammy
COPY semver-gen /go/src/app/semver-gen
ARG TARGETPLATFORM
COPY ${TARGETPLATFORM}/semver-generator /go/src/app/semver-generator
COPY config-release.yaml /go/src/app/config.yaml
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
+64 -8
View File
@@ -16,6 +16,7 @@ Project created overnight, to prove that management of semantic versioning is NO
- [Calculations example \[standard\]](#calculations-example-standard)
- [Calculations example \[strict matching\]](#calculations-example-strict-matching)
- [Release candidates](#release-candidates)
- [Tag prefix stripping](#tag-prefix-stripping)
- [Example configuration](#example-configuration)
- [Good to know](#good-to-know)
@@ -29,6 +30,7 @@ Project created overnight, to prove that management of semantic versioning is NO
* With flag `-e` or config `force.existing: true` the existing tags in versioning will be respected, helping you to avoid the version conflicts.
* With config `force.commit: deadbeef` where `deadbeef` is the commit hash - calculations will start from the specified commit.
* Tag prefix stripping: The `v` prefix is automatically stripped from tags (e.g., `v1.2.3``1.2.3`). Additional prefixes can be configured via `tag_prefixes` for monorepo setups (e.g., `app-1.2.3`, `infra-1.2.3`).
### Important changes
@@ -52,7 +54,7 @@ export GITHUB_TOKEN=yourPersonalApiToken
##### Homebrew (macOS)
```bash
brew install --cask lukaszraczylo/taps/semver-gen
brew install --cask lukaszraczylo/taps/semver-generator
```
##### Manual Download
@@ -63,9 +65,9 @@ You can download latest versions of the binaries from the [release page](https:/
Darwin ARM64/AMD64, Linux ARM64/AMD64, Windows AMD64
```bash
bash$ ./semver-gen generate -r https://github.com/nextapps-de/winbox
bash$ semver-generator generate -r https://github.com/nextapps-de/winbox
SEMVER 9.0.10
bash$ ./semver-gen generate -l
bash$ semver-generator generate -l
SEMVER 5.1.1
```
@@ -73,8 +75,8 @@ SEMVER 5.1.1
```yaml
Usage:
semver-gen generate [flags]
semver-gen [command]
semver-generator generate [flags]
semver-generator [command]
Available Commands:
generate Generates semantic version
@@ -84,7 +86,7 @@ Flags:
-c, --config string Path to config file (default "semver.yaml")
-d, --debug Enable debug mode
-e, --existing Respect existing tags
-h, --help help for semver-gen
-h, --help help for semver-generator
-l, --local Use local repository
-r, --repository string Remote repository URL. (default "https://github.com/lukaszraczylo/simple-gql-client")
-b, --branch string Remote repository URL Branch. (default "main")
@@ -98,7 +100,7 @@ Flags:
The binary can update itself to the latest version:
```bash
semver-gen -u
semver-generator -u
```
This downloads the latest release for your platform directly from GitHub releases. No authentication is required.
@@ -144,6 +146,25 @@ jobs:
docker pull ghcr.io/lukaszraczylo/semver-generator:latest
```
#### Verifying Release Signatures
All release checksums and Docker images are signed with [cosign](https://github.com/sigstore/cosign) using keyless signing. To verify:
```bash
# Verify checksum signature
cosign verify-blob \
--certificate-identity-regexp "https://github.com/lukaszraczylo/semver-generator/.*" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
--bundle "<checksums-file>.sigstore.json" \
<checksums-file>
# Verify Docker image
cosign verify \
--certificate-identity-regexp "https://github.com/lukaszraczylo/semver-generator/.*" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
ghcr.io/lukaszraczylo/semver-generator:latest
```
**Docker supported architectures:**
Linux/arm64, Linux/amd64
@@ -182,6 +203,36 @@ to generate the appropriate release in format `1.3.37-rc.1` and counting up unti
- add-rc
```
#### Tag prefix stripping
When using the `-e` (existing tags) flag, the semver-generator needs to parse existing git tags to determine the current version. Tags often include prefixes that need to be stripped before version parsing.
**Automatic `v` prefix stripping:**
The `v` prefix is always stripped automatically from tags. For example:
- `v1.2.3` → parsed as `1.2.3`
- `v0.5.0` → parsed as `0.5.0`
**Custom prefixes for monorepos:**
In monorepo setups where different components have their own versioned tags, you can configure additional prefixes to strip:
```yaml
tag_prefixes:
- "app-"
- "infra-"
- "api-"
- "frontend-"
```
With this configuration:
- `app-1.2.3` → parsed as `1.2.3`
- `infra-0.5.0` → parsed as `0.5.0`
- `api-2.0.0-rc.1` → parsed as `2.0.0-rc.1` (release candidate)
This is particularly useful when:
- You have multiple services/components in a single repository
- Your CI/CD creates tags with component prefixes
- You want to track versions separately for different parts of your codebase
#### Example configuration
```yaml
@@ -196,6 +247,10 @@ blacklist:
- "Merge pull request"
- "feature/"
- "feature:"
tag_prefixes:
- "app-"
- "infra-"
- "service-"
wording:
patch:
- update
@@ -215,9 +270,10 @@ wording:
* `force`: sets the "starting" version, you don't need to specify this section as the default is always `0`
* `force.commit`: allows you to set commit hash from which the calculations should start
* `blacklist`: terms to ignore when processing commits. Any commit containing these terms will be skipped in version calculations. Useful for ignoring merge commits, feature branch names, and other unwanted triggers.
* `tag_prefixes`: prefixes to strip from existing tags before parsing version numbers. Useful for monorepos where tags are prefixed with component names (e.g., `app-1.2.3`, `infra-0.5.0`). The `v` prefix is always stripped automatically.
* `wording`: words the program should look for in the git commits to increment (patch|minor|major)
### Good to know
### Good to knows
* Word matching uses fuzzy search AND is case INSENSITIVE
* I do not recommend using common words ( like "the" from the example configuration )
+6 -1
View File
@@ -121,7 +121,11 @@ func main() {
}
// List commits
utils.ListCommits(&repo.GitRepo)
if _, err := utils.ListCommits(&repo.GitRepo); err != nil {
utils.Error("Unable to list commits", map[string]interface{}{
"error": err.Error(),
})
}
// List existing tags if needed
if params.varExisting || repo.Config.Force.Existing {
@@ -140,6 +144,7 @@ func main() {
repo.Semver,
params.varExisting || repo.Config.Force.Existing,
params.varStrict || repo.Config.Force.Strict,
repo.Config.TagPrefixes,
)
// Print semantic version
+39 -18
View File
@@ -267,7 +267,7 @@ func (suite *Tests) Test_checkMatches() {
if tt.name == "No match" {
return nil
}
// For other test cases, match if the needle is in the haystack
for _, h := range haystack {
if strings.Contains(h, needle) || strings.Contains(needle, h) {
@@ -276,7 +276,7 @@ func (suite *Tests) Test_checkMatches() {
}
return nil
}
got := utils.CheckMatches(tt.args.content, tt.args.targets, tt.blacklist)
assertObj.Equal(tt.want, got, "Unexpected result in "+tt.name)
})
@@ -285,7 +285,8 @@ func (suite *Tests) Test_checkMatches() {
func (suite *Tests) Test_parseExistingSemver() {
type args struct {
tagName string
tagName string
prefixes []string
}
tests := []struct {
name string
@@ -296,7 +297,8 @@ func (suite *Tests) Test_parseExistingSemver() {
{
name: "Test parsing existing semver",
args: args{
tagName: "1.2.3",
tagName: "1.2.3",
prefixes: []string{},
},
currentSemver: utils.SemVer{Major: 1, Minor: 1, Patch: 1},
wantSemanticVersion: utils.SemVer{
@@ -308,7 +310,8 @@ func (suite *Tests) Test_parseExistingSemver() {
{
name: "Test parsing existing semver with v",
args: args{
tagName: "v1.2.3",
tagName: "v1.2.3",
prefixes: []string{"v"},
},
currentSemver: utils.SemVer{Major: 1, Minor: 1, Patch: 1},
wantSemanticVersion: utils.SemVer{
@@ -320,7 +323,8 @@ func (suite *Tests) Test_parseExistingSemver() {
{
name: "Test parsing existing semver with rc",
args: args{
tagName: "1.2.5-rc.7",
tagName: "1.2.5-rc.7",
prefixes: []string{},
},
currentSemver: utils.SemVer{Major: 1, Minor: 1, Patch: 1},
wantSemanticVersion: utils.SemVer{
@@ -331,10 +335,25 @@ func (suite *Tests) Test_parseExistingSemver() {
EnableReleaseCandidate: true,
},
},
{
name: "Test parsing prefixed tag without rc",
args: args{
tagName: "app-0.0.16",
prefixes: []string{"app-", "infra-"},
},
currentSemver: utils.SemVer{Major: 1, Minor: 1, Patch: 1},
wantSemanticVersion: utils.SemVer{
Major: 0,
Minor: 0,
Patch: 16,
EnableReleaseCandidate: false,
},
},
{
name: "Test invalid semver format",
args: args{
tagName: "invalid",
tagName: "invalid",
prefixes: []string{},
},
currentSemver: utils.SemVer{Major: 2, Minor: 3, Patch: 4},
wantSemanticVersion: utils.SemVer{
@@ -346,7 +365,8 @@ func (suite *Tests) Test_parseExistingSemver() {
{
name: "Test partial semver",
args: args{
tagName: "1.2",
tagName: "1.2",
prefixes: []string{},
},
currentSemver: utils.SemVer{Major: 2, Minor: 3, Patch: 4},
wantSemanticVersion: utils.SemVer{
@@ -358,7 +378,8 @@ func (suite *Tests) Test_parseExistingSemver() {
{
name: "Test empty tag",
args: args{
tagName: "",
tagName: "",
prefixes: []string{},
},
currentSemver: utils.SemVer{Major: 2, Minor: 3, Patch: 4},
wantSemanticVersion: utils.SemVer{
@@ -370,7 +391,7 @@ func (suite *Tests) Test_parseExistingSemver() {
}
for _, tt := range tests {
suite.T().Run(tt.name, func(t *testing.T) {
got := utils.ParseExistingSemver(tt.args.tagName, tt.currentSemver)
got := utils.ParseExistingSemver(tt.args.tagName, tt.currentSemver, tt.args.prefixes)
assertObj.Equal(tt.wantSemanticVersion.Major, got.Major, "Unexpected MAJOR semver result in "+tt.name)
assertObj.Equal(tt.wantSemanticVersion.Minor, got.Minor, "Unexpected MINOR semver result in "+tt.name)
assertObj.Equal(tt.wantSemanticVersion.Patch, got.Patch, "Unexpected PATCH semver result in "+tt.name)
@@ -382,10 +403,10 @@ func (suite *Tests) Test_parseExistingSemver() {
func (suite *Tests) TestSetup_ListCommits() {
type fields struct {
RepositoryName string
RepositoryBranch string
LocalConfigFile string
GitRepo utils.GitRepository
RepositoryName string
RepositoryBranch string
LocalConfigFile string
GitRepo utils.GitRepository
}
tests := []struct {
@@ -441,23 +462,23 @@ func (suite *Tests) TestSetup_ListCommits() {
if tt.name == "List commits from existing repository" {
t.Skip("Skipping test that requires repository access")
}
s := &Setup{
RepositoryName: tt.fields.RepositoryName,
RepositoryBranch: tt.fields.RepositoryBranch,
GitRepo: tt.fields.GitRepo,
}
config, _ := utils.ReadConfig(tt.fields.LocalConfigFile)
s.Config = config
err := utils.PrepareRepository(&s.GitRepo)
if err != nil && !tt.wantErr {
if tt.name != "List commits starting with certain hash" {
t.Fatalf("Failed to prepare repository: %v", err)
}
}
if err == nil {
listOfCommits, err := utils.ListCommits(&s.GitRepo)
if !tt.wantErr {
+22 -12
View File
@@ -26,26 +26,36 @@ type Force struct {
// Config represents the application configuration
type Config struct {
Wording Wording
Force Force
Blacklist []string
Wording Wording
Force Force
Blacklist []string
TagPrefixes []string // Prefixes to strip from tags before parsing (e.g., "app-", "infra-", "v")
}
// ReadConfig reads the configuration from a file
func ReadConfig(file string) (*Config, error) {
config := &Config{}
viper.SetConfigFile(file)
err := viper.ReadInConfig()
if err != nil {
err = fmt.Errorf("fatal error config file: %s", err)
return config, err
}
viper.UnmarshalKey("wording", &config.Wording)
viper.UnmarshalKey("force", &config.Force)
viper.UnmarshalKey("blacklist", &config.Blacklist)
if err := viper.UnmarshalKey("wording", &config.Wording); err != nil {
return config, fmt.Errorf("error parsing wording config: %w", err)
}
if err := viper.UnmarshalKey("force", &config.Force); err != nil {
return config, fmt.Errorf("error parsing force config: %w", err)
}
if err := viper.UnmarshalKey("blacklist", &config.Blacklist); err != nil {
return config, fmt.Errorf("error parsing blacklist config: %w", err)
}
if err := viper.UnmarshalKey("tag_prefixes", &config.TagPrefixes); err != nil {
return config, fmt.Errorf("error parsing tag_prefixes config: %w", err)
}
return config, nil
}
@@ -55,14 +65,14 @@ func ApplyForcedVersioning(force Force, semver *SemVer) {
Debug("Forced versioning (MAJOR)", map[string]interface{}{"major": force.Major})
semver.Major = force.Major
}
if force.Minor > 0 {
Debug("Forced versioning (MINOR)", map[string]interface{}{"minor": force.Minor})
semver.Minor = force.Minor
}
if force.Patch > 0 {
Debug("Forced versioning (PATCH)", map[string]interface{}{"patch": force.Patch})
semver.Patch = force.Patch
}
}
}
+40 -32
View File
@@ -29,14 +29,14 @@ type TagDetails struct {
// GitRepository represents a git repository
type GitRepository struct {
Handler *git.Repository
Name string
Branch string
LocalPath string
UseLocal bool
Commits []CommitDetails
Tags []TagDetails
StartCommit string
Handler *git.Repository
Name string
Branch string
LocalPath string
UseLocal bool
Commits []CommitDetails
Tags []TagDetails
StartCommit string
}
// PrepareRepository prepares the git repository for use
@@ -47,15 +47,15 @@ func PrepareRepository(repo *GitRepository) error {
u, err := url.Parse(repo.Name)
if err != nil {
Error("Unable to parse repository URL", map[string]interface{}{
"error": err.Error(),
"url": repo.Name,
"error": err.Error(),
"url": repo.Name,
})
return err
}
repo.LocalPath = fmt.Sprintf("/tmp/semver/%s/%s", u.Path, repo.Branch)
os.RemoveAll(repo.LocalPath)
_ = os.RemoveAll(repo.LocalPath) // Ignore error - directory may not exist
repo.Handler, err = git.PlainClone(repo.LocalPath, false, &git.CloneOptions{
URL: repo.Name,
ReferenceName: plumbing.NewBranchReferenceName(repo.Branch),
@@ -66,11 +66,11 @@ func PrepareRepository(repo *GitRepository) error {
},
Tags: git.AllTags,
})
if err != nil {
Error("Unable to clone repository", map[string]interface{}{
"error": err.Error(),
"url": repo.Name,
"error": err.Error(),
"url": repo.Name,
})
return err
}
@@ -79,14 +79,20 @@ func PrepareRepository(repo *GitRepository) error {
repo.Handler, err = git.PlainOpen(repo.LocalPath)
if err != nil {
Error("Unable to open local repository", map[string]interface{}{
"error": err.Error(),
"path": repo.LocalPath,
"error": err.Error(),
"path": repo.LocalPath,
})
return err
}
}
os.Chdir(repo.LocalPath)
if err := os.Chdir(repo.LocalPath); err != nil {
Error("Unable to change directory", map[string]interface{}{
"error": err.Error(),
"path": repo.LocalPath,
})
return err
}
return nil
}
@@ -105,14 +111,14 @@ func ListCommits(repo *GitRepository) ([]CommitDetails, error) {
if err != nil {
return []CommitDetails{}, err
}
commitsList, err := repo.Handler.Log(&git.LogOptions{From: ref.Hash()})
if err != nil {
return []CommitDetails{}, err
}
var tmpResults []CommitDetails
commitsList.ForEach(func(c *object.Commit) error {
if err := commitsList.ForEach(func(c *object.Commit) error {
tmpResults = append(tmpResults, CommitDetails{
Hash: c.Hash.String(),
Author: c.Author.String(),
@@ -123,17 +129,19 @@ func ListCommits(repo *GitRepository) ([]CommitDetails, error) {
return tmpResults[i].Timestamp.Unix() < tmpResults[j].Timestamp.Unix()
})
return nil
})
}); err != nil {
return []CommitDetails{}, err
}
Debug("Listing commits", map[string]interface{}{"commits": tmpResults})
// Filter commits starting from the specified commit if provided
if repo.StartCommit != "" {
for commitId, cmt := range tmpResults {
if cmt.Hash == repo.StartCommit {
Debug("Found commit match", map[string]interface{}{
"commit": cmt.Hash,
"index": commitId,
"index": commitId,
})
repo.Commits = tmpResults[commitId:]
break
@@ -150,32 +158,32 @@ func ListCommits(repo *GitRepository) ([]CommitDetails, error) {
// ListExistingTags lists all tags in the repository
func ListExistingTags(repo *GitRepository) {
Debug("Listing existing tags", nil)
// Check if Handler is nil to avoid panic
if repo.Handler == nil {
Debug("Repository handler is nil, skipping tag listing", nil)
return
}
refs, err := repo.Handler.Tags()
if err != nil {
Error("Unable to list tags", map[string]interface{}{"error": err.Error()})
return
}
if err := refs.ForEach(func(ref *plumbing.Reference) error {
repo.Tags = append(repo.Tags, TagDetails{
Name: ref.Name().Short(),
Hash: ref.Hash().String(),
})
Debug("Found tag", map[string]interface{}{
"tag": ref.Name().Short(),
"tag": ref.Name().Short(),
"hash": ref.Hash().String(),
})
return nil
}); err != nil {
Error("Error iterating tags", map[string]interface{}{"error": err.Error()})
}
}
}
+41 -15
View File
@@ -209,7 +209,7 @@ func downloadBinary(url string) (string, error) {
}
// Create temp file
tempFile, err := os.CreateTemp("", "semver-gen-update-*")
tempFile, err := os.CreateTemp("", "semver-generator-update-*")
if err != nil {
return "", err
}
@@ -219,30 +219,33 @@ func downloadBinary(url string) (string, error) {
if strings.HasSuffix(url, ".tar.gz") {
// For tar.gz, we need to extract the binary
if err := extractTarGz(resp.Body, tempFile); err != nil {
tempFile.Close()
os.Remove(tempPath)
_ = tempFile.Close()
_ = os.Remove(tempPath)
return "", err
}
} else {
// Direct binary download
if _, err := io.Copy(tempFile, resp.Body); err != nil {
tempFile.Close()
os.Remove(tempPath)
_ = tempFile.Close()
_ = os.Remove(tempPath)
return "", err
}
}
tempFile.Close()
if err := tempFile.Close(); err != nil {
_ = os.Remove(tempPath)
return "", err
}
return tempPath, nil
}
// extractTarGz extracts the semver-gen binary from a tar.gz archive
// extractTarGz extracts the semver-generator binary from a tar.gz archive
func extractTarGz(r io.Reader, destFile *os.File) error {
// For simplicity, we'll download the whole archive to a temp file first,
// then use tar command to extract. This avoids adding archive/tar dependency.
// Create temp archive file
archiveFile, err := os.CreateTemp("", "semver-gen-archive-*.tar.gz")
archiveFile, err := os.CreateTemp("", "semver-generator-archive-*.tar.gz")
if err != nil {
return err
}
@@ -250,13 +253,15 @@ func extractTarGz(r io.Reader, destFile *os.File) error {
defer os.Remove(archivePath)
if _, err := io.Copy(archiveFile, r); err != nil {
archiveFile.Close()
_ = archiveFile.Close()
return err
}
if err := archiveFile.Close(); err != nil {
return err
}
archiveFile.Close()
// Extract using tar command
extractDir, err := os.MkdirTemp("", "semver-gen-extract-*")
extractDir, err := os.MkdirTemp("", "semver-generator-extract-*")
if err != nil {
return err
}
@@ -268,22 +273,34 @@ func extractTarGz(r io.Reader, destFile *os.File) error {
return fmt.Errorf("failed to extract archive: %w", err)
}
// Find the semver-gen binary in the extracted files
// Find the binary in the extracted files
// Support both new name (semver-generator) and old name (semver-gen) for backwards compatibility
binaryPath := ""
entries, err := os.ReadDir(extractDir)
if err != nil {
return err
}
// First try to find semver-generator (new name)
for _, entry := range entries {
if entry.Name() == "semver-gen" || strings.HasPrefix(entry.Name(), "semver-gen") && !strings.Contains(entry.Name(), ".") {
if entry.Name() == "semver-generator" {
binaryPath = fmt.Sprintf("%s/%s", extractDir, entry.Name())
break
}
}
// Fallback to semver-gen (old name) for older releases
if binaryPath == "" {
return fmt.Errorf("semver-gen binary not found in archive")
for _, entry := range entries {
if entry.Name() == "semver-gen" {
binaryPath = fmt.Sprintf("%s/%s", extractDir, entry.Name())
break
}
}
}
if binaryPath == "" {
return fmt.Errorf("binary not found in archive (looked for semver-generator and semver-gen)")
}
// Copy the binary to the destination
@@ -324,6 +341,7 @@ var runCommandFunc = func(cmdStr string) error {
// replaceBinary replaces the current binary with the new one
func replaceBinary(newBinary, currentBinary string) error {
// Make the new binary executable
// #nosec G302 -- 0755 is required for executable binaries
if err := os.Chmod(newBinary, 0755); err != nil {
return err
}
@@ -338,13 +356,17 @@ func replaceBinary(newBinary, currentBinary string) error {
}
// copyFile copies a file from src to dst
// Note: This function is only called internally with controlled paths from
// os.CreateTemp and os.Executable, not with user-supplied paths.
func copyFile(src, dst string) error {
// #nosec G304 -- src is from os.CreateTemp, not user input
srcFile, err := os.Open(src)
if err != nil {
return err
}
defer srcFile.Close()
// #nosec G304 -- dst is from os.Executable, not user input
dstFile, err := os.Create(dst)
if err != nil {
return err
@@ -356,6 +378,7 @@ func copyFile(src, dst string) error {
}
// Make executable
// #nosec G302 -- 0755 is required for executable binaries
return os.Chmod(dst, 0755)
}
@@ -397,7 +420,10 @@ func parseVersionParts(v string) []int {
for _, p := range parts {
var num int
fmt.Sscanf(p, "%d", &num)
if _, err := fmt.Sscanf(p, "%d", &num); err != nil {
// If parsing fails, use 0 for this part
num = 0
}
result = append(result, num)
}
+13 -12
View File
@@ -13,6 +13,7 @@ func CalculateSemver(
initialSemver SemVer,
respectExisting bool,
strictMode bool,
tagPrefixes []string,
) SemVer {
semver := initialSemver
@@ -22,10 +23,10 @@ func CalculateSemver(
for _, tagHash := range tags {
if commit.Hash == tagHash.Hash {
Debug("Found existing tag", map[string]interface{}{
"tag": tagHash.Name,
"tag": tagHash.Name,
"commit": strings.TrimSuffix(commit.Message, "\n"),
})
semver = ParseExistingSemver(tagHash.Name, semver)
semver = ParseExistingSemver(tagHash.Name, semver, tagPrefixes)
continue
}
}
@@ -35,7 +36,7 @@ func CalculateSemver(
if !strictMode {
semver.Patch++
Debug("Incrementing patch (DEFAULT)", map[string]interface{}{
"commit": strings.TrimSuffix(commit.Message, "\n"),
"commit": strings.TrimSuffix(commit.Message, "\n"),
"semver": FormatSemver(semver),
})
}
@@ -55,44 +56,44 @@ func CalculateSemver(
semver.EnableReleaseCandidate = false
semver.Release = 0
Debug("Incrementing major (WORDING)", map[string]interface{}{
"commit": strings.TrimSuffix(commit.Message, "\n"),
"commit": strings.TrimSuffix(commit.Message, "\n"),
"semver": FormatSemver(semver),
})
continue
}
if matchMinor {
semver.Minor++
semver.Patch = 1
semver.EnableReleaseCandidate = false
semver.Release = 0
Debug("Incrementing minor (WORDING)", map[string]interface{}{
"commit": strings.TrimSuffix(commit.Message, "\n"),
"commit": strings.TrimSuffix(commit.Message, "\n"),
"semver": FormatSemver(semver),
})
continue
}
if matchReleaseCandidate {
semver.Release++
semver.Patch = 1
semver.EnableReleaseCandidate = true
Debug("Incrementing release candidate (WORDING)", map[string]interface{}{
"commit": strings.TrimSuffix(commit.Message, "\n"),
"commit": strings.TrimSuffix(commit.Message, "\n"),
"semver": FormatSemver(semver),
})
continue
}
if matchPatch {
semver.Patch++
Debug("Incrementing patch (WORDING)", map[string]interface{}{
"commit": strings.TrimSuffix(commit.Message, "\n"),
"commit": strings.TrimSuffix(commit.Message, "\n"),
"semver": FormatSemver(semver),
})
continue
}
}
return semver
}
}
+52 -11
View File
@@ -19,7 +19,7 @@ func TestCalculateSemver(t *testing.T) {
// More sophisticated mock implementation for testing
for _, h := range haystack {
// Check for substring match to better simulate fuzzy search
if h == needle || (len(h) >= 3 && len(needle) >= 3 &&
if h == needle || (len(h) >= 3 && len(needle) >= 3 &&
(h[:3] == needle[:3] || h[len(h)-3:] == needle[len(needle)-3:])) {
return []string{h}
}
@@ -29,7 +29,7 @@ func TestCalculateSemver(t *testing.T) {
// Test data
now := time.Now()
// Common wording and blacklist for all tests
wording := Wording{
Patch: []string{"update", "fix", "initial"},
@@ -49,6 +49,7 @@ func TestCalculateSemver(t *testing.T) {
initialSemver SemVer
respectExisting bool
strictMode bool
tagPrefixes []string
want SemVer
}{
{
@@ -76,11 +77,12 @@ func TestCalculateSemver(t *testing.T) {
initialSemver: SemVer{},
respectExisting: true,
strictMode: false,
tagPrefixes: []string{},
want: SemVer{
Major: 2,
Minor: 0,
Patch: 1, // Initial tag 2.0.0 + one patch increment
Release: 1,
Major: 2,
Minor: 0,
Patch: 1, // Initial tag 2.0.0 + one patch increment
Release: 1,
EnableReleaseCandidate: true,
},
},
@@ -109,11 +111,12 @@ func TestCalculateSemver(t *testing.T) {
initialSemver: SemVer{},
respectExisting: true,
strictMode: true,
tagPrefixes: []string{},
want: SemVer{
Major: 2,
Minor: 0,
Patch: 1, // Initial tag 2.0.0 + patch from "update" keyword
Release: 1,
Major: 2,
Minor: 0,
Patch: 1, // Initial tag 2.0.0 + patch from "update" keyword
Release: 1,
EnableReleaseCandidate: true,
},
},
@@ -142,6 +145,7 @@ func TestCalculateSemver(t *testing.T) {
initialSemver: SemVer{},
respectExisting: false,
strictMode: false,
tagPrefixes: []string{},
want: SemVer{
Major: 0,
Minor: 1,
@@ -173,6 +177,7 @@ func TestCalculateSemver(t *testing.T) {
initialSemver: SemVer{Major: 1},
respectExisting: false,
strictMode: true,
tagPrefixes: []string{},
want: SemVer{
Major: 1,
Minor: 1,
@@ -199,6 +204,7 @@ func TestCalculateSemver(t *testing.T) {
initialSemver: SemVer{},
respectExisting: false,
strictMode: false,
tagPrefixes: []string{},
want: SemVer{
Major: 0,
Minor: 0,
@@ -225,6 +231,7 @@ func TestCalculateSemver(t *testing.T) {
initialSemver: SemVer{},
respectExisting: false,
strictMode: false,
tagPrefixes: []string{},
want: SemVer{
Major: 0,
Minor: 0,
@@ -233,6 +240,39 @@ func TestCalculateSemver(t *testing.T) {
EnableReleaseCandidate: true,
},
},
{
name: "With prefixed tags should not be RC",
commits: []CommitDetails{
{
Hash: "commit1",
Message: "tagged commit",
Timestamp: now.Add(-3 * time.Hour),
},
{
Hash: "commit2",
Message: "another commit",
Timestamp: now.Add(-2 * time.Hour),
},
},
tags: []TagDetails{
{
Name: "app-0.0.16",
Hash: "commit1",
},
},
wording: wording,
blacklist: blacklist,
initialSemver: SemVer{},
respectExisting: true,
strictMode: true, // Use strict mode for predictable results
tagPrefixes: []string{"app-", "infra-"},
want: SemVer{
Major: 0,
Minor: 0,
Patch: 16, // From tag, no additional increments in strict mode
EnableReleaseCandidate: false,
},
},
}
for _, tt := range tests {
@@ -245,6 +285,7 @@ func TestCalculateSemver(t *testing.T) {
tt.initialSemver,
tt.respectExisting,
tt.strictMode,
tt.tagPrefixes,
)
assert.Equal(t, tt.want.Major, got.Major, "Major version mismatch")
@@ -254,4 +295,4 @@ func TestCalculateSemver(t *testing.T) {
assert.Equal(t, tt.want.EnableReleaseCandidate, got.EnableReleaseCandidate, "EnableReleaseCandidate mismatch")
})
}
}
}
+72 -22
View File
@@ -51,52 +51,102 @@ func FormatSemver(semver SemVer) string {
var extractNumber = regexp.MustCompile("[0-9]+")
// StripTagPrefix removes configured prefixes from a tag name
// The "v" prefix is always stripped by default (e.g., v1.2.3 -> 1.2.3)
func StripTagPrefix(tagName string, prefixes []string) string {
result := tagName
// Always strip "v" prefix by default
if strings.HasPrefix(result, "v") && len(result) > 1 {
// Only strip if followed by a digit (to avoid stripping "version-1.0.0")
if result[1] >= '0' && result[1] <= '9' {
result = result[1:]
Debug("Stripped default 'v' prefix from tag", map[string]interface{}{
"original": tagName,
"result": result,
})
}
}
// Then strip any user-configured prefixes
for _, prefix := range prefixes {
if strings.HasPrefix(result, prefix) {
result = strings.TrimPrefix(result, prefix)
Debug("Stripped prefix from tag", map[string]interface{}{
"original": tagName,
"prefix": prefix,
"result": result,
})
break // Only strip one prefix
}
}
return result
}
// ParseExistingSemver parses a semantic version from a tag name
func ParseExistingSemver(tagName string, currentSemver SemVer) SemVer {
func ParseExistingSemver(tagName string, currentSemver SemVer, prefixes []string) SemVer {
Debug("Parsing existing semver", map[string]interface{}{"tag": tagName})
tagNameParts := strings.Split(tagName, ".")
// Strip configured prefixes before parsing
cleanTagName := StripTagPrefix(tagName, prefixes)
// Check for release candidate pattern (-rc.X) before splitting
isReleaseCandidate := false
rcVersion := 0
if idx := strings.Index(cleanTagName, "-rc."); idx != -1 {
isReleaseCandidate = true
rcPart := cleanTagName[idx+4:] // Get everything after "-rc."
rcMatches := extractNumber.FindAllString(rcPart, 1)
if len(rcMatches) > 0 {
rcVersion, _ = strconv.Atoi(rcMatches[0])
}
// Remove the RC suffix for version parsing
cleanTagName = cleanTagName[:idx]
Debug("Detected release candidate", map[string]interface{}{
"rc_version": rcVersion,
"clean_tag_name": cleanTagName,
})
}
tagNameParts := strings.Split(cleanTagName, ".")
if len(tagNameParts) < 3 {
Debug("Unable to parse incompatible semver (non x.y.z)", map[string]interface{}{"tag": tagName})
return currentSemver
}
semanticVersion := SemVer{}
// Extract major version
majorMatches := extractNumber.FindAllString(tagNameParts[0], -1)
if len(majorMatches) > 0 {
semanticVersion.Major, _ = strconv.Atoi(majorMatches[0])
}
// Extract minor version
minorMatches := extractNumber.FindAllString(tagNameParts[1], -1)
if len(minorMatches) > 0 {
semanticVersion.Minor, _ = strconv.Atoi(minorMatches[0])
}
// Extract patch version
patchMatches := extractNumber.FindAllString(tagNameParts[2], -1)
if len(patchMatches) > 0 {
semanticVersion.Patch, _ = strconv.Atoi(patchMatches[0])
}
// Extract release candidate version if present
if len(tagNameParts) > 3 {
releaseMatches := extractNumber.FindAllString(tagNameParts[3], -1)
if len(releaseMatches) > 0 {
semanticVersion.Release, _ = strconv.Atoi(releaseMatches[0])
semanticVersion.EnableReleaseCandidate = true
}
// Set release candidate if detected
if isReleaseCandidate {
semanticVersion.Release = rcVersion
semanticVersion.EnableReleaseCandidate = true
}
return semanticVersion
}
// CheckMatches checks if any of the targets match the content
func CheckMatches(content []string, targets []string, blacklist []string) bool {
contentStr := strings.Join(content, " ")
// First check if any target matches
hasMatch := false
for _, tgt := range targets {
@@ -104,8 +154,8 @@ func CheckMatches(content []string, targets []string, blacklist []string) bool {
if len(matches) > 0 {
hasMatch = true
Debug("Found match", map[string]interface{}{
"target": tgt,
"match": strings.Join(matches, ","),
"target": tgt,
"match": strings.Join(matches, ","),
"content": contentStr,
})
break
@@ -117,14 +167,14 @@ func CheckMatches(content []string, targets []string, blacklist []string) bool {
for _, blacklistTerm := range blacklist {
if strings.Contains(strings.ToLower(contentStr), strings.ToLower(blacklistTerm)) {
Debug("Blacklisted term detected, ignoring commit", map[string]interface{}{
"content": contentStr,
"content": contentStr,
"blacklist_term": blacklistTerm,
})
return false
}
}
}
return hasMatch
}
@@ -132,4 +182,4 @@ func CheckMatches(content []string, targets []string, blacklist []string) bool {
var FuzzyFind = func(needle string, haystack []string) []string {
// This will be replaced with the actual implementation in main.go
return nil
}
}
+64 -11
View File
@@ -58,15 +58,17 @@ func TestParseExistingSemver(t *testing.T) {
InitLogger(false)
tests := []struct {
name string
tagName string
name string
tagName string
currentSemver SemVer
want SemVer
prefixes []string
want SemVer
}{
{
name: "Standard semver",
tagName: "1.2.3",
name: "Standard semver",
tagName: "1.2.3",
currentSemver: SemVer{},
prefixes: []string{},
want: SemVer{
Major: 1,
Minor: 2,
@@ -74,9 +76,10 @@ func TestParseExistingSemver(t *testing.T) {
},
},
{
name: "With v prefix",
tagName: "v2.3.4",
name: "With v prefix configured",
tagName: "v2.3.4",
currentSemver: SemVer{},
prefixes: []string{"v"},
want: SemVer{
Major: 2,
Minor: 3,
@@ -84,9 +87,32 @@ func TestParseExistingSemver(t *testing.T) {
},
},
{
name: "With release candidate",
tagName: "3.4.5-rc.2",
name: "With app- prefix configured",
tagName: "app-1.2.3",
currentSemver: SemVer{},
prefixes: []string{"app-", "infra-"},
want: SemVer{
Major: 1,
Minor: 2,
Patch: 3,
},
},
{
name: "With prefix but not in config - should still parse numbers",
tagName: "v2.3.4",
currentSemver: SemVer{},
prefixes: []string{}, // v not in prefixes
want: SemVer{
Major: 2,
Minor: 3,
Patch: 4,
},
},
{
name: "With release candidate",
tagName: "3.4.5-rc.2",
currentSemver: SemVer{},
prefixes: []string{},
want: SemVer{
Major: 3,
Minor: 4,
@@ -95,6 +121,31 @@ func TestParseExistingSemver(t *testing.T) {
EnableReleaseCandidate: true,
},
},
{
name: "With prefix and release candidate",
tagName: "app-1.0.0-rc.3",
currentSemver: SemVer{},
prefixes: []string{"app-"},
want: SemVer{
Major: 1,
Minor: 0,
Patch: 0,
Release: 3,
EnableReleaseCandidate: true,
},
},
{
name: "Prefixed tag without RC should NOT be RC",
tagName: "app-0.0.16",
currentSemver: SemVer{},
prefixes: []string{"app-"},
want: SemVer{
Major: 0,
Minor: 0,
Patch: 16,
EnableReleaseCandidate: false,
},
},
{
name: "Invalid format",
tagName: "not-a-semver",
@@ -103,6 +154,7 @@ func TestParseExistingSemver(t *testing.T) {
Minor: 1,
Patch: 1,
},
prefixes: []string{},
want: SemVer{
Major: 1,
Minor: 1,
@@ -117,6 +169,7 @@ func TestParseExistingSemver(t *testing.T) {
Minor: 5,
Patch: 5,
},
prefixes: []string{},
want: SemVer{
Major: 5,
Minor: 5,
@@ -127,7 +180,7 @@ func TestParseExistingSemver(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ParseExistingSemver(tt.tagName, tt.currentSemver)
got := ParseExistingSemver(tt.tagName, tt.currentSemver, tt.prefixes)
assert.Equal(t, tt.want.Major, got.Major, "Major version mismatch")
assert.Equal(t, tt.want.Minor, got.Minor, "Minor version mismatch")
assert.Equal(t, tt.want.Patch, got.Patch, "Patch version mismatch")
@@ -196,4 +249,4 @@ func TestCheckMatches(t *testing.T) {
assert.Equal(t, tt.want, got)
})
}
}
}
+5
View File
@@ -5,6 +5,11 @@ force:
existing: true
strict: false
commit: 960207e4677476ad31a9f389f74eaf9f33d49613
# tag_prefixes: (optional) prefixes to strip from existing tags
# Note: "v" prefix is always stripped automatically
# tag_prefixes:
# - "app-"
# - "service-"
wording:
patch:
- update
+4
View File
@@ -8,6 +8,10 @@ blacklist:
- "Merge pull request"
- "feature/"
- "feature:"
tag_prefixes:
# Note: "v" prefix is stripped automatically, no need to include it here
- "app-"
- "infra-"
wording:
patch:
- update
+9 -9
View File
@@ -157,10 +157,10 @@
<div class="w-3 h-3 rounded-full bg-green-500"></div>
<span class="ml-2 text-gray-400 text-sm">terminal</span>
</div>
<pre class="text-gray-100 text-sm sm:text-base overflow-x-auto"><code><span class="text-gray-400">$</span> semver-gen generate -l
<pre class="text-gray-100 text-sm sm:text-base overflow-x-auto"><code><span class="text-gray-400">$</span> semver-generator generate -l
<span class="text-emerald-400">SEMVER</span> 1.5.2
<span class="text-gray-400">$</span> semver-gen generate -r https://github.com/user/repo
<span class="text-gray-400">$</span> semver-generator generate -r https://github.com/user/repo
<span class="text-emerald-400">SEMVER</span> 2.3.0</code></pre>
</div>
</div>
@@ -260,7 +260,7 @@
<i class="fas fa-beer mr-2 text-amber-500"></i>
Homebrew (macOS)
</h3>
<pre class="bg-gray-900 text-gray-100 p-4 rounded-lg overflow-x-auto"><code>brew install --cask lukaszraczylo/taps/semver-gen</code></pre>
<pre class="bg-gray-900 text-gray-100 p-4 rounded-lg overflow-x-auto"><code>brew install --cask lukaszraczylo/taps/semver-generator</code></pre>
</div>
<div class="glass p-6 rounded-xl">
<h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-3 flex items-center">
@@ -295,22 +295,22 @@
CLI Usage
</h3>
<pre class="bg-gray-900 text-gray-100 p-4 rounded-lg overflow-x-auto mb-4"><code><span class="text-gray-400"># Local repository</span>
semver-gen generate -l
semver-generator generate -l
<span class="text-gray-400"># Remote repository</span>
semver-gen generate -r https://github.com/user/repo
semver-generator generate -r https://github.com/user/repo
<span class="text-gray-400"># With custom config</span>
semver-gen generate -l -c semver.yaml
semver-generator generate -l -c semver.yaml
<span class="text-gray-400"># Strict mode (only exact matches)</span>
semver-gen generate -l -s
semver-generator generate -l -s
<span class="text-gray-400"># Respect existing tags</span>
semver-gen generate -l -e
semver-generator generate -l -e
<span class="text-gray-400"># Self-update to latest version (no auth required)</span>
semver-gen -u</code></pre>
semver-generator -u</code></pre>
<div class="grid sm:grid-cols-2 gap-4 text-sm">
<div>
<h4 class="font-medium text-gray-900 dark:text-gray-100 mb-2">Flags</h4>
+2 -2
View File
@@ -60,11 +60,11 @@ if [[ ! -z "$INPUT_DEBUGMODE" ]]; then
echo "----"
echo "FLAGS: $FLAGS"
echo "----"
/go/src/app/semver-gen generate $FLAGS $*
/go/src/app/semver-generator generate $FLAGS $*
echo "----"
fi
OUT_SEMVER_GEN=$(/go/src/app/semver-gen generate $FLAGS $*)
OUT_SEMVER_GEN=$(/go/src/app/semver-generator generate $FLAGS $*)
[ $? -eq 0 ] || exit 1
CLEAN_SEMVER=$(echo $OUT_SEMVER_GEN | sed -e 's|SEMVER ||g')
echo "semantic_version=$CLEAN_SEMVER" >> $GITHUB_OUTPUT
+1 -1
View File
@@ -7,7 +7,7 @@ toolchain go1.24.6
require (
github.com/go-git/go-git/v5 v5.16.4
github.com/lithammer/fuzzysearch v1.1.8
github.com/lukaszraczylo/graphql-monitoring-proxy v0.41.20
github.com/lukaszraczylo/graphql-monitoring-proxy v0.42.4
github.com/lukaszraczylo/pandati v0.0.29
github.com/spf13/cobra v1.10.2
github.com/spf13/viper v1.21.0
+2 -2
View File
@@ -65,8 +65,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4=
github.com/lukaszraczylo/graphql-monitoring-proxy v0.41.20 h1:554N+HD5cTY074Y0LrL82cYQNCG1qDV3QKULgdLovs0=
github.com/lukaszraczylo/graphql-monitoring-proxy v0.41.20/go.mod h1:1FLcH7q+7cjUgQxyeVeF7ouBamGpcJZgqDF+j+cuFxI=
github.com/lukaszraczylo/graphql-monitoring-proxy v0.42.4 h1:gq/bEq+JzPes8WR24HHys9VvVWREDEXOWFoVSOFXCCw=
github.com/lukaszraczylo/graphql-monitoring-proxy v0.42.4/go.mod h1:1FLcH7q+7cjUgQxyeVeF7ouBamGpcJZgqDF+j+cuFxI=
github.com/lukaszraczylo/pandati v0.0.29 h1:WUEWm1+hWjE5KJbIL8OctG00x2dk4XKGJSlrjhxZ55k=
github.com/lukaszraczylo/pandati v0.0.29/go.mod h1:+DyTWKFaXd+jIfe7GW5w2S5PyTko/RXxMyOa+Vl713A=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=