migrate runs migrations from the provided filesystem
(db *gorm.DB, fsys fs.FS)
| 112 | |
| 113 | // migrate runs migrations from the provided filesystem |
| 114 | func migrate(db *gorm.DB, fsys fs.FS) error { |
| 115 | if err := db.Exec(` |
| 116 | CREATE TABLE IF NOT EXISTS schema_migrations ( |
| 117 | version INTEGER PRIMARY KEY, |
| 118 | applied_at DATETIME DEFAULT CURRENT_TIMESTAMP |
| 119 | ) |
| 120 | `).Error; err != nil { |
| 121 | return errors.Wrap(err, "initializing migration table") |
| 122 | } |
| 123 | |
| 124 | // Get current version |
| 125 | var version int |
| 126 | if err := db.Raw("SELECT COALESCE(MAX(version), 0) FROM schema_migrations").Scan(&version).Error; err != nil { |
| 127 | return errors.Wrap(err, "reading current version") |
| 128 | } |
| 129 | |
| 130 | // Read and validate migration files |
| 131 | migrations, err := getMigrationFiles(fsys) |
| 132 | if err != nil { |
| 133 | return err |
| 134 | } |
| 135 | |
| 136 | var filenames []string |
| 137 | for _, m := range migrations { |
| 138 | filenames = append(filenames, m.filename) |
| 139 | } |
| 140 | |
| 141 | log.WithFields(log.Fields{ |
| 142 | "version": version, |
| 143 | }).Info("Database schema version.") |
| 144 | |
| 145 | log.WithFields(log.Fields{ |
| 146 | "files": filenames, |
| 147 | }).Debug("Database migration files.") |
| 148 | |
| 149 | // Apply pending migrations |
| 150 | for _, m := range migrations { |
| 151 | if m.version <= version { |
| 152 | continue |
| 153 | } |
| 154 | |
| 155 | log.WithFields(log.Fields{ |
| 156 | "file": m.filename, |
| 157 | }).Info("Applying migration.") |
| 158 | |
| 159 | sql, err := fs.ReadFile(fsys, m.filename) |
| 160 | if err != nil { |
| 161 | return errors.Wrapf(err, "reading migration file %s", m.filename) |
| 162 | } |
| 163 | |
| 164 | if len(strings.TrimSpace(string(sql))) == 0 { |
| 165 | return errors.Errorf("migration file %s is empty", m.filename) |
| 166 | } |
| 167 | |
| 168 | if err := db.Exec(string(sql)).Error; err != nil { |
| 169 | return fmt.Errorf("migration %s failed: %w", m.filename, err) |
| 170 | } |
| 171 |