Allow explicit publication stores in Git metadata
This commit is contained in:
@@ -97,6 +97,10 @@ than relying on random retries.
|
|||||||
|
|
||||||
## R03: Allow an explicit override into Git metadata — P2
|
## R03: Allow an explicit override into Git metadata — P2
|
||||||
|
|
||||||
|
**Completed:** The override guard recognizes Git-discovered private and shared
|
||||||
|
metadata directories. Recording tests passed for ordinary, linked-worktree, and
|
||||||
|
separate-Git-directory layouts, alongside rejection of unignored worktree files.
|
||||||
|
|
||||||
**Relevant code:** `internal/publish/record.go:142`, especially the worktree
|
**Relevant code:** `internal/publish/record.go:142`, especially the worktree
|
||||||
containment and `git check-ignore` handling at lines 148–159.
|
containment and `git check-ignore` handling at lines 148–159.
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,23 @@ func normalizeRepositoryRoot(root string) (string, error) {
|
|||||||
return resolved, nil
|
return resolved, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MetadataDirectories returns the checkout's private and shared Git directories.
|
||||||
|
func MetadataDirectories(ctx context.Context, directory string) ([]string, error) {
|
||||||
|
var directories []string
|
||||||
|
for _, flag := range []string{"--absolute-git-dir", "--git-common-dir"} {
|
||||||
|
output, err := run(ctx, directory, nil, "rev-parse", "--path-format=absolute", flag)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("resolve Git metadata directory: %w", err)
|
||||||
|
}
|
||||||
|
resolved, err := normalizeRepositoryRoot(output)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
directories = append(directories, resolved)
|
||||||
|
}
|
||||||
|
return directories, nil
|
||||||
|
}
|
||||||
|
|
||||||
// DiscoverRepositoryRoot finds a containing checkout or confirms standalone use.
|
// DiscoverRepositoryRoot finds a containing checkout or confirms standalone use.
|
||||||
func DiscoverRepositoryRoot(ctx context.Context, directory string) (string, bool, error) {
|
func DiscoverRepositoryRoot(ctx context.Context, directory string) (string, bool, error) {
|
||||||
if directory == "" {
|
if directory == "" {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package publish
|
package publish
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -12,6 +13,8 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"go.maximumdirect.net/releasekit/internal/git"
|
||||||
)
|
)
|
||||||
|
|
||||||
const recordFormatVersion = 1
|
const recordFormatVersion = 1
|
||||||
@@ -148,6 +151,16 @@ func rejectUnignoredWorktreePath(repositoryDir, root, proposed string) error {
|
|||||||
if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
|
if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
metadata, err := git.MetadataDirectories(context.Background(), repositoryDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, directory := range metadata {
|
||||||
|
rel, err := filepath.Rel(directory, actual)
|
||||||
|
if err == nil && rel != ".." && !strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
cmd := exec.Command("git", "-C", repositoryDir, "check-ignore", "-q", "--", actual)
|
cmd := exec.Command("git", "-C", repositoryDir, "check-ignore", "-q", "--", actual)
|
||||||
err = cmd.Run()
|
err = cmd.Run()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|||||||
@@ -142,6 +142,36 @@ func TestSaveEvidenceResolvesRelativeAndIgnoredOverrides(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSaveEvidenceAllowsExplicitGitMetadataStores(t *testing.T) {
|
||||||
|
for _, layout := range []string{"ordinary", "linked", "separate"} {
|
||||||
|
t.Run(layout, func(t *testing.T) {
|
||||||
|
repo := recordRepo(t)
|
||||||
|
switch layout {
|
||||||
|
case "linked":
|
||||||
|
linked := filepath.Join(t.TempDir(), "linked")
|
||||||
|
recordGit(t, repo, "worktree", "add", "--detach", linked)
|
||||||
|
repo = linked
|
||||||
|
case "separate":
|
||||||
|
recordGit(t, repo, "init", "--separate-git-dir", filepath.Join(t.TempDir(), "metadata"))
|
||||||
|
}
|
||||||
|
defaultPath, err := SaveEvidence(repo, "", Evidence{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
privateDir := strings.TrimSuffix(recordGit(t, repo, "rev-parse", "--absolute-git-dir"), "\n")
|
||||||
|
for _, directory := range []string{filepath.Dir(defaultPath), filepath.Join(privateDir, "other-private-records")} {
|
||||||
|
path, err := SaveEvidence(repo, directory, Evidence{Tag: "v1.2.3"})
|
||||||
|
if err != nil || filepath.Dir(path) != directory {
|
||||||
|
t.Fatalf("explicit metadata store %q = %q, %v", directory, path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if status := recordGit(t, repo, "status", "--porcelain"); status != "" {
|
||||||
|
t.Fatalf("metadata records appeared in candidate: %s", status)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSaveEvidenceRejectsUnignoredOverrideInsideWorktree(t *testing.T) {
|
func TestSaveEvidenceRejectsUnignoredOverrideInsideWorktree(t *testing.T) {
|
||||||
repo := recordRepo(t)
|
repo := recordRepo(t)
|
||||||
_, err := SaveEvidence(repo, "records", Evidence{Tag: "v1.2.3"})
|
_, err := SaveEvidence(repo, "records", Evidence{Tag: "v1.2.3"})
|
||||||
|
|||||||
Reference in New Issue
Block a user