Compare commits

...

15 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
lukaszraczylo b80929ff52 fixup! Move updater to REST api. 2025-12-07 15:31:03 +00:00
21 changed files with 509 additions and 211 deletions
+3
View File
@@ -8,6 +8,7 @@ on:
permissions: permissions:
contents: write contents: write
actions: write actions: write
pull-requests: write
jobs: jobs:
autoupdate: autoupdate:
@@ -15,3 +16,5 @@ jobs:
with: with:
go-version: "1.24" go-version: "1.24"
release-workflow: "release.yaml" 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 - main
permissions: permissions:
id-token: write
contents: write contents: write
packages: write packages: write
@@ -22,5 +23,4 @@ jobs:
docker-enabled: true docker-enabled: true
rolling-release-tag: "v1" rolling-release-tag: "v1"
semver-config: "config-release.yaml" semver-config: "config-release.yaml"
secrets: secrets: inherit
homebrew-tap-token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
+36 -53
View File
@@ -7,7 +7,7 @@ before:
builds: builds:
- id: semver-gen - id: semver-gen
main: . main: .
binary: semver-gen binary: semver-generator
env: env:
- CGO_ENABLED=0 - CGO_ENABLED=0
goos: goos:
@@ -24,7 +24,7 @@ builds:
archives: archives:
- id: semver-gen - id: semver-gen
formats: [tar.gz] formats: [tar.gz]
name_template: "semver-gen-{{ .Version }}-{{ .Os }}-{{ .Arch }}" name_template: "semver-generator-{{ .Os }}-{{ .Arch }}"
format_overrides: format_overrides:
- goos: windows - goos: windows
formats: [zip] formats: [zip]
@@ -34,7 +34,7 @@ archives:
- config.yaml - config.yaml
checksum: checksum:
name_template: "semver-gen-{{ .Version }}-checksums.txt" name_template: "semver-generator-checksums.txt"
algorithm: sha256 algorithm: sha256
changelog: changelog:
@@ -55,59 +55,24 @@ release:
draft: false draft: false
prerelease: auto prerelease: auto
dockers: dockers_v2:
- id: semver-gen-amd64 - images:
goos: linux - "ghcr.io/lukaszraczylo/semver-generator"
goarch: amd64 tags:
ids: - "{{ .Version }}"
- semver-gen - "latest"
image_templates: - "v1"
- "ghcr.io/lukaszraczylo/semver-generator:{{ .Version }}-amd64" platforms:
- "ghcr.io/lukaszraczylo/semver-generator:latest-amd64" - linux/amd64
- "ghcr.io/lukaszraczylo/semver-generator:v1-amd64" - linux/arm64
dockerfile: Dockerfile.goreleaser dockerfile: Dockerfile.goreleaser
use: buildx
build_flag_templates:
- "--platform=linux/amd64"
extra_files: extra_files:
- config-release.yaml - config-release.yaml
- entrypoint.sh - 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: homebrew_casks:
- repository: - name: semver-generator
repository:
owner: lukaszraczylo owner: lukaszraczylo
name: homebrew-taps name: homebrew-taps
token: "{{ .Env.HOMEBREW_TAP_TOKEN }}" token: "{{ .Env.HOMEBREW_TAP_TOKEN }}"
@@ -115,12 +80,30 @@ homebrew_casks:
homepage: https://github.com/lukaszraczylo/semver-generator homepage: https://github.com/lukaszraczylo/semver-generator
description: "Automatic semantic version generator based on git commit messages" description: "Automatic semantic version generator based on git commit messages"
license: MIT license: MIT
url:
verified: github.com/lukaszraczylo/semver-generator
hooks: hooks:
post: post:
install: | install: |
if OS.mac? if OS.mac?
system_command "/usr/bin/xattr", system_command "/usr/bin/xattr",
args: ["-dr", "com.apple.quarantine", "#{staged_path}/semver-gen"] args: ["-dr", "com.apple.quarantine", "#{staged_path}/semver-generator"]
end 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 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 config-release.yaml /go/src/app/config.yaml
COPY entrypoint.sh /entrypoint.sh COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh RUN chmod +x /entrypoint.sh
+74 -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 \[standard\]](#calculations-example-standard)
- [Calculations example \[strict matching\]](#calculations-example-strict-matching) - [Calculations example \[strict matching\]](#calculations-example-strict-matching)
- [Release candidates](#release-candidates) - [Release candidates](#release-candidates)
- [Tag prefix stripping](#tag-prefix-stripping)
- [Example configuration](#example-configuration) - [Example configuration](#example-configuration)
- [Good to know](#good-to-know) - [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 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. * 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 ### Important changes
@@ -52,7 +54,7 @@ export GITHUB_TOKEN=yourPersonalApiToken
##### Homebrew (macOS) ##### Homebrew (macOS)
```bash ```bash
brew install --cask lukaszraczylo/taps/semver-gen brew install --cask lukaszraczylo/taps/semver-generator
``` ```
##### Manual Download ##### 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 Darwin ARM64/AMD64, Linux ARM64/AMD64, Windows AMD64
```bash ```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 SEMVER 9.0.10
bash$ ./semver-gen generate -l bash$ semver-generator generate -l
SEMVER 5.1.1 SEMVER 5.1.1
``` ```
@@ -73,8 +75,8 @@ SEMVER 5.1.1
```yaml ```yaml
Usage: Usage:
semver-gen generate [flags] semver-generator generate [flags]
semver-gen [command] semver-generator [command]
Available Commands: Available Commands:
generate Generates semantic version generate Generates semantic version
@@ -84,15 +86,25 @@ Flags:
-c, --config string Path to config file (default "semver.yaml") -c, --config string Path to config file (default "semver.yaml")
-d, --debug Enable debug mode -d, --debug Enable debug mode
-e, --existing Respect existing tags -e, --existing Respect existing tags
-h, --help help for semver-gen -h, --help help for semver-generator
-l, --local Use local repository -l, --local Use local repository
-r, --repository string Remote repository URL. (default "https://github.com/lukaszraczylo/simple-gql-client") -r, --repository string Remote repository URL. (default "https://github.com/lukaszraczylo/simple-gql-client")
-b, --branch string Remote repository URL Branch. (default "main") -b, --branch string Remote repository URL Branch. (default "main")
-s, --strict Strict matching -s, --strict Strict matching
-u, --update Update binary with latest -u, --update Update binary with latest (no authentication required)
-v, --version Display version -v, --version Display version
``` ```
##### Self-Update
The binary can update itself to the latest version:
```bash
semver-generator -u
```
This downloads the latest release for your platform directly from GitHub releases. No authentication is required.
#### As a github action #### As a github action
```yaml ```yaml
@@ -134,6 +146,25 @@ jobs:
docker pull ghcr.io/lukaszraczylo/semver-generator:latest 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:** **Docker supported architectures:**
Linux/arm64, Linux/amd64 Linux/arm64, Linux/amd64
@@ -172,6 +203,36 @@ to generate the appropriate release in format `1.3.37-rc.1` and counting up unti
- add-rc - 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 #### Example configuration
```yaml ```yaml
@@ -186,6 +247,10 @@ blacklist:
- "Merge pull request" - "Merge pull request"
- "feature/" - "feature/"
- "feature:" - "feature:"
tag_prefixes:
- "app-"
- "infra-"
- "service-"
wording: wording:
patch: patch:
- update - update
@@ -205,9 +270,10 @@ wording:
* `force`: sets the "starting" version, you don't need to specify this section as the default is always `0` * `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 * `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. * `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) * `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 * Word matching uses fuzzy search AND is case INSENSITIVE
* I do not recommend using common words ( like "the" from the example configuration ) * 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 // 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 // List existing tags if needed
if params.varExisting || repo.Config.Force.Existing { if params.varExisting || repo.Config.Force.Existing {
@@ -140,6 +144,7 @@ func main() {
repo.Semver, repo.Semver,
params.varExisting || repo.Config.Force.Existing, params.varExisting || repo.Config.Force.Existing,
params.varStrict || repo.Config.Force.Strict, params.varStrict || repo.Config.Force.Strict,
repo.Config.TagPrefixes,
) )
// Print semantic version // Print semantic version
+33 -12
View File
@@ -285,7 +285,8 @@ func (suite *Tests) Test_checkMatches() {
func (suite *Tests) Test_parseExistingSemver() { func (suite *Tests) Test_parseExistingSemver() {
type args struct { type args struct {
tagName string tagName string
prefixes []string
} }
tests := []struct { tests := []struct {
name string name string
@@ -296,7 +297,8 @@ func (suite *Tests) Test_parseExistingSemver() {
{ {
name: "Test parsing existing semver", name: "Test parsing existing semver",
args: args{ args: args{
tagName: "1.2.3", tagName: "1.2.3",
prefixes: []string{},
}, },
currentSemver: utils.SemVer{Major: 1, Minor: 1, Patch: 1}, currentSemver: utils.SemVer{Major: 1, Minor: 1, Patch: 1},
wantSemanticVersion: utils.SemVer{ wantSemanticVersion: utils.SemVer{
@@ -308,7 +310,8 @@ func (suite *Tests) Test_parseExistingSemver() {
{ {
name: "Test parsing existing semver with v", name: "Test parsing existing semver with v",
args: args{ args: args{
tagName: "v1.2.3", tagName: "v1.2.3",
prefixes: []string{"v"},
}, },
currentSemver: utils.SemVer{Major: 1, Minor: 1, Patch: 1}, currentSemver: utils.SemVer{Major: 1, Minor: 1, Patch: 1},
wantSemanticVersion: utils.SemVer{ wantSemanticVersion: utils.SemVer{
@@ -320,7 +323,8 @@ func (suite *Tests) Test_parseExistingSemver() {
{ {
name: "Test parsing existing semver with rc", name: "Test parsing existing semver with rc",
args: args{ 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}, currentSemver: utils.SemVer{Major: 1, Minor: 1, Patch: 1},
wantSemanticVersion: utils.SemVer{ wantSemanticVersion: utils.SemVer{
@@ -331,10 +335,25 @@ func (suite *Tests) Test_parseExistingSemver() {
EnableReleaseCandidate: true, 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", name: "Test invalid semver format",
args: args{ args: args{
tagName: "invalid", tagName: "invalid",
prefixes: []string{},
}, },
currentSemver: utils.SemVer{Major: 2, Minor: 3, Patch: 4}, currentSemver: utils.SemVer{Major: 2, Minor: 3, Patch: 4},
wantSemanticVersion: utils.SemVer{ wantSemanticVersion: utils.SemVer{
@@ -346,7 +365,8 @@ func (suite *Tests) Test_parseExistingSemver() {
{ {
name: "Test partial semver", name: "Test partial semver",
args: args{ args: args{
tagName: "1.2", tagName: "1.2",
prefixes: []string{},
}, },
currentSemver: utils.SemVer{Major: 2, Minor: 3, Patch: 4}, currentSemver: utils.SemVer{Major: 2, Minor: 3, Patch: 4},
wantSemanticVersion: utils.SemVer{ wantSemanticVersion: utils.SemVer{
@@ -358,7 +378,8 @@ func (suite *Tests) Test_parseExistingSemver() {
{ {
name: "Test empty tag", name: "Test empty tag",
args: args{ args: args{
tagName: "", tagName: "",
prefixes: []string{},
}, },
currentSemver: utils.SemVer{Major: 2, Minor: 3, Patch: 4}, currentSemver: utils.SemVer{Major: 2, Minor: 3, Patch: 4},
wantSemanticVersion: utils.SemVer{ wantSemanticVersion: utils.SemVer{
@@ -370,7 +391,7 @@ func (suite *Tests) Test_parseExistingSemver() {
} }
for _, tt := range tests { for _, tt := range tests {
suite.T().Run(tt.name, func(t *testing.T) { 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.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.Minor, got.Minor, "Unexpected MINOR semver result in "+tt.name)
assertObj.Equal(tt.wantSemanticVersion.Patch, got.Patch, "Unexpected PATCH 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() { func (suite *Tests) TestSetup_ListCommits() {
type fields struct { type fields struct {
RepositoryName string RepositoryName string
RepositoryBranch string RepositoryBranch string
LocalConfigFile string LocalConfigFile string
GitRepo utils.GitRepository GitRepo utils.GitRepository
} }
tests := []struct { tests := []struct {
+16 -6
View File
@@ -26,9 +26,10 @@ type Force struct {
// Config represents the application configuration // Config represents the application configuration
type Config struct { type Config struct {
Wording Wording Wording Wording
Force Force Force Force
Blacklist []string Blacklist []string
TagPrefixes []string // Prefixes to strip from tags before parsing (e.g., "app-", "infra-", "v")
} }
// ReadConfig reads the configuration from a file // ReadConfig reads the configuration from a file
@@ -42,9 +43,18 @@ func ReadConfig(file string) (*Config, error) {
return config, err return config, err
} }
viper.UnmarshalKey("wording", &config.Wording) if err := viper.UnmarshalKey("wording", &config.Wording); err != nil {
viper.UnmarshalKey("force", &config.Force) return config, fmt.Errorf("error parsing wording config: %w", err)
viper.UnmarshalKey("blacklist", &config.Blacklist) }
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 return config, nil
} }
+25 -17
View File
@@ -29,14 +29,14 @@ type TagDetails struct {
// GitRepository represents a git repository // GitRepository represents a git repository
type GitRepository struct { type GitRepository struct {
Handler *git.Repository Handler *git.Repository
Name string Name string
Branch string Branch string
LocalPath string LocalPath string
UseLocal bool UseLocal bool
Commits []CommitDetails Commits []CommitDetails
Tags []TagDetails Tags []TagDetails
StartCommit string StartCommit string
} }
// PrepareRepository prepares the git repository for use // PrepareRepository prepares the git repository for use
@@ -48,13 +48,13 @@ func PrepareRepository(repo *GitRepository) error {
if err != nil { if err != nil {
Error("Unable to parse repository URL", map[string]interface{}{ Error("Unable to parse repository URL", map[string]interface{}{
"error": err.Error(), "error": err.Error(),
"url": repo.Name, "url": repo.Name,
}) })
return err return err
} }
repo.LocalPath = fmt.Sprintf("/tmp/semver/%s/%s", u.Path, repo.Branch) 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{ repo.Handler, err = git.PlainClone(repo.LocalPath, false, &git.CloneOptions{
URL: repo.Name, URL: repo.Name,
@@ -70,7 +70,7 @@ func PrepareRepository(repo *GitRepository) error {
if err != nil { if err != nil {
Error("Unable to clone repository", map[string]interface{}{ Error("Unable to clone repository", map[string]interface{}{
"error": err.Error(), "error": err.Error(),
"url": repo.Name, "url": repo.Name,
}) })
return err return err
} }
@@ -80,13 +80,19 @@ func PrepareRepository(repo *GitRepository) error {
if err != nil { if err != nil {
Error("Unable to open local repository", map[string]interface{}{ Error("Unable to open local repository", map[string]interface{}{
"error": err.Error(), "error": err.Error(),
"path": repo.LocalPath, "path": repo.LocalPath,
}) })
return err 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 return nil
} }
@@ -112,7 +118,7 @@ func ListCommits(repo *GitRepository) ([]CommitDetails, error) {
} }
var tmpResults []CommitDetails var tmpResults []CommitDetails
commitsList.ForEach(func(c *object.Commit) error { if err := commitsList.ForEach(func(c *object.Commit) error {
tmpResults = append(tmpResults, CommitDetails{ tmpResults = append(tmpResults, CommitDetails{
Hash: c.Hash.String(), Hash: c.Hash.String(),
Author: c.Author.String(), Author: c.Author.String(),
@@ -123,7 +129,9 @@ func ListCommits(repo *GitRepository) ([]CommitDetails, error) {
return tmpResults[i].Timestamp.Unix() < tmpResults[j].Timestamp.Unix() return tmpResults[i].Timestamp.Unix() < tmpResults[j].Timestamp.Unix()
}) })
return nil return nil
}) }); err != nil {
return []CommitDetails{}, err
}
Debug("Listing commits", map[string]interface{}{"commits": tmpResults}) Debug("Listing commits", map[string]interface{}{"commits": tmpResults})
@@ -133,7 +141,7 @@ func ListCommits(repo *GitRepository) ([]CommitDetails, error) {
if cmt.Hash == repo.StartCommit { if cmt.Hash == repo.StartCommit {
Debug("Found commit match", map[string]interface{}{ Debug("Found commit match", map[string]interface{}{
"commit": cmt.Hash, "commit": cmt.Hash,
"index": commitId, "index": commitId,
}) })
repo.Commits = tmpResults[commitId:] repo.Commits = tmpResults[commitId:]
break break
@@ -170,7 +178,7 @@ func ListExistingTags(repo *GitRepository) {
}) })
Debug("Found tag", map[string]interface{}{ Debug("Found tag", map[string]interface{}{
"tag": ref.Name().Short(), "tag": ref.Name().Short(),
"hash": ref.Hash().String(), "hash": ref.Hash().String(),
}) })
+41 -15
View File
@@ -209,7 +209,7 @@ func downloadBinary(url string) (string, error) {
} }
// Create temp file // Create temp file
tempFile, err := os.CreateTemp("", "semver-gen-update-*") tempFile, err := os.CreateTemp("", "semver-generator-update-*")
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -219,30 +219,33 @@ func downloadBinary(url string) (string, error) {
if strings.HasSuffix(url, ".tar.gz") { if strings.HasSuffix(url, ".tar.gz") {
// For tar.gz, we need to extract the binary // For tar.gz, we need to extract the binary
if err := extractTarGz(resp.Body, tempFile); err != nil { if err := extractTarGz(resp.Body, tempFile); err != nil {
tempFile.Close() _ = tempFile.Close()
os.Remove(tempPath) _ = os.Remove(tempPath)
return "", err return "", err
} }
} else { } else {
// Direct binary download // Direct binary download
if _, err := io.Copy(tempFile, resp.Body); err != nil { if _, err := io.Copy(tempFile, resp.Body); err != nil {
tempFile.Close() _ = tempFile.Close()
os.Remove(tempPath) _ = os.Remove(tempPath)
return "", err return "", err
} }
} }
tempFile.Close() if err := tempFile.Close(); err != nil {
_ = os.Remove(tempPath)
return "", err
}
return tempPath, nil 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 { func extractTarGz(r io.Reader, destFile *os.File) error {
// For simplicity, we'll download the whole archive to a temp file first, // 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. // then use tar command to extract. This avoids adding archive/tar dependency.
// Create temp archive file // 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 { if err != nil {
return err return err
} }
@@ -250,13 +253,15 @@ func extractTarGz(r io.Reader, destFile *os.File) error {
defer os.Remove(archivePath) defer os.Remove(archivePath)
if _, err := io.Copy(archiveFile, r); err != nil { if _, err := io.Copy(archiveFile, r); err != nil {
archiveFile.Close() _ = archiveFile.Close()
return err
}
if err := archiveFile.Close(); err != nil {
return err return err
} }
archiveFile.Close()
// Extract using tar command // Extract using tar command
extractDir, err := os.MkdirTemp("", "semver-gen-extract-*") extractDir, err := os.MkdirTemp("", "semver-generator-extract-*")
if err != nil { if err != nil {
return err return err
} }
@@ -268,22 +273,34 @@ func extractTarGz(r io.Reader, destFile *os.File) error {
return fmt.Errorf("failed to extract archive: %w", err) 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 := "" binaryPath := ""
entries, err := os.ReadDir(extractDir) entries, err := os.ReadDir(extractDir)
if err != nil { if err != nil {
return err return err
} }
// First try to find semver-generator (new name)
for _, entry := range entries { 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()) binaryPath = fmt.Sprintf("%s/%s", extractDir, entry.Name())
break break
} }
} }
// Fallback to semver-gen (old name) for older releases
if binaryPath == "" { 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 // 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 // replaceBinary replaces the current binary with the new one
func replaceBinary(newBinary, currentBinary string) error { func replaceBinary(newBinary, currentBinary string) error {
// Make the new binary executable // Make the new binary executable
// #nosec G302 -- 0755 is required for executable binaries
if err := os.Chmod(newBinary, 0755); err != nil { if err := os.Chmod(newBinary, 0755); err != nil {
return err return err
} }
@@ -338,13 +356,17 @@ func replaceBinary(newBinary, currentBinary string) error {
} }
// copyFile copies a file from src to dst // 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 { func copyFile(src, dst string) error {
// #nosec G304 -- src is from os.CreateTemp, not user input
srcFile, err := os.Open(src) srcFile, err := os.Open(src)
if err != nil { if err != nil {
return err return err
} }
defer srcFile.Close() defer srcFile.Close()
// #nosec G304 -- dst is from os.Executable, not user input
dstFile, err := os.Create(dst) dstFile, err := os.Create(dst)
if err != nil { if err != nil {
return err return err
@@ -356,6 +378,7 @@ func copyFile(src, dst string) error {
} }
// Make executable // Make executable
// #nosec G302 -- 0755 is required for executable binaries
return os.Chmod(dst, 0755) return os.Chmod(dst, 0755)
} }
@@ -397,7 +420,10 @@ func parseVersionParts(v string) []int {
for _, p := range parts { for _, p := range parts {
var num int 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) result = append(result, num)
} }
+3 -2
View File
@@ -13,6 +13,7 @@ func CalculateSemver(
initialSemver SemVer, initialSemver SemVer,
respectExisting bool, respectExisting bool,
strictMode bool, strictMode bool,
tagPrefixes []string,
) SemVer { ) SemVer {
semver := initialSemver semver := initialSemver
@@ -22,10 +23,10 @@ func CalculateSemver(
for _, tagHash := range tags { for _, tagHash := range tags {
if commit.Hash == tagHash.Hash { if commit.Hash == tagHash.Hash {
Debug("Found existing tag", map[string]interface{}{ Debug("Found existing tag", map[string]interface{}{
"tag": tagHash.Name, "tag": tagHash.Name,
"commit": strings.TrimSuffix(commit.Message, "\n"), "commit": strings.TrimSuffix(commit.Message, "\n"),
}) })
semver = ParseExistingSemver(tagHash.Name, semver) semver = ParseExistingSemver(tagHash.Name, semver, tagPrefixes)
continue continue
} }
} }
+49 -8
View File
@@ -49,6 +49,7 @@ func TestCalculateSemver(t *testing.T) {
initialSemver SemVer initialSemver SemVer
respectExisting bool respectExisting bool
strictMode bool strictMode bool
tagPrefixes []string
want SemVer want SemVer
}{ }{
{ {
@@ -76,11 +77,12 @@ func TestCalculateSemver(t *testing.T) {
initialSemver: SemVer{}, initialSemver: SemVer{},
respectExisting: true, respectExisting: true,
strictMode: false, strictMode: false,
tagPrefixes: []string{},
want: SemVer{ want: SemVer{
Major: 2, Major: 2,
Minor: 0, Minor: 0,
Patch: 1, // Initial tag 2.0.0 + one patch increment Patch: 1, // Initial tag 2.0.0 + one patch increment
Release: 1, Release: 1,
EnableReleaseCandidate: true, EnableReleaseCandidate: true,
}, },
}, },
@@ -109,11 +111,12 @@ func TestCalculateSemver(t *testing.T) {
initialSemver: SemVer{}, initialSemver: SemVer{},
respectExisting: true, respectExisting: true,
strictMode: true, strictMode: true,
tagPrefixes: []string{},
want: SemVer{ want: SemVer{
Major: 2, Major: 2,
Minor: 0, Minor: 0,
Patch: 1, // Initial tag 2.0.0 + patch from "update" keyword Patch: 1, // Initial tag 2.0.0 + patch from "update" keyword
Release: 1, Release: 1,
EnableReleaseCandidate: true, EnableReleaseCandidate: true,
}, },
}, },
@@ -142,6 +145,7 @@ func TestCalculateSemver(t *testing.T) {
initialSemver: SemVer{}, initialSemver: SemVer{},
respectExisting: false, respectExisting: false,
strictMode: false, strictMode: false,
tagPrefixes: []string{},
want: SemVer{ want: SemVer{
Major: 0, Major: 0,
Minor: 1, Minor: 1,
@@ -173,6 +177,7 @@ func TestCalculateSemver(t *testing.T) {
initialSemver: SemVer{Major: 1}, initialSemver: SemVer{Major: 1},
respectExisting: false, respectExisting: false,
strictMode: true, strictMode: true,
tagPrefixes: []string{},
want: SemVer{ want: SemVer{
Major: 1, Major: 1,
Minor: 1, Minor: 1,
@@ -199,6 +204,7 @@ func TestCalculateSemver(t *testing.T) {
initialSemver: SemVer{}, initialSemver: SemVer{},
respectExisting: false, respectExisting: false,
strictMode: false, strictMode: false,
tagPrefixes: []string{},
want: SemVer{ want: SemVer{
Major: 0, Major: 0,
Minor: 0, Minor: 0,
@@ -225,6 +231,7 @@ func TestCalculateSemver(t *testing.T) {
initialSemver: SemVer{}, initialSemver: SemVer{},
respectExisting: false, respectExisting: false,
strictMode: false, strictMode: false,
tagPrefixes: []string{},
want: SemVer{ want: SemVer{
Major: 0, Major: 0,
Minor: 0, Minor: 0,
@@ -233,6 +240,39 @@ func TestCalculateSemver(t *testing.T) {
EnableReleaseCandidate: true, 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 { for _, tt := range tests {
@@ -245,6 +285,7 @@ func TestCalculateSemver(t *testing.T) {
tt.initialSemver, tt.initialSemver,
tt.respectExisting, tt.respectExisting,
tt.strictMode, tt.strictMode,
tt.tagPrefixes,
) )
assert.Equal(t, tt.want.Major, got.Major, "Major version mismatch") assert.Equal(t, tt.want.Major, got.Major, "Major version mismatch")
+62 -12
View File
@@ -51,11 +51,64 @@ func FormatSemver(semver SemVer) string {
var extractNumber = regexp.MustCompile("[0-9]+") 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 // 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}) 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 { if len(tagNameParts) < 3 {
Debug("Unable to parse incompatible semver (non x.y.z)", map[string]interface{}{"tag": tagName}) Debug("Unable to parse incompatible semver (non x.y.z)", map[string]interface{}{"tag": tagName})
return currentSemver return currentSemver
@@ -81,13 +134,10 @@ func ParseExistingSemver(tagName string, currentSemver SemVer) SemVer {
semanticVersion.Patch, _ = strconv.Atoi(patchMatches[0]) semanticVersion.Patch, _ = strconv.Atoi(patchMatches[0])
} }
// Extract release candidate version if present // Set release candidate if detected
if len(tagNameParts) > 3 { if isReleaseCandidate {
releaseMatches := extractNumber.FindAllString(tagNameParts[3], -1) semanticVersion.Release = rcVersion
if len(releaseMatches) > 0 { semanticVersion.EnableReleaseCandidate = true
semanticVersion.Release, _ = strconv.Atoi(releaseMatches[0])
semanticVersion.EnableReleaseCandidate = true
}
} }
return semanticVersion return semanticVersion
@@ -104,8 +154,8 @@ func CheckMatches(content []string, targets []string, blacklist []string) bool {
if len(matches) > 0 { if len(matches) > 0 {
hasMatch = true hasMatch = true
Debug("Found match", map[string]interface{}{ Debug("Found match", map[string]interface{}{
"target": tgt, "target": tgt,
"match": strings.Join(matches, ","), "match": strings.Join(matches, ","),
"content": contentStr, "content": contentStr,
}) })
break break
@@ -117,7 +167,7 @@ func CheckMatches(content []string, targets []string, blacklist []string) bool {
for _, blacklistTerm := range blacklist { for _, blacklistTerm := range blacklist {
if strings.Contains(strings.ToLower(contentStr), strings.ToLower(blacklistTerm)) { if strings.Contains(strings.ToLower(contentStr), strings.ToLower(blacklistTerm)) {
Debug("Blacklisted term detected, ignoring commit", map[string]interface{}{ Debug("Blacklisted term detected, ignoring commit", map[string]interface{}{
"content": contentStr, "content": contentStr,
"blacklist_term": blacklistTerm, "blacklist_term": blacklistTerm,
}) })
return false return false
+63 -10
View File
@@ -58,15 +58,17 @@ func TestParseExistingSemver(t *testing.T) {
InitLogger(false) InitLogger(false)
tests := []struct { tests := []struct {
name string name string
tagName string tagName string
currentSemver SemVer currentSemver SemVer
want SemVer prefixes []string
want SemVer
}{ }{
{ {
name: "Standard semver", name: "Standard semver",
tagName: "1.2.3", tagName: "1.2.3",
currentSemver: SemVer{}, currentSemver: SemVer{},
prefixes: []string{},
want: SemVer{ want: SemVer{
Major: 1, Major: 1,
Minor: 2, Minor: 2,
@@ -74,9 +76,10 @@ func TestParseExistingSemver(t *testing.T) {
}, },
}, },
{ {
name: "With v prefix", name: "With v prefix configured",
tagName: "v2.3.4", tagName: "v2.3.4",
currentSemver: SemVer{}, currentSemver: SemVer{},
prefixes: []string{"v"},
want: SemVer{ want: SemVer{
Major: 2, Major: 2,
Minor: 3, Minor: 3,
@@ -84,9 +87,32 @@ func TestParseExistingSemver(t *testing.T) {
}, },
}, },
{ {
name: "With release candidate", name: "With app- prefix configured",
tagName: "3.4.5-rc.2", tagName: "app-1.2.3",
currentSemver: SemVer{}, 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{ want: SemVer{
Major: 3, Major: 3,
Minor: 4, Minor: 4,
@@ -95,6 +121,31 @@ func TestParseExistingSemver(t *testing.T) {
EnableReleaseCandidate: true, 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", name: "Invalid format",
tagName: "not-a-semver", tagName: "not-a-semver",
@@ -103,6 +154,7 @@ func TestParseExistingSemver(t *testing.T) {
Minor: 1, Minor: 1,
Patch: 1, Patch: 1,
}, },
prefixes: []string{},
want: SemVer{ want: SemVer{
Major: 1, Major: 1,
Minor: 1, Minor: 1,
@@ -117,6 +169,7 @@ func TestParseExistingSemver(t *testing.T) {
Minor: 5, Minor: 5,
Patch: 5, Patch: 5,
}, },
prefixes: []string{},
want: SemVer{ want: SemVer{
Major: 5, Major: 5,
Minor: 5, Minor: 5,
@@ -127,7 +180,7 @@ func TestParseExistingSemver(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { 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.Major, got.Major, "Major version mismatch")
assert.Equal(t, tt.want.Minor, got.Minor, "Minor version mismatch") assert.Equal(t, tt.want.Minor, got.Minor, "Minor version mismatch")
assert.Equal(t, tt.want.Patch, got.Patch, "Patch version mismatch") assert.Equal(t, tt.want.Patch, got.Patch, "Patch version mismatch")
+5
View File
@@ -5,6 +5,11 @@ force:
existing: true existing: true
strict: false strict: false
commit: 960207e4677476ad31a9f389f74eaf9f33d49613 commit: 960207e4677476ad31a9f389f74eaf9f33d49613
# tag_prefixes: (optional) prefixes to strip from existing tags
# Note: "v" prefix is always stripped automatically
# tag_prefixes:
# - "app-"
# - "service-"
wording: wording:
patch: patch:
- update - update
+4
View File
@@ -8,6 +8,10 @@ blacklist:
- "Merge pull request" - "Merge pull request"
- "feature/" - "feature/"
- "feature:" - "feature:"
tag_prefixes:
# Note: "v" prefix is stripped automatically, no need to include it here
- "app-"
- "infra-"
wording: wording:
patch: patch:
- update - update
+13 -8
View File
@@ -157,10 +157,10 @@
<div class="w-3 h-3 rounded-full bg-green-500"></div> <div class="w-3 h-3 rounded-full bg-green-500"></div>
<span class="ml-2 text-gray-400 text-sm">terminal</span> <span class="ml-2 text-gray-400 text-sm">terminal</span>
</div> </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-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> <span class="text-emerald-400">SEMVER</span> 2.3.0</code></pre>
</div> </div>
</div> </div>
@@ -260,7 +260,7 @@
<i class="fas fa-beer mr-2 text-amber-500"></i> <i class="fas fa-beer mr-2 text-amber-500"></i>
Homebrew (macOS) Homebrew (macOS)
</h3> </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>
<div class="glass p-6 rounded-xl"> <div class="glass p-6 rounded-xl">
<h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-3 flex items-center"> <h3 class="font-semibold text-gray-900 dark:text-gray-100 mb-3 flex items-center">
@@ -295,19 +295,22 @@
CLI Usage CLI Usage
</h3> </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> <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> <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> <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> <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> <span class="text-gray-400"># Respect existing tags</span>
semver-gen generate -l -e</code></pre> semver-generator generate -l -e
<span class="text-gray-400"># Self-update to latest version (no auth required)</span>
semver-generator -u</code></pre>
<div class="grid sm:grid-cols-2 gap-4 text-sm"> <div class="grid sm:grid-cols-2 gap-4 text-sm">
<div> <div>
<h4 class="font-medium text-gray-900 dark:text-gray-100 mb-2">Flags</h4> <h4 class="font-medium text-gray-900 dark:text-gray-100 mb-2">Flags</h4>
@@ -318,6 +321,8 @@ semver-gen generate -l -e</code></pre>
<li><code class="text-emerald-600 dark:text-emerald-400">-s, --strict</code> Strict matching</li> <li><code class="text-emerald-600 dark:text-emerald-400">-s, --strict</code> Strict matching</li>
<li><code class="text-emerald-600 dark:text-emerald-400">-e, --existing</code> Respect existing tags</li> <li><code class="text-emerald-600 dark:text-emerald-400">-e, --existing</code> Respect existing tags</li>
<li><code class="text-emerald-600 dark:text-emerald-400">-d, --debug</code> Enable debug mode</li> <li><code class="text-emerald-600 dark:text-emerald-400">-d, --debug</code> Enable debug mode</li>
<li><code class="text-emerald-600 dark:text-emerald-400">-u, --update</code> Self-update to latest version</li>
<li><code class="text-emerald-600 dark:text-emerald-400">-v, --version</code> Display current version</li>
</ul> </ul>
</div> </div>
</div> </div>
+2 -2
View File
@@ -60,11 +60,11 @@ if [[ ! -z "$INPUT_DEBUGMODE" ]]; then
echo "----" echo "----"
echo "FLAGS: $FLAGS" echo "FLAGS: $FLAGS"
echo "----" echo "----"
/go/src/app/semver-gen generate $FLAGS $* /go/src/app/semver-generator generate $FLAGS $*
echo "----" echo "----"
fi 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 [ $? -eq 0 ] || exit 1
CLEAN_SEMVER=$(echo $OUT_SEMVER_GEN | sed -e 's|SEMVER ||g') CLEAN_SEMVER=$(echo $OUT_SEMVER_GEN | sed -e 's|SEMVER ||g')
echo "semantic_version=$CLEAN_SEMVER" >> $GITHUB_OUTPUT echo "semantic_version=$CLEAN_SEMVER" >> $GITHUB_OUTPUT
+1 -1
View File
@@ -7,7 +7,7 @@ toolchain go1.24.6
require ( require (
github.com/go-git/go-git/v5 v5.16.4 github.com/go-git/go-git/v5 v5.16.4
github.com/lithammer/fuzzysearch v1.1.8 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/lukaszraczylo/pandati v0.0.29
github.com/spf13/cobra v1.10.2 github.com/spf13/cobra v1.10.2
github.com/spf13/viper v1.21.0 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/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 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= 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.42.4 h1:gq/bEq+JzPes8WR24HHys9VvVWREDEXOWFoVSOFXCCw=
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/go.mod h1:1FLcH7q+7cjUgQxyeVeF7ouBamGpcJZgqDF+j+cuFxI=
github.com/lukaszraczylo/pandati v0.0.29 h1:WUEWm1+hWjE5KJbIL8OctG00x2dk4XKGJSlrjhxZ55k= 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/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= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=