Fix issues

This commit is contained in:
2025-12-24 11:39:34 +03:00
parent dc05c68ee2
commit 56848c73fd
3 changed files with 101 additions and 31 deletions

View File

@@ -4,6 +4,7 @@ SHELL := /bin/bash
REGISTRY := registry.halfakop.ru REGISTRY := registry.halfakop.ru
REPOSITORY := $(NAMESPACE)/$(PACKAGE) REPOSITORY := $(NAMESPACE)/$(PACKAGE)
PLATFORM ?= --platform=linux/amd64 PLATFORM ?= --platform=linux/amd64
GOCACHE ?= $(CURDIR)/.cache/go-build
SOURCE_VERSION ?= $(shell cat VERSION) SOURCE_VERSION ?= $(shell cat VERSION)
SOURCE_COMMIT ?= $(shell git rev-parse --short=8 HEAD) SOURCE_COMMIT ?= $(shell git rev-parse --short=8 HEAD)
@@ -31,27 +32,27 @@ help:
download: download:
@echo "Download dependencies" @echo "Download dependencies"
@go mod download @GOCACHE=$(GOCACHE) go mod download
fix: fix:
@echo "Fix code" @echo "Fix code"
@go fix ./... @GOCACHE=$(GOCACHE) go fix ./...
app: download fix app: download fix
@echo "Build application" @echo "Build application"
@go build -ldflags "$(LDFLAGS)" -o ./${EXEC} ./src @GOCACHE=$(GOCACHE) go build -ldflags "$(LDFLAGS)" -o ./${EXEC} ./src
tests: app tests: app
@echo "Run tests" @echo "Run tests"
@go test ./... @GOCACHE=$(GOCACHE) go test ./...
test: test:
@echo "Run unit tests" @echo "Run unit tests"
@go test -count=1 ./... @GOCACHE=$(GOCACHE) go test -count=1 ./...
test-integration: test-integration:
@echo "Run integration tests (requires Docker for Postgres)" @echo "Run integration tests (requires Docker for Postgres)"
@go test -tags=integration -count=1 ./... @GOCACHE=$(GOCACHE) go test -tags=integration -count=1 ./...
run: run:
@echo "Run application" @echo "Run application"
@@ -59,7 +60,7 @@ run:
clean: clean:
@echo "Clean build environment" @echo "Clean build environment"
@rm -rf ./${EXEC}% @rm -rf ./${EXEC}% $(GOCACHE)
release: clean build login push release: clean build login push

View File

@@ -1,6 +1,7 @@
package main package main
import ( import (
"fmt"
"log" "log"
"os" "os"
"strings" "strings"
@@ -8,6 +9,7 @@ import (
git "github.com/go-git/go-git/v5" git "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config" "github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object" "github.com/go-git/go-git/v5/plumbing/object"
) )
@@ -27,6 +29,22 @@ func normalizePath(p string) string {
return p return p
} }
func pushRefSpec(headRef *plumbing.Reference) (config.RefSpec, error) {
if headRef == nil || !headRef.Name().IsBranch() {
return "", fmt.Errorf("cannot determine branch to push from HEAD")
}
branch := headRef.Name().String()
return config.RefSpec(branch + ":" + branch), nil
}
func signatureFromEnv() *object.Signature {
return &object.Signature{
Name: getenvDefault("GIT_USERNAME", "bumpversion"),
Email: getenvDefault("GIT_EMAIL", "bumpversion@deploy"),
When: time.Now().UTC(),
}
}
// gitCommit выполняет коммит с внесёнными изменениями // gitCommit выполняет коммит с внесёнными изменениями
func gitCommit(bc *BumpConfig, newVersion string, configPath string) { func gitCommit(bc *BumpConfig, newVersion string, configPath string) {
// Открываем локальный репозиторий (предполагается, что он существует в папке ".") // Открываем локальный репозиторий (предполагается, что он существует в папке ".")
@@ -90,11 +108,7 @@ func gitCommit(bc *BumpConfig, newVersion string, configPath string) {
"{new_version}", newVersion, "{new_version}", newVersion,
).Replace(bc.Message) ).Replace(bc.Message)
author := &object.Signature{ author := signatureFromEnv()
Name: getenvDefault("GIT_USERNAME", "bumpversion"),
Email: getenvDefault("GIT_EMAIL", "bumpversion@deploy"),
When: time.Now().UTC(),
}
hash, err := worktree.Commit(commitMsg, &git.CommitOptions{ hash, err := worktree.Commit(commitMsg, &git.CommitOptions{
Author: author, Author: author,
@@ -132,11 +146,7 @@ func gitTag(bc *BumpConfig, newVersion string) {
commitMsg = strings.ReplaceAll(commitMsg, "{new_version}", newVersion) commitMsg = strings.ReplaceAll(commitMsg, "{new_version}", newVersion)
tagName := strings.ReplaceAll(bc.TagName, "{new_version}", newVersion) tagName := strings.ReplaceAll(bc.TagName, "{new_version}", newVersion)
_, err = repo.CreateTag(tagName, headRef.Hash(), &git.CreateTagOptions{ _, err = repo.CreateTag(tagName, headRef.Hash(), &git.CreateTagOptions{
Tagger: &object.Signature{ Tagger: signatureFromEnv(),
Name: os.Getenv("GIT_USERNAME"),
Email: os.Getenv("GIT_EMAIL"),
When: time.Now(),
},
Message: commitMsg, Message: commitMsg,
}) })
if err != nil { if err != nil {
@@ -154,12 +164,22 @@ func gitPush(bc *BumpConfig, newVersion string) {
tagName := strings.ReplaceAll(bc.TagName, "{new_version}", newVersion) tagName := strings.ReplaceAll(bc.TagName, "{new_version}", newVersion)
headRef, err := repo.Head()
if err != nil {
log.Fatalf("HEAD open error: %v", err)
}
branchSpec, err := pushRefSpec(headRef)
if err != nil {
log.Fatalf("Push branch detection error: %v", err)
}
// (Опционально) Выполняем push на удаленный репозиторий // (Опционально) Выполняем push на удаленный репозиторий
tagSpec := config.RefSpec("refs/tags/" + tagName + ":refs/tags/" + tagName) tagSpec := config.RefSpec("refs/tags/" + tagName + ":refs/tags/" + tagName)
err = repo.Push(&git.PushOptions{ err = repo.Push(&git.PushOptions{
RemoteName: "origin", RemoteName: "origin",
RefSpecs: []config.RefSpec{ RefSpecs: []config.RefSpec{
"refs/heads/master:refs/heads/master", branchSpec,
tagSpec, tagSpec,
}, },
}) })

View File

@@ -6,6 +6,9 @@ import (
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
) )
func TestGetBumpConfig(t *testing.T) { func TestGetBumpConfig(t *testing.T) {
@@ -129,6 +132,57 @@ func TestBumpVersionInvalidCurrent(t *testing.T) {
} }
} }
func TestBumpVersionMissingGroups(t *testing.T) {
bc := &BumpConfig{
CurrentVersion: "1.2.3",
Parse: `^(?P<major>\d+)\.(?P<minor>\d+)$`,
Serialize: "{major}.{minor}.{patch}",
}
if _, err := bumpVersion(bc, "patch"); err == nil {
t.Fatalf("expected error when parse pattern misses patch group")
}
}
func TestGetBumpConfigMissingFile(t *testing.T) {
tmpDir := t.TempDir()
if _, err := getBumpConfig(filepath.Join(tmpDir, "missing.cfg")); err == nil {
t.Fatalf("expected error for missing config file")
}
}
func TestSignatureFromEnvDefaults(t *testing.T) {
prevUser := os.Getenv("GIT_USERNAME")
prevEmail := os.Getenv("GIT_EMAIL")
t.Cleanup(func() {
_ = os.Setenv("GIT_USERNAME", prevUser)
_ = os.Setenv("GIT_EMAIL", prevEmail)
})
_ = os.Unsetenv("GIT_USERNAME")
_ = os.Unsetenv("GIT_EMAIL")
sig := signatureFromEnv()
if sig.Name != "bumpversion" || sig.Email != "bumpversion@deploy" {
t.Fatalf("signatureFromEnv defaults = %s %s, want bumpversion bumpversion@deploy", sig.Name, sig.Email)
}
}
func TestPushRefSpec(t *testing.T) {
head := plumbing.NewHashReference(plumbing.NewBranchReferenceName("main"), plumbing.ZeroHash)
spec, err := pushRefSpec(head)
if err != nil {
t.Fatalf("pushRefSpec returned error: %v", err)
}
want := config.RefSpec("refs/heads/main:refs/heads/main")
if spec != want {
t.Fatalf("pushRefSpec = %q, want %q", spec, want)
}
_, err = pushRefSpec(plumbing.NewHashReference(plumbing.HEAD, plumbing.ZeroHash))
if err == nil {
t.Fatalf("expected error for non-branch HEAD")
}
}
func TestUpdateFiles(t *testing.T) { func TestUpdateFiles(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
oldV := "1.2.3" oldV := "1.2.3"
@@ -168,7 +222,7 @@ func TestResolveFlag(t *testing.T) {
negative *bool negative *bool
defaultValue bool defaultValue bool
want bool want bool
wantPanic bool wantErr bool
}{ }{
{ {
name: "positive wins", name: "positive wins",
@@ -192,26 +246,21 @@ func TestResolveFlag(t *testing.T) {
want: true, want: true,
}, },
{ {
name: "panic on conflict", name: "error on conflict",
positive: boolPtr(true), positive: boolPtr(true),
negative: boolPtr(true), negative: boolPtr(true),
defaultValue: false, defaultValue: false,
wantPanic: true, wantErr: true,
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
defer func() { got, err := resolveFlag(tt.positive, tt.negative, tt.defaultValue)
if r := recover(); tt.wantPanic && r == nil { if (err != nil) != tt.wantErr {
t.Fatalf("expected panic but function returned") t.Fatalf("resolveFlag error = %v, wantErr %v", err, tt.wantErr)
} else if !tt.wantPanic && r != nil {
t.Fatalf("unexpected panic: %v", r)
} }
}() if tt.wantErr {
got := resolveFlag(tt.positive, tt.negative, tt.defaultValue)
if tt.wantPanic {
return return
} }
if got != tt.want { if got != tt.want {