From 8e7ffd8f4fdd8bcebf377982297ca73eb4185bbd Mon Sep 17 00:00:00 2001 From: vickydotbat Date: Sat, 18 Apr 2026 16:03:49 +0200 Subject: [PATCH] Resolve LFS objects from git common dir --- internal/pipeline/build.go | 25 ++++++++++++-- internal/pipeline/pipeline_test.go | 53 ++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/internal/pipeline/build.go b/internal/pipeline/build.go index 69f1513..04c91b8 100644 --- a/internal/pipeline/build.go +++ b/internal/pipeline/build.go @@ -733,10 +733,14 @@ func chunksFromManifest(assets []assetResource, entries []BuildManifestHAK) ([]h func collectLFSAssetInfo(p *project.Project) (map[string]lfsAssetInfo, error) { result := make(map[string]lfsAssetInfo) + lfsObjectRoot, err := resolveLFSObjectRoot(p.Root) + if err != nil { + return nil, err + } for _, rel := range p.Inventory.AssetFiles { rel = filepath.ToSlash(rel) abs := filepath.Join(p.AssetsDir(), filepath.FromSlash(rel)) - info, ok, err := parseLFSPointerFile(abs, p.Root) + info, ok, err := parseLFSPointerFile(abs, lfsObjectRoot) if err != nil { return nil, err } @@ -748,7 +752,22 @@ func collectLFSAssetInfo(p *project.Project) (map[string]lfsAssetInfo, error) { return result, nil } -func parseLFSPointerFile(path, repoRoot string) (lfsAssetInfo, bool, error) { +func resolveLFSObjectRoot(repoRoot string) (string, error) { + output, err := gitOutput(repoRoot, "rev-parse", "--git-common-dir") + if err != nil { + return filepath.Join(repoRoot, ".git", "lfs", "objects"), nil + } + commonDir := strings.TrimSpace(output) + if commonDir == "" { + return filepath.Join(repoRoot, ".git", "lfs", "objects"), nil + } + if !filepath.IsAbs(commonDir) { + commonDir = filepath.Join(repoRoot, commonDir) + } + return filepath.Join(filepath.Clean(commonDir), "lfs", "objects"), nil +} + +func parseLFSPointerFile(path, lfsObjectRoot string) (lfsAssetInfo, bool, error) { entry, err := os.Lstat(path) if err != nil { return lfsAssetInfo{}, false, fmt.Errorf("stat %s: %w", path, err) @@ -784,7 +803,7 @@ func parseLFSPointerFile(path, repoRoot string) (lfsAssetInfo, bool, error) { return lfsAssetInfo{}, false, nil } if len(info.OID) >= 4 { - info.ObjectPath = filepath.Join(repoRoot, ".git", "lfs", "objects", info.OID[:2], info.OID[2:4], info.OID) + info.ObjectPath = filepath.Join(lfsObjectRoot, info.OID[:2], info.OID[2:4], info.OID) } return info, true, nil } diff --git a/internal/pipeline/pipeline_test.go b/internal/pipeline/pipeline_test.go index 07fcb34..177792d 100644 --- a/internal/pipeline/pipeline_test.go +++ b/internal/pipeline/pipeline_test.go @@ -1874,6 +1874,59 @@ func TestPlanHAKChunksPutsNewestAssetsInLastChunk(t *testing.T) { } } +func TestCollectLFSAssetInfoUsesGitCommonDirForWorktree(t *testing.T) { + if _, err := exec.LookPath("git"); err != nil { + t.Skip("git not available") + } + + parent := t.TempDir() + repoRoot := filepath.Join(parent, "repo") + worktreeRoot := filepath.Join(parent, "worktree") + mustMkdir(t, repoRoot) + runGitTest(t, repoRoot, "init") + mustWriteFile(t, filepath.Join(repoRoot, "README.md"), "test\n") + runGitTest(t, repoRoot, "add", ".") + runGitTest(t, repoRoot, "config", "user.name", "Test User") + runGitTest(t, repoRoot, "config", "user.email", "test@example.com") + runGitTest(t, repoRoot, "commit", "-m", "initial") + runGitTest(t, repoRoot, "worktree", "add", worktreeRoot, "HEAD") + + oid := strings.Repeat("a", 64) + assetRel := filepath.Join("core", "foo.mdl") + assetPath := filepath.Join(worktreeRoot, "assets", assetRel) + mustMkdir(t, filepath.Dir(assetPath)) + mustWriteFile(t, assetPath, "version https://git-lfs.github.com/spec/v1\n"+"oid sha256:"+oid+"\n"+"size 5\n") + + lfsObjectPath := filepath.Join(repoRoot, ".git", "lfs", "objects", oid[:2], oid[2:4], oid) + mustMkdir(t, filepath.Dir(lfsObjectPath)) + mustWriteFile(t, lfsObjectPath, "model") + + proj := &project.Project{ + Root: worktreeRoot, + Config: project.Config{ + Paths: project.PathConfig{Source: "src", Assets: "assets", Build: "build"}, + }, + Inventory: project.Inventory{ + AssetFiles: []string{filepath.ToSlash(assetRel)}, + }, + } + + infos, err := collectLFSAssetInfo(proj) + if err != nil { + t.Fatalf("collect lfs asset info: %v", err) + } + info, ok := infos[filepath.ToSlash(assetRel)] + if !ok { + t.Fatal("expected LFS asset info") + } + if got, want := info.ObjectPath, lfsObjectPath; got != want { + t.Fatalf("unexpected LFS object path:\ngot %s\nwant %s", got, want) + } + if _, err := lfsPathResource(assetPath, info, true); err != nil { + t.Fatalf("expected worktree LFS object to resolve: %v", err) + } +} + func runGitCommitTest(t *testing.T, dir, timestamp, message string) { t.Helper() runGitTest(t, dir, "add", ".")