- Updated 01-02-PLAN.md Task 3 to NOT run migrations on startup - Updated must_haves truth: 'run via ZIO effect when invoked' (not 'on startup') - Updated verification/success_criteria to reflect manual migration - Updated ROADMAP success criteria #3 to match CONTEXT.md decision Per CONTEXT.md line 22-23: migrations run manually via CLI command
7.8 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | must_haves | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 01-foundation | 02 | execute | 2 |
|
|
true |
|
Purpose: Establish database connectivity with type-safe queries and version-controlled schema changes. Output: Working database layer with migrations service and a /ready endpoint that verifies connectivity.
<execution_context> @/home/jin/.claude/get-shit-done/workflows/execute-plan.md @/home/jin/.claude/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/phases/01-foundation/01-CONTEXT.md @.planning/phases/01-foundation/01-RESEARCH.md @.planning/phases/01-foundation/01-01-SUMMARY.md Task 1: Create Quill PostgreSQL context src/db/QuillContext.scala Create Quill context for PostgreSQL with ZIO integration.src/db/QuillContext.scala:
- Import io.getquill.* and io.getquill.jdbczio.Quill
- Create dataSourceLayer: ZLayer[Any, Throwable, javax.sql.DataSource]
- Use Quill.DataSource.fromPrefix("database") to read from HOCON config
- Create quillLayer: ZLayer[javax.sql.DataSource, Nothing, Quill.Postgres[SnakeCase]]
- Use Quill.Postgres.fromNamingStrategy(SnakeCase)
- Export combined layer: dataSourceLayer >>> quillLayer
IMPORTANT: Never use "io" as a variable name in this file (Quill package conflict).
Configuration note: Quill reads HikariCP config from the "database" prefix in application.conf. The existing database config structure should work, but may need adjustment to match HikariCP's expected format:
database {
dataSourceClassName = "org.postgresql.ds.PGSimpleDataSource"
dataSource {
serverName = "localhost"
portNumber = 5432
databaseName = "summercms"
user = "summercms"
password = "summercms"
}
}
Update resources/application.conf to use HikariCP-compatible format if needed.
mill compile succeeds with Quill context
QuillContext.scala exports dataSourceLayer and quillLayer, compiles without errors
- src/db/Migrator.scala:
- Case class MigrationStatus(current: Option[String], pending: Int)
- Trait Migrator with methods:
- def migrate: Task[Int] (returns count of migrations run)
- def status: Task[MigrationStatus]
- Object Migrator with:
- val live: ZLayer[javax.sql.DataSource, Nothing, Migrator]
- Implementation configures Flyway with:
- dataSource from layer
- locations: "classpath:db/migration"
- table: "summer_migrations" (not default flyway_schema_history)
- baselineOnMigrate: true (for existing databases)
- resources/db/migration/V1__create_summer_users.sql:
-- Summer CMS initial schema
-- Creates the base users table for future auth
CREATE TABLE summer_users (
id BIGSERIAL PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_summer_users_email ON summer_users(email);
-- Trigger for updated_at
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ language 'plpgsql';
CREATE TRIGGER update_summer_users_updated_at
BEFORE UPDATE ON summer_users
FOR EACH ROW
EXECUTE FUNCTION update_updated_at_column();
Naming follows conventions from CONTEXT.md:
- snake_case columns
- summer_ prefix for core tables
- id, created_at, updated_at by default
mill compilesucceeds, migration file exists at correct path Migrator service defined with migrate/status methods, V1 migration creates summer_users
Modify src/api/HealthRoutes.scala:
- Keep existing GET /health -> Response.text("ok") (always 200)
- Add GET /ready endpoint:
- Requires DataSource from ZLayer
- Attempts: ZIO.attempt(ds.getConnection.close())
- Success: Response.text("ready") with 200
- Failure: Response.status(Status.ServiceUnavailable) with 503
Pattern:
Method.GET / "ready" -> handler {
ZIO.serviceWithZIO[javax.sql.DataSource] { ds =>
ZIO.attempt {
val conn = ds.getConnection
conn.close()
}.as(Response.text("ready"))
.catchAll(_ => ZIO.succeed(Response.status(Status.ServiceUnavailable)))
}
}
Update Main.scala to:
- Provide dataSourceLayer to the server
- DO NOT run migrations on startup (per CONTEXT.md: migrations run manually via CLI)
- The Migrator service is available but not auto-invoked
Note: The /ready endpoint will return 503 if no database is running, which is correct behavior.
Migrations will be triggered via CLI command in a later phase (Phase 5: CLI Scaffolding).
With PostgreSQL running: curl localhost:8080/ready returns "ready"
Without PostgreSQL: curl localhost:8080/ready returns 503
/ready endpoint exists, returns 200 when DB connected, 503 when not
<success_criteria>
- Quill context exists with PostgreSQL configuration
- Flyway migrator service exists as ZIO effect (available for CLI invocation)
- V1 migration creates summer_users table with proper schema
- /ready endpoint verifies database connectivity
- Compile-time SQL validation works (intentional bad query causes compile error)
- Server starts WITHOUT auto-running migrations </success_criteria>