feat(releaser): initial release v0.4.0
ci / vet, staticcheck, test, build (push) Failing after 3m26s
release / Build and publish release (push) Successful in 4m28s

Complete GitFlow release automation tool for Conventional Commits workflows:

- Core pipeline: branch parsing, tag discovery, commit analysis, version bump
- Maven pom.xml read/write, git commit/tag, HTTPS push with token auth
- GitLab release creation via API with auto-generated release notes
- Configurable via .releaser.yml (tag_prefix, branch_pattern, commit_message, pom_path, gitlab)
- CLI flags: --dry-run, --no-push, --no-commit, --tag-only, --branch, --pom, --tag-prefix, --branch-pattern
- Dockerfile (multi-stage Alpine), .releaser.gitlab-ci.yml reusable template
- Gitea CI (vet + staticcheck + test + build) and release (5-platform cross-compilation) workflows
- Taskfile with build/test/cov/lint/fuzz/ci/docker tasks
- 96% test coverage with real in-memory git repos and fuzz tests for all parsers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-07 00:07:53 +02:00
commit f0723e706a
30 changed files with 3959 additions and 0 deletions
+91
View File
@@ -0,0 +1,91 @@
package config
import (
"errors"
"fmt"
"os"
"path/filepath"
"gopkg.in/yaml.v3"
"git.k3nny.fr/releaser/internal/branch"
)
const filename = ".releaser.yml"
type Config struct {
Git GitConfig `yaml:"git"`
Maven MavenConfig `yaml:"maven"`
GitLab GitLabConfig `yaml:"gitlab"`
}
type GitConfig struct {
TagPrefix string `yaml:"tag_prefix"`
BranchPattern string `yaml:"branch_pattern"`
CommitMessage string `yaml:"commit_message"`
AuthorName string `yaml:"author_name"`
AuthorEmail string `yaml:"author_email"`
}
type MavenConfig struct {
PomPath string `yaml:"pom_path"`
}
type GitLabConfig struct {
URL string `yaml:"url"`
Token string `yaml:"token"`
Project string `yaml:"project"`
}
func defaults() Config {
return Config{
Git: GitConfig{
TagPrefix: "v",
BranchPattern: branch.DefaultBranchPattern,
CommitMessage: "chore(release): {version} [skip ci]",
},
Maven: MavenConfig{
PomPath: "pom.xml",
},
}
}
// Load reads .releaser.yml from dir and merges it over the defaults.
// Missing file is not an error — defaults are returned as-is.
func Load(dir string) (Config, error) {
cfg := defaults()
data, err := os.ReadFile(filepath.Join(dir, filename))
if errors.Is(err, os.ErrNotExist) {
return cfg, nil
}
if err != nil {
return cfg, fmt.Errorf("read %s: %w", filename, err)
}
if err := yaml.Unmarshal(data, &cfg); err != nil {
return cfg, fmt.Errorf("parse %s: %w", filename, err)
}
return cfg, nil
}
// ApplyEnv fills empty GitLab fields from the standard GitLab CI environment variables.
// Values already set in the config file are never overwritten.
func (c *Config) ApplyEnv() {
if c.GitLab.Token == "" {
c.GitLab.Token = os.Getenv("GITLAB_TOKEN")
}
if c.GitLab.URL == "" {
// CI_SERVER_URL is the cleanest source ("https://gitlab.example.com")
c.GitLab.URL = os.Getenv("CI_SERVER_URL")
}
if c.GitLab.Project == "" {
// Prefer numeric ID; fall back to namespace/project path
if id := os.Getenv("CI_PROJECT_ID"); id != "" {
c.GitLab.Project = id
} else {
c.GitLab.Project = os.Getenv("CI_PROJECT_PATH")
}
}
}