avoid-project-id-collisions #2
							
								
								
									
										12
									
								
								internal/cache/cache.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								internal/cache/cache.go
									
									
									
									
										vendored
									
									
								
							| @@ -122,8 +122,16 @@ func (c *Cache) Read() error { | ||||
| 	d.Decode(c) | ||||
|  | ||||
| 	// Perform migrations | ||||
| 	if err := c.doMigrations(); err != nil { | ||||
| 		c.log.Error("Failed to run cache migrations", c.log.Args("error", err)) | ||||
| 	err, migrated := c.doMigrations() | ||||
| 	if err != nil { | ||||
| 		c.log.Error("Failed to run cache migrations", | ||||
| 			c.log.Args( | ||||
| 				"migrated", migrated, | ||||
| 				"error", err, | ||||
| 			)) | ||||
| 	} else if migrated > 0 { | ||||
| 		c.log.Info("Migrations run successfully", c.log.Args( | ||||
| 			"migrated", migrated)) | ||||
| 	} | ||||
|  | ||||
| 	c.readFromFile = true | ||||
|   | ||||
							
								
								
									
										37
									
								
								internal/cache/cache_migrations.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										37
									
								
								internal/cache/cache_migrations.go
									
									
									
									
										vendored
									
									
								
							| @@ -5,9 +5,13 @@ import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"golang.org/x/mod/semver" | ||||
|  | ||||
| 	"gitea.libretechconsulting.com/rmcguire/git-project-manager/internal/remotes/projects" | ||||
| ) | ||||
|  | ||||
| type migrationFunc func(c *Cache) error | ||||
| // Migrations funcs should return errors along with | ||||
| // number of records updated | ||||
| type migrationFunc func(c *Cache) (error, int) | ||||
|  | ||||
| // Registry of migrations by version | ||||
| var migrations = map[string]map[string]migrationFunc{ | ||||
| @@ -20,38 +24,55 @@ var migrations = map[string]map[string]migrationFunc{ | ||||
| // of cache read from disk. | ||||
| // Does not check to ensure migrations were successful, | ||||
| // only checks if a version has been achieved | ||||
| func (c *Cache) DoMigrations() error { | ||||
| func (c *Cache) DoMigrations() (error, int) { | ||||
| 	c.lock.Lock() | ||||
| 	defer c.lock.Unlock() | ||||
| 	return c.doMigrations() | ||||
| } | ||||
|  | ||||
| func (c *Cache) doMigrations() error { | ||||
| func (c *Cache) doMigrations() (error, int) { | ||||
| 	var errs error | ||||
| 	var migrated int | ||||
| 	for version, migrationFuncs := range migrations { | ||||
| 		var funcMigrated int | ||||
| 		if semver.Compare(c.CacheVersion, version) < 0 { | ||||
| 			for name, migration := range migrationFuncs { | ||||
| 				err := migration(c) | ||||
| 				err, numMigrated := migration(c) | ||||
| 				if err != nil { | ||||
| 					errs = errors.Join( | ||||
| 						errs, | ||||
| 						fmt.Errorf("%s - %s: %w", version, name, err), | ||||
| 					) | ||||
| 				} | ||||
| 				funcMigrated += numMigrated | ||||
| 			} | ||||
|  | ||||
| 			// We've reached a cache version, update the CacheVersion | ||||
| 			// and write to disk | ||||
| 			if errs == nil { | ||||
| 			if errs == nil && funcMigrated > 0 { | ||||
| 				c.CacheVersion = version | ||||
| 				c.write() | ||||
| 			} | ||||
| 		} | ||||
| 		migrated += funcMigrated | ||||
| 	} | ||||
|  | ||||
| 	return errs | ||||
| 	return errs, migrated | ||||
| } | ||||
|  | ||||
| func v010_aliases(c *Cache) error { | ||||
| 	return errors.New("unimplemented migration") | ||||
| func v010_aliases(c *Cache) (error, int) { | ||||
| 	var aliasesMigrated int | ||||
| 	var errs error | ||||
| 	for i, a := range c.Aliases { | ||||
| 		if a.ID == "" { | ||||
| 			if a.Remote == "" { | ||||
| 				errs = errors.Join(errs, | ||||
| 					fmt.Errorf("alias %s [id:%d] has no remote", a.Alias, a.ProjectID)) | ||||
| 				continue | ||||
| 			} | ||||
| 			c.Aliases[i].ID = projects.MakeID(a.Remote, a.ProjectID) | ||||
| 			aliasesMigrated++ | ||||
| 		} | ||||
| 	} | ||||
| 	return errs, aliasesMigrated | ||||
| } | ||||
|   | ||||
| @@ -9,6 +9,8 @@ import ( | ||||
| 	"github.com/go-git/go-git/v5" | ||||
| ) | ||||
|  | ||||
| // Git project metadata | ||||
| // Do not use Project.ID directly (remotes may conflict), use Project.GetID() | ||||
| type Project struct { | ||||
| 	ID                int | ||||
| 	Description       string | ||||
| @@ -44,19 +46,30 @@ func (pl *ProjectLanguages) AddLanguage(lang *ProjectLanguage) { | ||||
| 	*pl = append(*pl, lang) | ||||
| } | ||||
|  | ||||
| // Gets a unique ID using a short-sha of the http repo URL | ||||
| // Gets a unique ID using a short-sha of the http repo | ||||
| // along with the numerical ID of the project. | ||||
| // Uses SSH URL and then Remote if previous is empty | ||||
| func (p *Project) GetID() string { | ||||
| 	shaText := p.HTTPURLToRepo | ||||
| 	if shaText == "" && p.SSHURLToRepo != "" { | ||||
| 		shaText = p.SSHURLToRepo | ||||
| 	} else if shaText == "" { | ||||
| 		shaText = p.Remote | ||||
| 	return fmt.Sprintf("%s||%d", p.GetRemoteSha(), p.ID) | ||||
| } | ||||
|  | ||||
| 	shortSha := fmt.Sprintf("%x", sha1.Sum([]byte(shaText)))[:12] | ||||
| 	return fmt.Sprintf("%s||%d", shortSha, p.ID) | ||||
| func MakeID(remote string, projectID int) string { | ||||
| 	return fmt.Sprintf("%s||%d", GetRemoteSha(remote), projectID) | ||||
| } | ||||
|  | ||||
| func (p *Project) GetRemoteSha() string { | ||||
| 	remote := p.Remote | ||||
| 	if remote == "" && p.HTTPURLToRepo != "" { | ||||
| 		remote = p.HTTPURLToRepo | ||||
| 	} else if remote == "" && p.WebURL != "" { | ||||
| 		remote = p.WebURL | ||||
| 	} | ||||
|  | ||||
| 	return GetRemoteSha(remote) | ||||
| } | ||||
|  | ||||
| func GetRemoteSha(remote string) string { | ||||
| 	return fmt.Sprintf("%x", sha1.Sum([]byte(remote)))[:12] | ||||
| } | ||||
|  | ||||
| func (p *Project) String() string { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user