Reads a series of migration bundles from a file system, which practically speaking will always be the embedded FS read from the contents of the `migration/ /` subdirectory.
(migrationFS fs.FS, line string)
| 774 | // speaking will always be the embedded FS read from the contents of the |
| 775 | // `migration/<line>/` subdirectory. |
| 776 | func migrationsFromFS(migrationFS fs.FS, line string) ([]Migration, error) { |
| 777 | const subdir = "migration" |
| 778 | |
| 779 | var ( |
| 780 | lastBundle *Migration |
| 781 | migrations []Migration |
| 782 | ) |
| 783 | |
| 784 | err := fs.WalkDir(migrationFS, subdir, func(path string, entry fs.DirEntry, err error) error { |
| 785 | if err != nil { |
| 786 | return fmt.Errorf("error walking FS: %w", err) |
| 787 | } |
| 788 | |
| 789 | // The WalkDir callback is invoked for each embedded subdirectory and |
| 790 | // file. For our purposes here, we're only interested in files. |
| 791 | if entry.IsDir() { |
| 792 | return nil |
| 793 | } |
| 794 | |
| 795 | filename := path |
| 796 | |
| 797 | // Invoked with the full path name. Strip `migration/` from the front so |
| 798 | // we have a name that we can parse with. |
| 799 | if !strings.HasPrefix(filename, subdir) { |
| 800 | return fmt.Errorf("expected path %q to start with subdir %q", path, subdir) |
| 801 | } |
| 802 | filename = filename[len(subdir)+1:] |
| 803 | |
| 804 | // Ignore any migrations that don't belong to the line we're reading. |
| 805 | if !strings.HasPrefix(filename, line) { |
| 806 | return nil |
| 807 | } |
| 808 | filename = filename[len(line)+1:] |
| 809 | |
| 810 | versionStr, name, ok := strings.Cut(filename, "_") |
| 811 | if !ok { |
| 812 | return fmt.Errorf("expected name to start with version string like '001_': %q", filename) |
| 813 | } |
| 814 | |
| 815 | version, err := strconv.Atoi(versionStr) |
| 816 | if err != nil { |
| 817 | return fmt.Errorf("error parsing version %q: %w", versionStr, err) |
| 818 | } |
| 819 | |
| 820 | // Non-version name for the migration. So for `002_initial_schema` it |
| 821 | // would be `initial schema`. |
| 822 | name, _, _ = strings.Cut(name, ".") |
| 823 | name = strings.ReplaceAll(name, "_", " ") |
| 824 | |
| 825 | // This works because `fs.WalkDir` guarantees lexical order, so all 001* |
| 826 | // files always appear before all 002* files, etc. |
| 827 | if lastBundle == nil || lastBundle.Version != version { |
| 828 | migrations = append(migrations, Migration{Name: name, Version: version}) |
| 829 | lastBundle = &migrations[len(migrations)-1] |
| 830 | } |
| 831 | |
| 832 | file, err := migrationFS.Open(path) |
| 833 | if err != nil { |
searching dependent graphs…