Fix prod deploy Flyway validation for removed dev seed migrations.
Some checks failed
CI / E2E browser tests (push) Has been cancelled
CI / Lint, type check, unit tests, coverage (push) Has been cancelled

Backend crashed on startup because the prod DB still records V6 (and
possibly V2/V4) from when seeds lived in db/migration, while prod only
loads schema migrations. ignore-migration-patterns alone did not prevent
validate failure on the runner.

- Run Flyway repair before migrate on the prod profile
- Add ProdFlywayConfigTest for repair-then-migrate order
- Document the V6 error in README deploy troubleshooting
This commit is contained in:
Joakim Mörling 2026-05-21 16:52:15 +02:00
parent 61a7b8a40c
commit fb9713d8d8
3 changed files with 62 additions and 1 deletions

View file

@ -317,11 +317,15 @@ If the job passes the frontend check but the backend never becomes healthy:
1. Open the failed job log and read **Backend logs** (printed before rollback).
2. Match the error to a fix — do not guess:
- **`Detected applied migration not resolved locally: 6`** (or 2, 4) — prod DB still lists
dev seed migrations removed from `db/migration`. Fixed in app via `ProdFlywayConfig`
(repair before migrate); redeploy after that commit is on `master`.
- **`password authentication failed`** — DB credentials in the running stack do not match
what Postgres was initialized with; fix credentials or Postgres password to match (only
wipe the volume if you accept losing prod data).
- **`Production requires ADMIN_EMAIL and ADMIN_PASSWORD`** — add those Forgejo secrets.
- **Flyway / migration errors** — fix schema or migration history before redeploying.
- **Other Flyway / migration errors** — read the stack trace; do not wipe the volume unless
the log clearly requires it.
3. **DBeaver from your laptop** — prod Postgres binds to `127.0.0.1:5433` on the server only.
Use an SSH tunnel, then host `localhost` port `5433` (not `192.168.0.59` directly).

View file

@ -0,0 +1,24 @@
package se.bilhalsning.config;
import org.flywaydb.core.Flyway;
import org.springframework.boot.flyway.autoconfigure.FlywayMigrationStrategy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Configuration
@Profile("prod")
public class ProdFlywayConfig {
/**
* Prod databases may record dev seed migrations (V2, V4, V6) from before they moved to
* db/dev-migration/. Repair marks those missing scripts as deleted so validate passes.
*/
@Bean
public FlywayMigrationStrategy prodFlywayMigrationStrategy() {
return (Flyway flyway) -> {
flyway.repair();
flyway.migrate();
};
}
}

View file

@ -0,0 +1,33 @@
package se.bilhalsning.config;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import org.flywaydb.core.Flyway;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.flyway.autoconfigure.FlywayMigrationStrategy;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.mockito.InOrder;
@SpringBootTest(
properties = {
"app.admin.email=admin@test.se",
"app.admin.password=test-password",
})
@ActiveProfiles("prod")
class ProdFlywayConfigTest {
@Autowired
private FlywayMigrationStrategy flywayMigrationStrategy;
@Test
void shouldRepairBeforeMigrateOnProd() {
Flyway flyway = mock(Flyway.class);
flywayMigrationStrategy.migrate(flyway);
InOrder order = inOrder(flyway);
order.verify(flyway).repair();
order.verify(flyway).migrate();
}
}