Preserve existing model files when not using --clean

This commit is contained in:
2026-03-29 18:43:32 +02:00
parent 5c40cc142f
commit 987e55de10
3 changed files with 43 additions and 148 deletions

127
gen.go
View File

@@ -92,27 +92,17 @@ func ConnectDSN(dsn string) (*gorm.DB, error) {
// It cleans the output directory (if Clean is true), generates models using gorm.io/gen,
// and appends <Model>Cols variables with type-safe Field descriptors.
func (m *GormGen) GenModels(ctx context.Context) error {
dir := m.cfg.OutputDir
if !strings.HasPrefix(dir, "./") {
dir = "./" + dir
}
absDir, err := filepath.Abs(dir)
if err != nil {
return err
}
// When Clean is false, backup existing files first.
var backupDir string
if !m.cfg.Clean {
backupDir, err = m.backupDir(absDir)
if err != nil {
return fmt.Errorf("failed to backup existing files: %w", err)
// Clean output directory if Clean is true.
if m.cfg.Clean {
if err := m.cleanOutputDir(); err != nil {
return fmt.Errorf("failed to clean output dir: %w", err)
}
defer os.RemoveAll(backupDir)
}
if err := m.cleanOutputDir(); err != nil {
return fmt.Errorf("failed to clean output dir: %w", err)
// When Clean is false, save existing files to restore later.
var existingFiles map[string][]byte
if !m.cfg.Clean {
existingFiles = m.saveExistingFiles()
}
g := gen.NewGenerator(gen.Config{
@@ -164,11 +154,9 @@ func (m *GormGen) GenModels(ctx context.Context) error {
return fmt.Errorf("failed to cleanup generated files: %w", err)
}
// Restore backup if Clean was false.
if backupDir != "" {
if err := m.restoreBackup(absDir, backupDir); err != nil {
return fmt.Errorf("failed to restore backup: %w", err)
}
// Restore existing files that were not regenerated.
if !m.cfg.Clean {
m.restoreExistingFiles(existingFiles)
}
if err := m.generateCols(); err != nil {
@@ -178,75 +166,51 @@ func (m *GormGen) GenModels(ctx context.Context) error {
return nil
}
// backupDir creates a backup copy of the directory and returns the backup path.
func (m *GormGen) backupDir(dir string) (string, error) {
backupPath := dir + ".backup"
if _, err := os.Stat(backupPath); err == nil {
os.RemoveAll(backupPath)
// saveExistingFiles saves the content of existing .go files in the output directory.
func (m *GormGen) saveExistingFiles() map[string][]byte {
dir := m.cfg.OutputDir
if !strings.HasPrefix(dir, "./") {
dir = "./" + dir
}
if err := copyDir(dir, backupPath); err != nil {
return "", err
files := make(map[string][]byte)
entries, err := os.ReadDir(dir)
if err != nil {
return files
}
fmt.Printf("Backed up: %s -> %s\n", dir, backupPath)
return backupPath, nil
for _, entry := range entries {
if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") {
continue
}
path := filepath.Join(dir, entry.Name())
if data, err := os.ReadFile(path); err == nil {
files[entry.Name()] = data
}
}
return files
}
// restoreBackup restores files from backup that don't exist in the target directory.
func (m *GormGen) restoreBackup(dir, backupDir string) error {
entries, err := os.ReadDir(backupDir)
if err != nil {
return err
// restoreExistingFiles restores files that weren't regenerated.
func (m *GormGen) restoreExistingFiles(saved map[string][]byte) {
dir := m.cfg.OutputDir
if !strings.HasPrefix(dir, "./") {
dir = "./" + dir
}
restored := 0
for _, entry := range entries {
if entry.IsDir() {
continue
}
targetPath := filepath.Join(dir, entry.Name())
if _, err := os.Stat(targetPath); os.IsNotExist(err) {
srcPath := filepath.Join(backupDir, entry.Name())
data, err := os.ReadFile(srcPath)
if err != nil {
continue
for name, data := range saved {
path := filepath.Join(dir, name)
if _, err := os.Stat(path); os.IsNotExist(err) {
if err := os.WriteFile(path, data, 0644); err == nil {
fmt.Printf("Restored: %s\n", path)
restored++
}
if err := os.WriteFile(targetPath, data, 0644); err != nil {
return err
}
fmt.Printf("Restored: %s\n", targetPath)
restored++
}
}
if restored > 0 {
fmt.Printf("Restored %d files from backup\n", restored)
fmt.Printf("Restored %d existing files\n", restored)
}
return nil
}
// copyDir copies a directory recursively.
func copyDir(src, dst string) error {
return filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
rel, _ := filepath.Rel(src, path)
dstPath := filepath.Join(dst, rel)
if info.IsDir() {
return os.MkdirAll(dstPath, info.Mode())
}
return copyFile(path, dstPath)
})
}
// copyFile copies a single file.
func copyFile(src, dst string) error {
data, err := os.ReadFile(src)
if err != nil {
return err
}
return os.WriteFile(dst, data, 0644)
}
// cleanOutputDir removes existing .go files from the output directory
@@ -270,11 +234,6 @@ func (m *GormGen) cleanOutputDir() error {
return nil
}
// Skip cleaning if Clean is false.
if !m.cfg.Clean {
return nil
}
entries, err := os.ReadDir(absDir)
if err != nil {
return err