feat: add nomadRebase task for fast dev database cloning#7
Merged
Conversation
Closes #6. Introduces a Postgres-only Rebaser that drops the working database and re-clones it from a long-lived rebase template database via CREATE DATABASE ... WITH TEMPLATE, then runs any pending migrations. Designed for devs who maintain a rebase DB at a known-good point in time (often a restored production backup) to skip the multi-minute restore loop after local data corruption. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR adds a developer-focused “rebase” workflow to Nomad: a new nomadRebase sbt task that (on Postgres only) drops the target database, recreates it quickly using CREATE DATABASE … WITH TEMPLATE from a long-lived rebase/template DB, and then runs pending migrations.
Changes:
- Add
nomadRebasesbt task wiring that prompts for confirmation, invokes coreRebaser, then runsMigrator.migrate(). - Introduce core
Rebaserto enforce Postgres/same-server invariants, terminate sessions, and orchestrate DROP + CREATE DATABASE FROM TEMPLATE. - Add
NomadMigrations.rebaseDatasourceopt-in hook, plus docs and a new scripted test exercisingRebaserwith embedded Postgres.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| wiki/Table-of-Contents.md | Adds “Rebase” to the wiki navigation. |
| wiki/sbt-reference.md | Documents the new nomadRebase sbt task and links to the Rebase doc. |
| wiki/Rebase.md | New documentation page describing configuration, constraints, and behavior of rebase. |
| wiki/Clean-and-migrate.md | Updates wiki navigation to include the new Rebase page. |
| sbt-plugin/src/sbt-test/nomad/rebase-postgres/test | New scripted test entrypoint invoking the test app. |
| sbt-plugin/src/sbt-test/nomad/rebase-postgres/src/main/scala/Main.scala | New embedded-Postgres-based test app for Rebaser behavior and invariants. |
| sbt-plugin/src/sbt-test/nomad/rebase-postgres/src/main/resources/migrations/M001_CreateUsers.sql | Adds SQL fixture migration for the test app. |
| sbt-plugin/src/sbt-test/nomad/rebase-postgres/src/main/resources/migrations/M002_CreateProducts.sql | Adds SQL fixture migration for the test app. |
| sbt-plugin/src/sbt-test/nomad/rebase-postgres/project/plugins.sbt | Scripted test plugin wiring. |
| sbt-plugin/src/sbt-test/nomad/rebase-postgres/build.sbt | Scripted test build definition + dependencies. |
| sbt-plugin/src/main/scala/nomad/sbt/NomadPlugin.scala | Adds the nomadRebase task key and task implementation. |
| core/src/main/scala/nomad/Rebaser.scala | New core implementation for Postgres template-clone rebase orchestration. |
| core/src/main/scala/nomad/NomadMigrations.scala | Adds rebaseDatasource: Option[DataSource] to opt into rebase workflow. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
NovaMage
commented
May 15, 2026
NovaMage
commented
May 15, 2026
…ommit handling, target-side pool gate Five reviewer-flagged issues from PR #7: 1. readDatasourceMetadata's SQLException wrapper always blamed the target database, even when the rebase datasource failed to connect. Introduce a private Role enum and branch the hint by role. Regression test: Test 5 (broken rebase datasource). 2. terminateOtherSessions counted ResultSet rows instead of the boolean returned by pg_terminate_backend, so the "Terminated N sessions" log over-reported when the PID exited between the SELECT and the signal. Read the boolean, count successes, warn separately on failures. No regression test — the race path is not deterministically scriptable. 3. dropDatabaseIfExists logged "Dropped database" unconditionally despite the IF EXISTS clause. Pre-check pg_database, branch the log; keep IF EXISTS in the SQL for the brief race window. 4. Pool race on the target side between terminate and drop/create: the user's app pool can refill connections to target during our critical window. Guard with ALTER DATABASE target ALLOW_CONNECTIONS false (extracted helper withAllowConnectionsDisabled, restored in finally). The same trick cannot be applied to rebase — Postgres rejects ALLOW_CONNECTIONS=false on the session's own current database — and conceptually rebase is a passive long-lived template so the pool-refill race doesn't realistically arise on that side; documented in code. 5. DROP DATABASE and CREATE DATABASE ... WITH TEMPLATE cannot run inside a transaction, but DataSource#getConnection does not guarantee autoCommit=true (ORM-style pools often default to false). Save the borrowed autoCommit state, force true for the admin work, restore in finally so the connection returns to the pool in the state it was borrowed in. Regression test: Test 6 (DataSource proxy forcing autoCommit=false, plus a Connection wrapper capturing autoCommit-at-close to assert restoration). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
novamage@magaran.com holds the bypass repo role on nomad (mirrors the nixos-workstation Owner Shortcut described in the org AGENTS.md), so agents can run gh pr merge --admin --merge / git push directly after the signed-commit ownership check without going through the temporary-pr-overlord team dance. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
nomadRebasesbt task that drops the working database and re-clones it from a long-lived rebase template database via PostgresCREATE DATABASE ... WITH TEMPLATE, then runs any pending migrations on top.def rebaseDatasource: Option[DataSource] = NoneonNomadMigrationsto opt in.Rebaserclass encapsulates the destructive Postgres-only orchestration (same-server / single-host / target-exists / target≠rebase guards, session termination on both DBs, DROP + CREATE FROM TEMPLATE).Migratoris untouched (stays schema-scoped).Closes #6.
Test plan
core/compile+sbtPlugin/compilecleanclean; test; docclean (no scaladoc warnings from new code)nomad/rebase-postgressbt nomadRebaseagainst a real developer Postgres cluster (the existing scripted test exercisesRebaserdirectly viarun, matching theclean-and-migrate-postgrespattern — the sbt task wiring itself was not driven by the test)🤖 Generated with Claude Code