Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filter registered migrations by fs dirpath #553

Merged
merged 1 commit into from
Jul 11, 2023

Conversation

rafalneves
Copy link
Contributor

Closes #552.

Filters registered migration by correspondent fs file in the target dir path. This way, the same binary, can run goose multiple times, over different migration directories. Otherwise, all go migrations will run in the first goose run.

This modification shouldn't be a breaking change, at least not semantically. This was the behavior that I was expecting.
This way we are sure that only migrations in the specified directory are applied on a given run.

@rafalneves
Copy link
Contributor Author

Hi @mfridman, can you take a look at this, please?

Copy link
Collaborator

@mfridman mfridman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, before the code was including ALL go migrations from the registered global, but with this change we're filtering that global down to only the .go files from the filesystem. Essentially, this...

for _, migration := range registeredGoMigrations {
        //
	if _, ok := fsGoMigrations[v]; ok {
		migrations = append(migrations, migration)
	}
}

At first, this seems like a breaking change, but after looking at this a bit closer I don't think it is. The code previously assumed all .go migrations would be in the same folder and thus registered the same way, so that actually doesn't change 🤔

@rafalneves
Copy link
Contributor Author

If I understand correctly, before the code was including ALL go migrations from the registered global, but with this change we're filtering that global down to only the .go files from the filesystem.

Yeap, that's correct.

@rafalneves rafalneves requested a review from mfridman July 7, 2023 13:50
@mfridman
Copy link
Collaborator

mfridman commented Jul 8, 2023

Okay, this seems reasonable to me. Fwiw, this actually makes the implementation match the /v4 branch:

goose/provider.go

Lines 104 to 108 in 3d10b39

if goMigration, ok := registered[src.Version]; ok {
m = goMigration
} else {
unregistered = append(unregistered, currentBase)
}

... where we loop over the "sources" (filesystem), and ensure everything in the filesystem is registered in the global map.

In BOTH cases if a versioned filesystem .go migration isn't registered, it'll fail (this is the main thing I was looking for). This is intended to be a guard for users who added a "version"-looking migration file but didn't register it with an init. In the /v4 we have this comment to make this clear and a corresponding error message.

// Return an error if the given sources contain a versioned Go migration that has not been
// registered. This is a sanity check to ensure users didn't accidentally create a valid
// looking Go migration file and forget to register it.
//
// This is almost always a user error.

Copy link
Collaborator

@mfridman mfridman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm.

@rafalneves
Copy link
Contributor Author

Perfect, thanks for the review 🙌 . Please merge when ready.

@mfridman mfridman merged commit 928f577 into pressly:master Jul 11, 2023
@astromechza
Copy link

astromechza commented Jul 31, 2023

Unfortunately this was a breaking change for my app. I've been registering migrations that don't exist on the filesystem at all and are added as pure go functions. They now get excluded completely.

At least this got caught by the ErrNoMigrationFiles so we can detect and fix it.

I'll look at moving them to file-based migrations but I would prefer the single-binary approach.

@mfridman
Copy link
Collaborator

mfridman commented Jul 31, 2023

Ouch, sorry about that @astromechza. To clarify, were you using something like AddNamedMigration (or its equivalents) and adding a filename directly, but one that didn't exist on the filesystem?

I typically use Go's embed to include the files in the binary itself:

//go:embed *.sql
var EmbedMigrations embed.FS

And then goose.SetBaseFS(EmbedMigrations) to include them at the time of building the binary.

However, I could see a world where there is a "pure register function" that simply takes a version int64 and a function. We can them resolve whether it's a duplicate or not, but avoid touching the filesystem in its entirety.

@astromechza
Copy link

Yup, exactly, we were calling AddNamedMigration :), not big problem though, I've moved over to the embed FS since that's the one that's documented.

I think the docs do mention using AddMigration directly, and may need to be updated.

@mfridman
Copy link
Collaborator

Thanks for your report @astromechza, going to track this issue in #582

I'd like to bring back the ability to register pure go functions without forcing users to embed the source.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Go migrations run even if they are outside of the target migrations directory
3 participants