feat(pyproject): release v1.7.0 — Python pyproject.toml version bump
Add internal/pyproject package with ReadVersion and WriteVersion for pyproject.toml files. Reads [project].version (PEP 621) first, then falls back to [tool.poetry].version. Section boundaries are detected via TOML's rule that headers always start at the beginning of a line (\n[ pattern), so inline arrays with [ characters don't interfere. Config follows the established multi-value pattern: - python.pyproject_toml: single path (opt-in, no default) - python.pyproject_tomls: list for monorepos (overrides single) - --pyproject flag overrides pyproject_toml and clears pyproject_tomls 100% per-package statement coverage maintained across all 14 packages; FuzzReadVersion and FuzzWriteVersion added per fuzzing guidelines. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1101,6 +1101,7 @@ func TestPrintVerboseConfigBumpRulesAndNode(t *testing.T) {
|
||||
},
|
||||
Node: config.NodeConfig{PackageJSON: "package.json"},
|
||||
Gradle: config.GradleConfig{BuildFile: "build.gradle"},
|
||||
Python: config.PythonConfig{PyprojectTOML: "pyproject.toml"},
|
||||
}
|
||||
src := config.Sources{}
|
||||
|
||||
@@ -1122,6 +1123,9 @@ func TestPrintVerboseConfigBumpRulesAndNode(t *testing.T) {
|
||||
if !strings.Contains(output, "build.gradle") {
|
||||
t.Error("expected 'build.gradle' in output for gradle.paths")
|
||||
}
|
||||
if !strings.Contains(output, "pyproject.toml") {
|
||||
t.Error("expected 'pyproject.toml' in output for python.paths")
|
||||
}
|
||||
}
|
||||
|
||||
// ── node package.json handling ────────────────────────────────────────────────
|
||||
@@ -1321,3 +1325,143 @@ func TestRunGradleWriteVersionFails(t *testing.T) {
|
||||
t.Fatal("expected error when build.gradle is read-only")
|
||||
}
|
||||
}
|
||||
|
||||
// ── pyproject.toml handling ───────────────────────────────────────────────────
|
||||
|
||||
func writePyprojectFile(t *testing.T, dir, ver string) {
|
||||
t.Helper()
|
||||
content := fmt.Sprintf("[project]\nname = \"my-app\"\nversion = \"%s\"\n", ver)
|
||||
if err := os.WriteFile(filepath.Join(dir, "pyproject.toml"), []byte(content), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunPyprojectVersionBump(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
repo, err := gogit.PlainInit(dir, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
writePyprojectFile(t, dir, "0.0.0")
|
||||
commitAll(t, repo, dir, "chore: init")
|
||||
|
||||
os.WriteFile(filepath.Join(dir, ".releaser.yml"), []byte("python:\n pyproject_toml: \"pyproject.toml\"\n"), 0644)
|
||||
|
||||
addFile(t, dir, "x.go", "// fix")
|
||||
w, _ := repo.Worktree()
|
||||
w.Add("x.go")
|
||||
w.Commit("fix: something", &gogit.CommitOptions{Author: testSig()})
|
||||
|
||||
if err := execCmd(t, "--no-push", "--branch", "release/1.2", "--repo", dir); err != nil {
|
||||
t.Fatalf("pyproject version bump: unexpected error: %v", err)
|
||||
}
|
||||
|
||||
data, _ := os.ReadFile(filepath.Join(dir, "pyproject.toml"))
|
||||
if !strings.Contains(string(data), `version = "1.2.0"`) {
|
||||
t.Errorf("expected version 1.2.0 in pyproject.toml, got: %s", data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunPyprojectPoetryVersionBump(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
repo, err := gogit.PlainInit(dir, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
content := "[tool.poetry]\nname = \"my-app\"\nversion = \"0.0.0\"\n"
|
||||
os.WriteFile(filepath.Join(dir, "pyproject.toml"), []byte(content), 0644)
|
||||
commitAll(t, repo, dir, "chore: init")
|
||||
|
||||
os.WriteFile(filepath.Join(dir, ".releaser.yml"), []byte("python:\n pyproject_toml: \"pyproject.toml\"\n"), 0644)
|
||||
|
||||
addFile(t, dir, "x.go", "// fix")
|
||||
w, _ := repo.Worktree()
|
||||
w.Add("x.go")
|
||||
w.Commit("fix: something", &gogit.CommitOptions{Author: testSig()})
|
||||
|
||||
if err := execCmd(t, "--no-push", "--branch", "release/1.2", "--repo", dir); err != nil {
|
||||
t.Fatalf("poetry version bump: unexpected error: %v", err)
|
||||
}
|
||||
|
||||
data, _ := os.ReadFile(filepath.Join(dir, "pyproject.toml"))
|
||||
if !strings.Contains(string(data), `version = "1.2.0"`) {
|
||||
t.Errorf("expected version 1.2.0 in pyproject.toml, got: %s", data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunPyprojectOverrideFlag(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
repo, err := gogit.PlainInit(dir, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Join(dir, "sub"), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
content := "[project]\nversion = \"0.0.0\"\n"
|
||||
os.WriteFile(filepath.Join(dir, "sub", "pyproject.toml"), []byte(content), 0644)
|
||||
commitAll(t, repo, dir, "chore: init")
|
||||
|
||||
addFile(t, dir, "x.go", "// fix")
|
||||
w, _ := repo.Worktree()
|
||||
w.Add("x.go")
|
||||
w.Commit("fix: something", &gogit.CommitOptions{Author: testSig()})
|
||||
|
||||
if err := execCmd(t, "--no-push", "--branch", "release/1.2", "--repo", dir, "--pyproject", "sub/pyproject.toml"); err != nil {
|
||||
t.Fatalf("--pyproject flag: unexpected error: %v", err)
|
||||
}
|
||||
|
||||
data, _ := os.ReadFile(filepath.Join(dir, "sub", "pyproject.toml"))
|
||||
if !strings.Contains(string(data), `version = "1.2.0"`) {
|
||||
t.Errorf("expected version 1.2.0, got: %s", data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunPyprojectReadVersionFails(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
repo, err := gogit.PlainInit(dir, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// pyproject.toml with no version field
|
||||
os.WriteFile(filepath.Join(dir, "pyproject.toml"), []byte("[build-system]\nrequires=[]\n"), 0644)
|
||||
commitAll(t, repo, dir, "chore: init")
|
||||
|
||||
os.WriteFile(filepath.Join(dir, ".releaser.yml"), []byte("python:\n pyproject_toml: \"pyproject.toml\"\n"), 0644)
|
||||
|
||||
addFile(t, dir, "x.go", "// fix")
|
||||
w, _ := repo.Worktree()
|
||||
w.Add("x.go")
|
||||
w.Commit("fix: something", &gogit.CommitOptions{Author: testSig()})
|
||||
|
||||
if err := execCmd(t, "--branch", "release/1.2", "--repo", dir); err == nil {
|
||||
t.Fatal("expected error when pyproject.toml has no version")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunPyprojectWriteVersionFails(t *testing.T) {
|
||||
if os.Getuid() == 0 {
|
||||
t.Skip("skipping: chmod restrictions do not apply when running as root")
|
||||
}
|
||||
dir := t.TempDir()
|
||||
repo, err := gogit.PlainInit(dir, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
writePyprojectFile(t, dir, "0.0.0")
|
||||
commitAll(t, repo, dir, "chore: init")
|
||||
|
||||
os.WriteFile(filepath.Join(dir, ".releaser.yml"), []byte("python:\n pyproject_toml: \"pyproject.toml\"\n"), 0644)
|
||||
|
||||
addFile(t, dir, "x.go", "// fix")
|
||||
w, _ := repo.Worktree()
|
||||
w.Add("x.go")
|
||||
w.Commit("fix: something", &gogit.CommitOptions{Author: testSig()})
|
||||
|
||||
os.Chmod(filepath.Join(dir, "pyproject.toml"), 0444)
|
||||
defer os.Chmod(filepath.Join(dir, "pyproject.toml"), 0644)
|
||||
|
||||
if err := execCmd(t, "--branch", "release/1.2", "--repo", dir); err == nil {
|
||||
t.Fatal("expected error when pyproject.toml is read-only")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user