Skip to content

feat: add nomadRebase task for fast dev database cloning#7

Merged
NovaMage merged 5 commits into
mainfrom
6-nomad-rebase
May 15, 2026
Merged

feat: add nomadRebase task for fast dev database cloning#7
NovaMage merged 5 commits into
mainfrom
6-nomad-rebase

Conversation

@NovaMage
Copy link
Copy Markdown
Member

Summary

  • New nomadRebase sbt task that drops the working database and re-clones it from a long-lived rebase template database via Postgres CREATE DATABASE ... WITH TEMPLATE, then runs any pending migrations on top.
  • New overridable def rebaseDatasource: Option[DataSource] = None on NomadMigrations to opt in.
  • New core Rebaser class encapsulates the destructive Postgres-only orchestration (same-server / single-host / target-exists / target≠rebase guards, session termination on both DBs, DROP + CREATE FROM TEMPLATE). Migrator is untouched (stays schema-scoped).
  • Confirmation prompt is mandatory — no bypass flag. The task is a developer convenience, not for CI.

Closes #6.

Test plan

  • core/compile + sbtPlugin/compile clean
  • clean; test; doc clean (no scaladoc warnings from new code)
  • All 23 scripted tests pass, including the new nomad/rebase-postgres
  • New scripted test exercises: happy-path rebase + migrate (seeded row + M001 history clone into target, then M002 applied on top), H2 rejection, target==rebase rejection, different-servers rejection (two embedded-pg instances)
  • Manual end-to-end of sbt nomadRebase against a real developer Postgres cluster (the existing scripted test exercises Rebaser directly via run, matching the clean-and-migrate-postgres pattern — the sbt task wiring itself was not driven by the test)

🤖 Generated with Claude Code

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>
Copilot AI review requested due to automatic review settings May 15, 2026 18:06
Copy link
Copy Markdown

Copilot AI left a 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 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 nomadRebase sbt task wiring that prompts for confirmation, invokes core Rebaser, then runs Migrator.migrate().
  • Introduce core Rebaser to enforce Postgres/same-server invariants, terminate sessions, and orchestrate DROP + CREATE DATABASE FROM TEMPLATE.
  • Add NomadMigrations.rebaseDatasource opt-in hook, plus docs and a new scripted test exercising Rebaser with 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.

Comment thread core/src/main/scala/nomad/Rebaser.scala Outdated
Comment thread core/src/main/scala/nomad/Rebaser.scala Outdated
Comment thread core/src/main/scala/nomad/Rebaser.scala Outdated
Comment thread sbt-plugin/src/main/scala/nomad/sbt/NomadPlugin.scala
Comment thread core/src/main/scala/nomad/Rebaser.scala
NovaMage and others added 4 commits May 15, 2026 15:16
…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>
@NovaMage NovaMage merged commit fb84541 into main May 15, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add nomadRebase task for fast database cloning from a rebase source

2 participants