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) | 	d.Decode(c) | ||||||
|  |  | ||||||
| 	// Perform migrations | 	// Perform migrations | ||||||
| 	if err := c.doMigrations(); err != nil { | 	err, migrated := c.doMigrations() | ||||||
| 		c.log.Error("Failed to run cache migrations", c.log.Args("error", err)) | 	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 | 	c.readFromFile = true | ||||||
|   | |||||||
							
								
								
									
										37
									
								
								internal/cache/cache_migrations.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										37
									
								
								internal/cache/cache_migrations.go
									
									
									
									
										vendored
									
									
								
							| @@ -5,9 +5,13 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
|  |  | ||||||
| 	"golang.org/x/mod/semver" | 	"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 | // Registry of migrations by version | ||||||
| var migrations = map[string]map[string]migrationFunc{ | var migrations = map[string]map[string]migrationFunc{ | ||||||
| @@ -20,38 +24,55 @@ var migrations = map[string]map[string]migrationFunc{ | |||||||
| // of cache read from disk. | // of cache read from disk. | ||||||
| // Does not check to ensure migrations were successful, | // Does not check to ensure migrations were successful, | ||||||
| // only checks if a version has been achieved | // only checks if a version has been achieved | ||||||
| func (c *Cache) DoMigrations() error { | func (c *Cache) DoMigrations() (error, int) { | ||||||
| 	c.lock.Lock() | 	c.lock.Lock() | ||||||
| 	defer c.lock.Unlock() | 	defer c.lock.Unlock() | ||||||
| 	return c.doMigrations() | 	return c.doMigrations() | ||||||
| } | } | ||||||
|  |  | ||||||
| func (c *Cache) doMigrations() error { | func (c *Cache) doMigrations() (error, int) { | ||||||
| 	var errs error | 	var errs error | ||||||
|  | 	var migrated int | ||||||
| 	for version, migrationFuncs := range migrations { | 	for version, migrationFuncs := range migrations { | ||||||
|  | 		var funcMigrated int | ||||||
| 		if semver.Compare(c.CacheVersion, version) < 0 { | 		if semver.Compare(c.CacheVersion, version) < 0 { | ||||||
| 			for name, migration := range migrationFuncs { | 			for name, migration := range migrationFuncs { | ||||||
| 				err := migration(c) | 				err, numMigrated := migration(c) | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					errs = errors.Join( | 					errs = errors.Join( | ||||||
| 						errs, | 						errs, | ||||||
| 						fmt.Errorf("%s - %s: %w", version, name, err), | 						fmt.Errorf("%s - %s: %w", version, name, err), | ||||||
| 					) | 					) | ||||||
| 				} | 				} | ||||||
|  | 				funcMigrated += numMigrated | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			// We've reached a cache version, update the CacheVersion | 			// We've reached a cache version, update the CacheVersion | ||||||
| 			// and write to disk | 			// and write to disk | ||||||
| 			if errs == nil { | 			if errs == nil && funcMigrated > 0 { | ||||||
| 				c.CacheVersion = version | 				c.CacheVersion = version | ||||||
| 				c.write() | 				c.write() | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 		migrated += funcMigrated | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return errs | 	return errs, migrated | ||||||
| } | } | ||||||
|  |  | ||||||
| func v010_aliases(c *Cache) error { | func v010_aliases(c *Cache) (error, int) { | ||||||
| 	return errors.New("unimplemented migration") | 	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" | 	"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 { | type Project struct { | ||||||
| 	ID                int | 	ID                int | ||||||
| 	Description       string | 	Description       string | ||||||
| @@ -44,19 +46,30 @@ func (pl *ProjectLanguages) AddLanguage(lang *ProjectLanguage) { | |||||||
| 	*pl = append(*pl, lang) | 	*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. | // along with the numerical ID of the project. | ||||||
| // Uses SSH URL and then Remote if previous is empty | // Uses SSH URL and then Remote if previous is empty | ||||||
| func (p *Project) GetID() string { | func (p *Project) GetID() string { | ||||||
| 	shaText := p.HTTPURLToRepo | 	return fmt.Sprintf("%s||%d", p.GetRemoteSha(), p.ID) | ||||||
| 	if shaText == "" && p.SSHURLToRepo != "" { | } | ||||||
| 		shaText = p.SSHURLToRepo |  | ||||||
| 	} else if shaText == "" { | func MakeID(remote string, projectID int) string { | ||||||
| 		shaText = p.Remote | 	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 | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	shortSha := fmt.Sprintf("%x", sha1.Sum([]byte(shaText)))[:12] | 	return GetRemoteSha(remote) | ||||||
| 	return fmt.Sprintf("%s||%d", shortSha, p.ID) | } | ||||||
|  |  | ||||||
|  | func GetRemoteSha(remote string) string { | ||||||
|  | 	return fmt.Sprintf("%x", sha1.Sum([]byte(remote)))[:12] | ||||||
| } | } | ||||||
|  |  | ||||||
| func (p *Project) String() string { | func (p *Project) String() string { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user