We write the migrations in SQL, committed as part of the pull request. Migrations tested on copies of production database along with the code changes. Automatically applied by a script when a pull request gets merged/deployed. No special tooling really, just a couple of SQL files.
We have a SQL preprocessor that lets us refer to environment globals and class constants in SQL, so we're not hard-coding magic numbers and strings in the SQL. It resolves those with reflection on the code and replaces the named globals/constants before sending the SQL to the database.