Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95b7758c42 | ||
|
|
e88bce176e | ||
|
|
54a7b1e6d8 | ||
|
|
d6cf715a4a | ||
|
|
d4ab1e690c | ||
|
|
5637163ed0 | ||
|
|
f7aeaaca5c | ||
|
|
1357993978 | ||
|
|
0b27479ce3 | ||
|
|
1b8c6d789a | ||
|
|
f575d0ea75 | ||
|
|
e29140f884 | ||
|
|
9b61354e6c |
No files matched your search
+14
-16
@@ -8,14 +8,14 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
runs-on: [ubuntu-latest, amd64]
|
runs-on: [ubuntu-latest, amd64]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version: '1.26'
|
go-version: '1.27'
|
||||||
|
|
||||||
- name: Install just
|
- name: Install just
|
||||||
run: |
|
run: |
|
||||||
@@ -26,6 +26,8 @@ jobs:
|
|||||||
/root/bin/just vet test
|
/root/bin/just vet test
|
||||||
/root/bin/just build-linux-amd64
|
/root/bin/just build-linux-amd64
|
||||||
./bin/gitbump_linux_amd64 --version
|
./bin/gitbump_linux_amd64 --version
|
||||||
|
- name: Lint
|
||||||
|
uses: https://lab.zlymeda.dynu.net/actions/go-lint@master
|
||||||
|
|
||||||
build:
|
build:
|
||||||
runs-on: [ubuntu-latest, amd64]
|
runs-on: [ubuntu-latest, amd64]
|
||||||
@@ -34,30 +36,26 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v7
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
persist-credentials: true
|
persist-credentials: true
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v7
|
||||||
with:
|
with:
|
||||||
go-version: '1.26'
|
go-version: '1.27'
|
||||||
|
|
||||||
- name: Bump & Tag
|
- name: Bump & Tag
|
||||||
run: go run main.go
|
run: go run main.go
|
||||||
|
|
||||||
notify-failure:
|
notify:
|
||||||
runs-on: [ubuntu-latest, amd64]
|
runs-on: [ubuntu-latest, amd64]
|
||||||
needs: [test, build]
|
needs: [test, build]
|
||||||
if: always() && (contains(needs.*.result, 'failure') || contains(needs.*.result, 'skipped'))
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
- name: ntfy-failed-notifications
|
- name: Notify
|
||||||
uses: niniyas/ntfy-action@master
|
uses: https://lab.zlymeda.dynu.net/actions/ci-notify@master
|
||||||
with:
|
with:
|
||||||
url: ${{ secrets.NTFY_URL }}
|
needs: ${{ toJSON(needs) }}
|
||||||
topic: ${{ secrets.NTFY_TOPIC }}
|
ntfy_token: ${{ secrets.NTFY_TOKEN }}
|
||||||
priority: 5
|
|
||||||
tags: flying_saucer,warning,🚨,🛠️,action,failed
|
|
||||||
details: Workflow has failed!
|
|
||||||
actions: 'default'
|
|
||||||
@@ -19,7 +19,16 @@ jobs:
|
|||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.23'
|
go-version: '1.23'
|
||||||
- uses: https://gitea.com/your-user/gitbump@master
|
- uses: https://lab.zlymeda.dynu.net/actions/gitbump@master
|
||||||
|
```
|
||||||
|
|
||||||
|
For a fixed base version with an incrementing fork suffix:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: https://lab.zlymeda.dynu.net/actions/gitbump@master
|
||||||
|
with:
|
||||||
|
bump: suffix
|
||||||
|
suffix: meda
|
||||||
```
|
```
|
||||||
|
|
||||||
### Inputs
|
### Inputs
|
||||||
@@ -27,7 +36,8 @@ jobs:
|
|||||||
| Input | Description | Default |
|
| Input | Description | Default |
|
||||||
|-------|-------------|---------|
|
|-------|-------------|---------|
|
||||||
| `dry-run` | Show what would be done without making changes | `false` |
|
| `dry-run` | Show what would be done without making changes | `false` |
|
||||||
| `bump` | Force specific bump type (`major`, `minor`, or `patch`) | |
|
| `bump` | Force specific bump type (`major`, `minor`, `patch`, or `suffix`) | |
|
||||||
|
| `suffix` | Suffix label used when creating the first suffix | |
|
||||||
| `prefix` | Version prefix | `v` |
|
| `prefix` | Version prefix | `v` |
|
||||||
| `no-push` | Create tag but don't push to remote | `false` |
|
| `no-push` | Create tag but don't push to remote | `false` |
|
||||||
| `major-patterns` | Comma-separated list for major version bumps | `bump major,breaking change` |
|
| `major-patterns` | Comma-separated list for major version bumps | `bump major,breaking change` |
|
||||||
|
|||||||
+4
-1
@@ -6,7 +6,10 @@ inputs:
|
|||||||
required: false
|
required: false
|
||||||
default: 'false'
|
default: 'false'
|
||||||
bump:
|
bump:
|
||||||
description: 'Force specific bump type (major, minor, or patch)'
|
description: 'Force specific bump type (major, minor, patch, or suffix)'
|
||||||
|
required: false
|
||||||
|
suffix:
|
||||||
|
description: 'Suffix label used when creating the first suffix'
|
||||||
required: false
|
required: false
|
||||||
prefix:
|
prefix:
|
||||||
description: 'Version prefix (default "v")'
|
description: 'Version prefix (default "v")'
|
||||||
|
|||||||
@@ -22,10 +22,20 @@ type Config struct {
|
|||||||
dryRun bool
|
dryRun bool
|
||||||
forcedBump string
|
forcedBump string
|
||||||
versionPrefix string
|
versionPrefix string
|
||||||
|
suffixLabel string
|
||||||
patterns Patterns
|
patterns Patterns
|
||||||
noPush bool
|
noPush bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type taggedVersion struct {
|
||||||
|
major int
|
||||||
|
minor int
|
||||||
|
patch int
|
||||||
|
suffixLabel string
|
||||||
|
suffixRevision int
|
||||||
|
hasSuffix bool
|
||||||
|
}
|
||||||
|
|
||||||
type Patterns struct {
|
type Patterns struct {
|
||||||
major []string
|
major []string
|
||||||
minor []string
|
minor []string
|
||||||
@@ -74,11 +84,11 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
major, minor, patch, err := parseVersion(tag, config.versionPrefix)
|
version, err := parseTaggedVersion(tag, config.versionPrefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error parsing version: %v", err)
|
log.Fatalf("Error parsing version: %v", err)
|
||||||
}
|
}
|
||||||
fmt.Println("version:", major, minor, patch)
|
fmt.Println("version:", version.major, version.minor, version.patch)
|
||||||
|
|
||||||
bumpType := getBumpType(commits, config)
|
bumpType := getBumpType(commits, config)
|
||||||
fmt.Println("bump type:", bumpType)
|
fmt.Println("bump type:", bumpType)
|
||||||
@@ -87,7 +97,18 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
newVersion := calculateNewVersion(major, minor, patch, bumpType, config.versionPrefix)
|
var newVersion string
|
||||||
|
if bumpType == "suffix" {
|
||||||
|
newVersion, err = calculateSuffixVersion(version, config.suffixLabel, config.versionPrefix)
|
||||||
|
} else {
|
||||||
|
if version.hasSuffix {
|
||||||
|
log.Fatalf("Cannot apply %s bump to suffixed version %s", bumpType, tag)
|
||||||
|
}
|
||||||
|
newVersion = calculateNewVersion(version.major, version.minor, version.patch, bumpType, config.versionPrefix)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error calculating version: %v", err)
|
||||||
|
}
|
||||||
fmt.Println("new version:", newVersion)
|
fmt.Println("new version:", newVersion)
|
||||||
|
|
||||||
if config.dryRun {
|
if config.dryRun {
|
||||||
@@ -109,8 +130,9 @@ func parseFlags() Config {
|
|||||||
|
|
||||||
flag.BoolVar(&config.printVersion, "version", false, "Just print version")
|
flag.BoolVar(&config.printVersion, "version", false, "Just print version")
|
||||||
flag.BoolVar(&config.dryRun, "dry-run", getEnvBool("DRY_RUN", false), "Show what would be done without making changes")
|
flag.BoolVar(&config.dryRun, "dry-run", getEnvBool("DRY_RUN", false), "Show what would be done without making changes")
|
||||||
flag.StringVar(&config.forcedBump, "bump", os.Getenv("BUMP"), "Force specific bump type (major, minor, or patch)")
|
flag.StringVar(&config.forcedBump, "bump", getEnv("BUMP", ""), "Force specific bump type (major, minor, patch, or suffix)")
|
||||||
flag.StringVar(&config.versionPrefix, "prefix", getEnv("PREFIX", "v"), "Version prefix (default 'v')")
|
flag.StringVar(&config.versionPrefix, "prefix", getEnv("PREFIX", "v"), "Version prefix (default 'v')")
|
||||||
|
flag.StringVar(&config.suffixLabel, "suffix", getEnv("SUFFIX", ""), "Suffix label used when creating the first suffix")
|
||||||
flag.BoolVar(&config.noPush, "no-push", getEnvBool("NO_PUSH", false), "Create tag but don't push to remote")
|
flag.BoolVar(&config.noPush, "no-push", getEnvBool("NO_PUSH", false), "Create tag but don't push to remote")
|
||||||
|
|
||||||
majorPatterns := flag.String("major-patterns", getEnv("MAJOR_PATTERNS", "bump major,breaking change"), "Comma-separated list will be joined with '|' for major version bumps")
|
majorPatterns := flag.String("major-patterns", getEnv("MAJOR_PATTERNS", "bump major,breaking change"), "Comma-separated list will be joined with '|' for major version bumps")
|
||||||
@@ -127,20 +149,20 @@ func parseFlags() Config {
|
|||||||
chore: strings.Split(*chorePatterns, ","),
|
chore: strings.Split(*chorePatterns, ","),
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.forcedBump != "" && !contains([]string{"major", "minor", "patch"}, config.forcedBump) {
|
if config.forcedBump != "" && !contains([]string{"major", "minor", "patch", "suffix"}, config.forcedBump) {
|
||||||
log.Fatalf("Invalid bump type: %s. Must be one of: major, minor, patch", config.forcedBump)
|
log.Fatalf("Invalid bump type: %s. Must be one of: major, minor, patch, suffix", config.forcedBump)
|
||||||
}
|
}
|
||||||
|
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEnv(key, fallback string) string {
|
func getEnv(key, fallback string) string {
|
||||||
if value, ok := os.LookupEnv(key); ok {
|
if value, ok := os.LookupEnv(key); ok && value != "" {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
// Check for Gitea/GitHub Actions input prefix
|
// Check for Gitea/GitHub Actions input prefix
|
||||||
inputKey := "INPUT_" + strings.ReplaceAll(strings.ToUpper(key), "-", "_")
|
inputKey := "INPUT_" + strings.ReplaceAll(strings.ToUpper(key), "-", "_")
|
||||||
if value, ok := os.LookupEnv(inputKey); ok {
|
if value, ok := os.LookupEnv(inputKey); ok && value != "" {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
return fallback
|
return fallback
|
||||||
@@ -214,29 +236,60 @@ func getCommitsSinceTag(tag string) ([]string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func parseVersion(tag, prefix string) (int, int, int, error) {
|
func parseVersion(tag, prefix string) (int, int, int, error) {
|
||||||
tag = strings.TrimPrefix(tag, prefix)
|
version, err := parseTaggedVersion(tag, prefix)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, 0, err
|
||||||
|
}
|
||||||
|
if version.hasSuffix {
|
||||||
|
return 0, 0, 0, fmt.Errorf("version has a suffix: %s", tag)
|
||||||
|
}
|
||||||
|
return version.major, version.minor, version.patch, nil
|
||||||
|
}
|
||||||
|
|
||||||
parts := strings.Split(tag, ".")
|
func parseTaggedVersion(tag, prefix string) (taggedVersion, error) {
|
||||||
|
rawVersion := strings.TrimPrefix(tag, prefix)
|
||||||
|
baseVersion := rawVersion
|
||||||
|
suffix := ""
|
||||||
|
if separator := strings.IndexByte(rawVersion, '-'); separator >= 0 {
|
||||||
|
baseVersion = rawVersion[:separator]
|
||||||
|
suffix = rawVersion[separator+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(baseVersion, ".")
|
||||||
if len(parts) != 3 {
|
if len(parts) != 3 {
|
||||||
return 0, 0, 0, fmt.Errorf("invalid version format: %s", tag)
|
return taggedVersion{}, fmt.Errorf("invalid version format: %s", rawVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
major, err := strconv.Atoi(parts[0])
|
major, err := strconv.Atoi(parts[0])
|
||||||
if err != nil {
|
if err != nil || major < 0 {
|
||||||
return 0, 0, 0, err
|
return taggedVersion{}, fmt.Errorf("invalid major version: %s", parts[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
minor, err := strconv.Atoi(parts[1])
|
minor, err := strconv.Atoi(parts[1])
|
||||||
if err != nil {
|
if err != nil || minor < 0 {
|
||||||
return 0, 0, 0, err
|
return taggedVersion{}, fmt.Errorf("invalid minor version: %s", parts[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
patch, err := strconv.Atoi(parts[2])
|
patch, err := strconv.Atoi(parts[2])
|
||||||
if err != nil {
|
if err != nil || patch < 0 {
|
||||||
return 0, 0, 0, err
|
return taggedVersion{}, fmt.Errorf("invalid patch version: %s", parts[2])
|
||||||
}
|
}
|
||||||
|
|
||||||
return major, minor, patch, nil
|
version := taggedVersion{major: major, minor: minor, patch: patch}
|
||||||
|
if suffix == "" {
|
||||||
|
return version, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
suffixParts := strings.Split(suffix, ".")
|
||||||
|
if len(suffixParts) != 2 || suffixParts[0] == "" {
|
||||||
|
return taggedVersion{}, fmt.Errorf("invalid version suffix: %s", suffix)
|
||||||
|
}
|
||||||
|
revision, err := strconv.Atoi(suffixParts[1])
|
||||||
|
if err != nil || revision < 1 {
|
||||||
|
return taggedVersion{}, fmt.Errorf("invalid version suffix revision: %s", suffix)
|
||||||
|
}
|
||||||
|
version.suffixLabel = suffixParts[0]
|
||||||
|
version.suffixRevision = revision
|
||||||
|
version.hasSuffix = true
|
||||||
|
return version, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getBumpType(commits []string, config Config) string {
|
func getBumpType(commits []string, config Config) string {
|
||||||
@@ -289,6 +342,32 @@ func calculateNewVersion(major, minor, patch int, bumpType, prefix string) strin
|
|||||||
return fmt.Sprintf("%s%d.%d.%d", prefix, major, minor, patch)
|
return fmt.Sprintf("%s%d.%d.%d", prefix, major, minor, patch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func calculateSuffixVersion(version taggedVersion, suffixLabel, prefix string) (string, error) {
|
||||||
|
if version.hasSuffix {
|
||||||
|
if suffixLabel != "" && suffixLabel != version.suffixLabel {
|
||||||
|
return "", fmt.Errorf("suffix label %q does not match %q", suffixLabel, version.suffixLabel)
|
||||||
|
}
|
||||||
|
version.suffixRevision++
|
||||||
|
return formatTaggedVersion(version, prefix), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if suffixLabel == "" {
|
||||||
|
return "", fmt.Errorf("suffix label is required for a base version")
|
||||||
|
}
|
||||||
|
version.suffixLabel = suffixLabel
|
||||||
|
version.suffixRevision = 1
|
||||||
|
version.hasSuffix = true
|
||||||
|
return formatTaggedVersion(version, prefix), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatTaggedVersion(version taggedVersion, prefix string) string {
|
||||||
|
base := fmt.Sprintf("%s%d.%d.%d", prefix, version.major, version.minor, version.patch)
|
||||||
|
if version.hasSuffix {
|
||||||
|
return fmt.Sprintf("%s-%s.%d", base, version.suffixLabel, version.suffixRevision)
|
||||||
|
}
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
|
||||||
func createAndPushTag(version string, noPush bool) error {
|
func createAndPushTag(version string, noPush bool) error {
|
||||||
if out, err := git("tag " + version); err != nil {
|
if out, err := git("tag " + version); err != nil {
|
||||||
return fmt.Errorf("error creating tag: %v; stdout: %v", err, out)
|
return fmt.Errorf("error creating tag: %v; stdout: %v", err, out)
|
||||||
|
|||||||
@@ -12,3 +12,89 @@ func TestParseVersion(t *testing.T) {
|
|||||||
t.Fatal("version does not match")
|
t.Fatal("version does not match")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseTaggedVersion(t *testing.T) {
|
||||||
|
version, err := parseTaggedVersion("v0.10.50-meda.2", "v")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if version.major != 0 || version.minor != 10 || version.patch != 50 {
|
||||||
|
t.Fatalf("base version does not match: %+v", version)
|
||||||
|
}
|
||||||
|
if !version.hasSuffix || version.suffixLabel != "meda" || version.suffixRevision != 2 {
|
||||||
|
t.Fatalf("suffix does not match: %+v", version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseVersionRejectsSuffix(t *testing.T) {
|
||||||
|
if _, _, _, err := parseVersion("v0.10.50-meda.2", "v"); err == nil {
|
||||||
|
t.Fatal("expected suffixed version to be rejected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetEnvActionInput(t *testing.T) {
|
||||||
|
t.Setenv("BUMP", "")
|
||||||
|
t.Setenv("SUFFIX", "")
|
||||||
|
t.Setenv("INPUT_BUMP", "suffix")
|
||||||
|
t.Setenv("INPUT_SUFFIX", "meda")
|
||||||
|
|
||||||
|
if value := getEnv("BUMP", ""); value != "suffix" {
|
||||||
|
t.Fatalf("expected suffix bump, got %s", value)
|
||||||
|
}
|
||||||
|
if value := getEnv("SUFFIX", ""); value != "meda" {
|
||||||
|
t.Fatalf("expected meda suffix, got %s", value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCalculateSuffixVersion(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
version taggedVersion
|
||||||
|
suffix string
|
||||||
|
expected string
|
||||||
|
expectsErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "first suffix",
|
||||||
|
version: taggedVersion{major: 0, minor: 10, patch: 50},
|
||||||
|
suffix: "meda",
|
||||||
|
expected: "v0.10.50-meda.1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "increment suffix",
|
||||||
|
version: taggedVersion{major: 0, minor: 10, patch: 50, suffixLabel: "meda", suffixRevision: 1, hasSuffix: true},
|
||||||
|
suffix: "meda",
|
||||||
|
expected: "v0.10.50-meda.2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "missing label",
|
||||||
|
version: taggedVersion{major: 0, minor: 10, patch: 50},
|
||||||
|
expectsErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "label mismatch",
|
||||||
|
version: taggedVersion{major: 0, minor: 10, patch: 50, suffixLabel: "meda", suffixRevision: 1, hasSuffix: true},
|
||||||
|
suffix: "other",
|
||||||
|
expectsErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
actual, err := calculateSuffixVersion(test.version, test.suffix, "v")
|
||||||
|
if test.expectsErr {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected an error")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if actual != test.expected {
|
||||||
|
t.Fatalf("expected %s, got %s", test.expected, actual)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
|
||||||
|
}
|
||||||
Reference in new issue
Block a user