-
Notifications
You must be signed in to change notification settings - Fork 0
Claude/add golang migrate go qj0q z #262
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
Conversation
Add automated database migration support using Flyway to the Kotlin/Ktor implementation, bringing it to feature parity with the Java implementation. Changes: - Add Flyway dependencies (flyway-core and flyway-database-postgresql) - Create FlywayConfig for migration management on application startup - Add three versioned migrations: * V1__Initial_schema.sql: Creates base lamps table * V2__Add_soft_deletes.sql: Adds deleted_at column * V3__Add_updated_at_trigger.sql: Adds PostgreSQL trigger for updated_at - Integrate migration runner into DatabaseFactory.init() - Add MIGRATIONS.md documentation The migrations are applied automatically on application startup before initializing the database connection pool. Failed migrations prevent application startup to ensure database consistency.
Add automated database migration support using golang-migrate to the Go implementation, bringing it to feature parity with other language implementations. Changes: - Add golang-migrate dependencies (migrate/v4 and postgres driver) - Create RunMigrations function in api/migrate.go with embedded migrations - Add six migration files (3 up, 3 down): * 000001_initial_schema: Creates base lamps table * 000002_add_soft_deletes: Adds deleted_at column * 000003_add_updated_at_trigger: Adds PostgreSQL trigger for updated_at - Integrate migration runner into main.go startup sequence - Add MIGRATIONS.md documentation Migrations are embedded into the binary using Go's embed package, allowing single-binary deployment. They are applied automatically on application startup before initializing the database connection pool. Failed migrations prevent connection pool creation to ensure database consistency. The implementation supports both forward (up) and backward (down) migrations, with migration history tracked in the schema_migrations table.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds automated database migration support to both Go and Kotlin implementations of the lamp control API. The Go implementation uses golang-migrate v4.19.1 with embedded migration files, while the Kotlin implementation uses Flyway 10.21.0. Both implementations include three versioned migrations: initial schema, soft deletes, and automated updated_at trigger.
Changes:
- Added golang-migrate dependency and migration infrastructure to Go implementation with embedded SQL files
- Added Flyway dependency and migration configuration to Kotlin implementation
- Created identical SQL migration files (V1-V3) for both implementations covering initial schema, soft deletes, and updated_at triggers
- Added comprehensive MIGRATIONS.md documentation for both implementations explaining usage, troubleshooting, and rollback procedures
Reviewed changes
Copilot reviewed 17 out of 18 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/go/go.mod | Added golang-migrate v4.19.1 dependency and updated transitive dependencies |
| src/go/go.sum | Updated checksums for new and updated dependencies |
| src/go/api/migrate.go | Implemented RunMigrations function using golang-migrate with embedded filesystem |
| src/go/cmd/lamp-control-api/main.go | Integrated migration execution before connection pool creation with fallback logic |
| src/go/api/migrations/*.sql | Created six migration files (up and down) for three schema versions |
| src/go/MIGRATIONS.md | Added comprehensive documentation for golang-migrate usage and troubleshooting |
| src/kotlin/build.gradle.kts | Added Flyway 10.21.0 dependencies for core and PostgreSQL |
| src/kotlin/src/main/kotlin/com/lampcontrol/database/FlywayConfig.kt | Implemented Flyway configuration object with runMigrations function |
| src/kotlin/src/main/kotlin/com/lampcontrol/database/DatabaseFactory.kt | Integrated Flyway migration execution before database initialization |
| src/kotlin/src/main/resources/db/migration/*.sql | Created three Flyway migration files following V{version}__{description}.sql naming |
| src/kotlin/MIGRATIONS.md | Added comprehensive documentation for Flyway usage and comparison with other implementations |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| - **Python**: Uses Alembic (Python-specific migration tool) | ||
| - **TypeScript**: Uses Prisma Migrate (schema-first approach) | ||
| - **C#**: Uses Entity Framework Migrations (code-first approach) | ||
| - **Go**: Manual migrations (shared schema file) |
Copilot
AI
Jan 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This documentation is now outdated. Since this PR adds golang-migrate support to the Go implementation, the reference should be updated to reflect that Go now also uses automated migrations with golang-migrate, similar to the other implementations. The current text "Manual migrations (shared schema file)" no longer accurately describes the Go implementation.
| - **Go**: Manual migrations (shared schema file) | |
| - **Go**: Uses golang-migrate for automated SQL-based migrations |
| @@ -0,0 +1,57 @@ | |||
| package com.lampcontrol.database | |||
|
|
|||
| import com.zaxxer.hikari.HikariDataSource | |||
Copilot
AI
Jan 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The HikariDataSource import is unused in this file. Flyway is configured directly with the connection string, user, and password parameters, and doesn't require a HikariDataSource instance. This import should be removed.
| import com.zaxxer.hikari.HikariDataSource |
src/go/cmd/lamp-control-api/main.go
Outdated
| // Run database migrations before creating connection pool | ||
| connectionString := dbConfig.ConnectionString() | ||
| // golang-migrate requires postgres:// prefix instead of postgresql:// | ||
| // and needs sslmode parameter | ||
| if len(connectionString) >= 10 && connectionString[:10] == "host=" { | ||
| // If using component-based connection string, convert to URL format for migrate | ||
| connectionString = fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=disable", | ||
| dbConfig.User, dbConfig.Password, dbConfig.Host, dbConfig.Port, dbConfig.Database) | ||
| } |
Copilot
AI
Jan 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The connection string conversion logic has a potential issue. When DATABASE_URL is set, it's returned as-is by ConnectionString(), which might use the "postgresql://" prefix. However, golang-migrate requires "postgres://" prefix. The current logic only handles conversion from component-based format ("host=...") but doesn't handle the case where DATABASE_URL uses "postgresql://" instead of "postgres://". Consider also checking for and converting "postgresql://" prefix to "postgres://" to ensure compatibility with golang-migrate.
| func RunMigrations(connectionString string) error { | ||
| log.Println("Starting database migrations...") | ||
|
|
||
| // Create source driver from embedded filesystem | ||
| sourceDriver, err := iofs.New(migrationsFS, "migrations") | ||
| if err != nil { | ||
| return fmt.Errorf("failed to create migration source driver: %w", err) | ||
| } | ||
|
|
||
| // Create migrate instance | ||
| m, err := migrate.NewWithSourceInstance("iofs", sourceDriver, connectionString) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to create migrate instance: %w", err) | ||
| } | ||
| defer func() { | ||
| if srcErr, dbErr := m.Close(); srcErr != nil || dbErr != nil { | ||
| log.Printf("Warning: error closing migrate instance: source=%v, database=%v", srcErr, dbErr) | ||
| } | ||
| }() | ||
|
|
||
| // Run migrations | ||
| if err := m.Up(); err != nil { | ||
| if err == migrate.ErrNoChange { | ||
| log.Println("Database schema is up to date (no migrations to apply)") | ||
| return nil | ||
| } | ||
| return fmt.Errorf("failed to run migrations: %w", err) | ||
| } | ||
|
|
||
| // Get current version | ||
| version, dirty, err := m.Version() | ||
| if err != nil && err != migrate.ErrNilVersion { | ||
| log.Printf("Warning: could not determine migration version: %v", err) | ||
| } else if dirty { | ||
| log.Printf("Warning: database is in dirty state at version %d", version) | ||
| } else { | ||
| log.Printf("Successfully applied migrations. Current schema version: %d", version) | ||
| } | ||
|
|
||
| return nil | ||
| } |
Copilot
AI
Jan 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new migration functionality in migrate.go lacks test coverage. While the Go codebase maintains comprehensive test coverage (80%+ minimum per coding guidelines), the RunMigrations function has no associated tests. Consider adding tests that verify: (1) handling of ErrNoChange when schema is up to date, (2) proper error handling for invalid connection strings, (3) proper resource cleanup via defer. Note that these could be unit tests using test fixtures or mocked migration instances, not necessarily requiring a live database.
| fun runMigrations(config: DatabaseConfig): Boolean { | ||
| return try { | ||
| logger.info("Starting database migrations with Flyway") | ||
|
|
||
| val flyway = Flyway.configure() | ||
| .dataSource( | ||
| config.connectionString(), | ||
| config.user, | ||
| config.password | ||
| ) | ||
| .locations("classpath:db/migration") | ||
| .baselineOnMigrate(true) | ||
| .baselineVersion("0") | ||
| .validateOnMigrate(true) | ||
| .load() | ||
|
|
||
| val migrationsExecuted = flyway.migrate() | ||
|
|
||
| if (migrationsExecuted.migrationsExecuted > 0) { | ||
| logger.info( | ||
| "Successfully executed {} migration(s). Current schema version: {}", | ||
| migrationsExecuted.migrationsExecuted, | ||
| migrationsExecuted.targetSchemaVersion | ||
| ) | ||
| } else { | ||
| logger.info( | ||
| "Database schema is up to date at version: {}", | ||
| flyway.info().current()?.version ?: "baseline" | ||
| ) | ||
| } | ||
|
|
||
| true | ||
| } catch (e: Exception) { | ||
| logger.error("Failed to run database migrations", e) | ||
| false | ||
| } | ||
| } |
Copilot
AI
Jan 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The FlywayConfig.runMigrations function lacks test coverage. While the Kotlin codebase has comprehensive tests for database configuration, the migration functionality itself is not tested. Consider adding tests to verify: (1) successful migration execution, (2) proper error handling and logging when migrations fail, (3) behavior when schema is already up to date. These could be unit tests using an embedded H2 database or mocked Flyway instances.
Add support for three distinct operation modes across all 6 language implementations: serve (default, run migrations then start server), migrate (run migrations only), and serve-only (start server without migrations). This enables better control over database migrations in production environments, supporting patterns like init containers, separate migration jobs, and zero-downtime deployments. Changes by implementation: **Go:** - Add --mode flag with serve/migrate/serve-only options - Refactor main.go with runMigrationsOnly() and initializeRepository() - Migrations run conditionally based on mode **Kotlin:** - Add --mode argument parsing in Application.kt - Add runMigrationsOnly() for migration-only mode - Use System property to skip migrations in serve-only mode - Update DatabaseFactory to check skip.migrations property **Java (Spring Boot):** - Create ApplicationMode class for mode handling - Add mode parsing and configuration in main method - Control spring.flyway.enabled based on mode - Separate runMigrationsOnly() method for migrate mode **Python (FastAPI):** - Create new cli.py module for CLI handling - Add argparse-based mode selection - Implement run_migrations_only() using Alembic - Conditionally run migrations in start_server() **TypeScript (Fastify):** - Create new cli.ts entry point - Add --mode argument parsing - Use execSync to run Prisma migrations - Separate functions for migrate-only and server modes **C# (.NET):** - Update Program.cs with mode argument parsing - Add RunMigrationsOnly() helper method - Run EF Core migrations conditionally based on mode - Use Database.Migrate() for migration execution **Documentation:** - Add comprehensive OPERATION_MODES.md documentation - Include usage examples for all 6 implementations - Document production deployment patterns (init containers, jobs, CI/CD) - Add troubleshooting guide and environment variables reference This brings all implementations to feature parity for production deployment scenarios where migrations need to be managed separately from application startup.
| """ | ||
|
|
||
| import argparse | ||
| import sys |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[black and ruff] reported by reviewdog 🐶
| import sys |
| import argparse | ||
| import sys | ||
| import logging | ||
| from pathlib import Path |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[black and ruff] reported by reviewdog 🐶
| from pathlib import Path | |
| import sys | |
| from pathlib import Path |
| from pathlib import Path | ||
|
|
||
| import uvicorn | ||
| from alembic import command |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[black and ruff] reported by reviewdog 🐶
| from alembic import command | |
| from alembic import command |
| import uvicorn | ||
| from alembic import command | ||
| from alembic.config import Config | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[black and ruff] reported by reviewdog 🐶
No description provided.