agentsclimarketplace

Kora database migration

Skill kora-projects/kora-skills/plugins/kora-v1/skills/kora-database-migration

Agent Skills for Kora Framework — compile-time DI for Java/Kotlin backend development.

Install
npx -y skills add kora-projects/kora-skills --skill kora-database-migration

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Kora database migration modules for Flyway and Liquibase that run schema migrations on application startup. Covers the FlywayJdbcDatabaseModule and LiquibaseJdbcDatabaseModule, the database-flyway and database-liquibase artifacts, FlywayConfig/LiquibaseConfig keys (locations, changelog, executeInTransaction, validateOnMigrate, mixed), versioned SQL scripts, and the recommended out-of-process strategy (Flyway Gradle plugin, K8s Job, CI) for horizontally scaled services. Use when wiring migrations into a @KoraApp, picking Flyway vs Liquibase, configuring flyway/liquibase config sections, or fixing checksum/race-condition failures on startup.

SKILL.md

11.6 KB, as published. Nobody here has run it

Kora Database Migration — Flyway and Liquibase

Kora ships two optional modules that run database migrations during the application's Lifecycle startup, on top of the JDBC datasource:

  • FlywayFlywayJdbcDatabaseModule, artifact database-flyway, config class FlywayConfig.
  • LiquibaseLiquibaseJdbcDatabaseModule, artifact database-liquibase, config class LiquibaseConfig.

Both require the JDBC module (database-jdbc). Migrations execute through a Lifecycle interceptor (FlywayJdbcDatabaseInterceptor / LiquibaseJdbcDatabaseInterceptor) before dependent components are initialized.

Production note. Kora's maintainers do not recommend running migrations from inside the application when the service scales horizontally — every replica and every restart would trigger a migration. Prefer the out-of-process strategy (Flyway Gradle plugin locally, K8s Job / CI in production). See Out-of-process migrations. The canonical kora-java-crud example wires only JdbcDatabaseModule and runs Flyway via the Gradle plugin, not the migration module.


Quick Start (Flyway, in-app)

1. Dependencies (build.gradle). Kora artifacts inherit the version from the kora-parent BOM — never version them individually. The annotation processor is mandatory: without it @KoraApp generates nothing.

dependencies {
    koraBom platform("ru.tinkoff.kora:kora-parent:1.2.17")
    annotationProcessor "ru.tinkoff.kora:annotation-processors"

    implementation "ru.tinkoff.kora:database-jdbc"     // required datasource
    implementation "ru.tinkoff.kora:database-flyway"   // Flyway migration module
}

Kotlin: replace the processor with ksp "ru.tinkoff.kora:symbol-processors".

2. Plug the module into @KoraApp:

import ru.tinkoff.kora.common.KoraApp;
import ru.tinkoff.kora.config.hocon.HoconConfigModule;
import ru.tinkoff.kora.database.flyway.FlywayJdbcDatabaseModule;

@KoraApp
public interface Application extends
        HoconConfigModule,
        FlywayJdbcDatabaseModule { }

3. Migration script at src/main/resources/db/migration/V1__setup_tables.sql:

CREATE TABLE IF NOT EXISTS categories (
    id   BIGINT  NOT NULL GENERATED ALWAYS AS IDENTITY,
    name VARCHAR NOT NULL,
    PRIMARY KEY (id)
);

CREATE TABLE IF NOT EXISTS pets (
    id          BIGINT   NOT NULL GENERATED ALWAYS AS IDENTITY,
    name        VARCHAR  NOT NULL,
    status      SMALLINT NOT NULL,
    category_id BIGINT   NOT NULL REFERENCES categories(id),
    PRIMARY KEY (id)
);

4. Configure (application.conf). The JDBC datasource and the flyway section both read environment variables for credentials:

db {
  jdbcUrl  = ${POSTGRES_JDBC_URL}
  username = ${POSTGRES_USER}
  password = ${POSTGRES_PASS}
}

flyway {
  locations = ["db/migration"]   // default; classpath dir holding V*.sql
}

On startup Flyway scans db/migration, applies pending scripts in version order, and records them in its flyway_schema_history table (created automatically).


Flyway vs Liquibase

FlywayLiquibase
Artifactdatabase-flywaydatabase-liquibase
ModuleFlywayJdbcDatabaseModuleLiquibaseJdbcDatabaseModule
Config classFlywayConfigLiquibaseConfig
Script formatversioned SQL (V<n>__<name>.sql)changelog (XML default; formatted SQL, YAML, JSON)
Default locationdb/migrationdb/changelog/db.changelog-master.xml
Rollbacknot in the open-source Flywaydeclarable in the changelog
Picked whenthe default; simplest, SQL-onlyyou need declarative rollback or already have a Liquibase changelog

Pick one module. Plug exactly one of the two into @KoraApp — they both manage the same schema and must not run together.


Flyway configuration

The keys below are the full FlywayConfig surface (defaults shown):

flyway {
  enabled              = true            // run migrations on startup; false disables
  locations            = ["db/migration"] // classpath dirs with V*.sql scripts
  executeInTransaction = true            // wrap each migration in a transaction
  validateOnMigrate    = true            // verify checksums of applied scripts first
  mixed                = false           // allow transactional + non-transactional in one run
  configurationProperties {}             // raw key/value passed to Flyway#configurationProperties
}
  • mixed = true is needed only for databases where some statements cannot run inside a transaction (PostgreSQL, Aurora PostgreSQL, SQL Server, SQLite) — e.g. CREATE INDEX CONCURRENTLY. When enabled, the whole run executes without a transaction.
  • configurationProperties is the escape hatch for any native Flyway option not surfaced as a typed key (e.g. flyway.defaultSchema).

See references/flyway-migration-reference.md for script naming rules, multiple locations, and checksum recovery.


Liquibase configuration

LiquibaseConfig exposes a single key: the path to the master changelog.

liquibase {
  changelog = "db/changelog/db.changelog-master.xml"  // default master file path
}

Point it at a formatted-SQL master if you prefer SQL over XML:

liquibase {
  changelog = "db/changelog/db.changelog-master.sql"
}

Liquibase formatted SQL requires a header line and a --changeset directive per change; rollback is declared with --rollback:

--liquibase formatted sql

--changeset developer:1
CREATE TABLE users (
    id    BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    email VARCHAR(255) NOT NULL UNIQUE
);
--rollback DROP TABLE users;

--changeset developer:2
CREATE TABLE orders (
    id      BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    user_id BIGINT NOT NULL REFERENCES users(id),
    total   DECIMAL(10,2) NOT NULL
);
--rollback DROP TABLE orders;

See references/liquibase-migration-reference.md for changeset directives (contexts, labels), include files, and rollback patterns.


Out-of-process migrations (recommended)

For horizontally scaled services, disable the in-app module and run migrations once, before the app starts.

Local development — Flyway Gradle plugin (as in kora-java-crud):

plugins {
    id "org.flywaydb.flyway" version "8.4.2"
}

flyway {
    url       = "jdbc:postgresql://localhost:5432/mydb"
    user      = "postgres"
    password  = "postgres"
    locations = ["classpath:db/migration"]
}
./gradlew flywayMigrate

Production — Kubernetes Job runs the migration container once before rolling out the app, and the app keeps flyway.enabled = false:

apiVersion: batch/v1
kind: Job
metadata:
  name: db-migration
spec:
  backoffLimit: 1
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: flyway
          image: flyway/flyway:8.4.2
          args: ["migrate"]
          env:
            - name: FLYWAY_URL
              valueFrom: { configMapKeyRef: { name: db-config, key: url } }

CI — invoke ./gradlew flywayMigrate (or the Liquibase equivalent) as a pipeline step against the target database before deployment.

If you keep the in-app module in a scaled deployment, two replicas may race on the first migration. Flyway serializes via its history table lock, but a failed/locked run can still block startup — out-of-process avoids this entirely.


Testing migrations

The kora-java-crud example does not call Flyway by hand in tests. It uses the Testcontainers extension io.goodforgod:testcontainers-extensions-postgres, which runs the same db/migration scripts against a throwaway Postgres container:

@TestcontainersPostgreSQL(
        network = @Network(shared = true),
        mode = ContainerMode.PER_RUN,
        migration = @Migration(
                engine = Migration.Engines.FLYWAY,
                apply  = Migration.Mode.PER_METHOD,
                drop   = Migration.Mode.PER_METHOD))
@KoraAppTest(Application.class)
class IntegrationTests implements KoraAppTestConfigModifier {

    @ConnectionPostgreSQL
    private JdbcConnection connection;

    // @TestComponent-injected repositories use the migrated schema
}

Test dependency:

testImplementation "io.goodforgod:testcontainers-extensions-postgres:0.13.1"
testImplementation "org.testcontainers:junit-jupiter:1.21.4"
testImplementation "ru.tinkoff.kora:test-junit5"

Migration.Engines.LIQUIBASE switches the same extension to Liquibase. See kora-testing-junit-java for @KoraAppTest.


What's in references/ and assets/

FilePurpose
references/flyway-migration-reference.mdScript naming, multiple locations, transactional vs mixed, checksum recovery
references/liquibase-migration-reference.mdFormatted-SQL changesets, rollback, contexts/labels, includes
assets/V1__initial_schema.sql.templateFlyway initial migration starter
assets/002-orders-table.sql.templateFollow-up Flyway/Liquibase change starter
assets/db.changelog-master.sql.templateLiquibase formatted-SQL master changelog
assets/README.mdHow to copy and rename the templates

Common pitfalls

SymptomFix
No migrations run on startupflyway.enabled/module present; locations points at a real classpath dir holding V*.sql
"Validation failed. Checksum changed"An applied script was edited. Never edit applied migrations — add a new versioned script, or repair the history out-of-process
CREATE INDEX CONCURRENTLY fails inside a transactionSet flyway.mixed = true (whole run becomes non-transactional)
Two replicas race on first deployDisable the in-app module; run migrations via Gradle plugin / K8s Job / CI
Liquibase formatted SQL ignoredFirst line must be --liquibase formatted sql; each change needs --changeset <author>:<id>
Both modules plugged into @KoraAppKeep exactly one of FlywayJdbcDatabaseModule / LiquibaseJdbcDatabaseModule

Related skills

Source of truth

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.