1package migrate
2
3import (
4 "context"
5 "os"
6 "path/filepath"
7 "strconv"
8
9 "github.com/charmbracelet/log/v2"
10 "github.com/charmbracelet/soft-serve/pkg/config"
11 "github.com/charmbracelet/soft-serve/pkg/db"
12 "github.com/charmbracelet/soft-serve/pkg/db/models"
13)
14
15const (
16 migrateLfsObjectsName = "migrate_lfs_objects"
17 migrateLfsObjectsVersion = 3
18)
19
20// Correct LFS objects relative path.
21// From OID[:2]/OID[2:4]/OID[4:] to OID[:2]/OID[2:4]/OID
22// See: https://github.com/git-lfs/git-lfs/blob/main/docs/spec.md#intercepting-git
23var migrateLfsObjects = Migration{
24 Name: migrateLfsObjectsName,
25 Version: migrateLfsObjectsVersion,
26 Migrate: func(ctx context.Context, tx *db.Tx) error {
27 cfg := config.FromContext(ctx)
28 logger := log.FromContext(ctx).WithPrefix("migrate_lfs_objects")
29
30 var repoIDs []int64
31 if err := tx.Select(&repoIDs, "SELECT id FROM repos"); err != nil {
32 return err //nolint:wrapcheck
33 }
34 for _, r := range repoIDs {
35 var objs []models.LFSObject
36 if err := tx.Select(&objs, "SELECT * FROM lfs_objects WHERE repo_id = ?", r); err != nil {
37 return err //nolint:wrapcheck
38 }
39 objsp := filepath.Join(cfg.DataPath, "lfs", strconv.FormatInt(r, 10), "objects")
40 for _, obj := range objs {
41 oldpath := filepath.Join(objsp, badRelativePath(obj.Oid))
42 newpath := filepath.Join(objsp, goodRelativePath(obj.Oid))
43 if _, err := os.Stat(oldpath); err == nil {
44 if err := os.Rename(oldpath, newpath); err != nil {
45 logger.Error("rename lfs object", "oldpath", oldpath, "newpath", newpath, "err", err)
46 continue
47 }
48 }
49 }
50 }
51 return nil
52 },
53 Rollback: func(context.Context, *db.Tx) error {
54 return nil
55 },
56}
57
58func goodRelativePath(oid string) string {
59 if len(oid) < 5 {
60 return oid
61 }
62 return filepath.Join(oid[:2], oid[2:4], oid)
63}
64
65func badRelativePath(oid string) string {
66 if len(oid) < 5 {
67 return oid
68 }
69 return filepath.Join(oid[:2], oid[2:4], oid[4:])
70}