Как правильно реализовать continuous deployment для запуска миграций?
Ну все руками делается.
Делаете каталог для файлов миграции и в нем какой-то главный типа control.sh
в control.sh инклюдите актуальные файлы миграции
Для дедупликации проще всего в базе сделать таблицу, в которую при миграции добавляется "дата, имя скрипта миграции, статус (completed/failed), и при выполнении скрипта миграции соответствнено он проверяет по базе выполнялся уже или нет, чтобы не запускаться вторично. Может быть какие-то скрипты наоборот нужно прогонять каждый раз, тогда можно в таблицу добавить колонку для такого флага.

И все. При автодеплое конкретной версии - в ее ветке оно найдет в control.sh список миграционных скриптов, control.sh через базу данных может перепровить надо ли их запускать или пропустить, если они уже выполнились, или выполнить независимо от этого и сделает запись в базу о выполнении.

Естественно контроль - ответственность разработчиков, магии тут не будет. И предполагается, что вы всегда деплоите более позднюю версию поверх старой.
Не изобретайте велосипед.

Есть готовый инструмент для миграций Flyway - проще некуда.