DEV Community

Cover image for How to Automate Database Releases Across MSSQL, PostgreSQL, and Oracle with a Single CLI
D-Band
D-Band

Posted on

How to Automate Database Releases Across MSSQL, PostgreSQL, and Oracle with a Single CLI

How to Automate Database Releases Across MSSQL, PostgreSQL, and Oracle with a Single CLI

Managing database releases across multiple platforms is one of the harder parts of a DevOps pipeline. Application code gets clean CI/CD workflows, but databases often end up with a folder of SQL scripts, a shared checklist, and someone holding their breath on release night.

This guide walks through how to use drm-cli — an open-source Data Release Manager — to automate and standardize database releases across MSSQL, PostgreSQL, and Oracle from a single CLI.


What drm-cli does

  • Versioned release management backed by a local SQLite or JSON database
  • Dry-run mode — generates deployment scripts without executing them so you can review exactly what will happen before anything touches your database
  • MSSQL, PostgreSQL, and Oracle support from one tool
  • Parallel deployments across multiple target databases
  • AES-encrypted credential storage
  • CI/CD integration (GitHub Actions, Azure DevOps)
  • AI layer: MCP server + natural-language agent for querying release state

Requirements

  • Node.js ≥ 16
  • Python ≥ 3.10

Installation

npm install -g @d-band-drm/drm-cli
drm-cli install -f /opt/drm -d sqlite -p myencryptionkey
Enter fullscreen mode Exit fullscreen mode
Flag Description
-f Target install path
-d Backend: sqlite or json
-p Encryption key (omit for unencrypted)
--trace Enable debug logging

This installs DRM into /opt/drm and saves the path to ~/.drm-cli.json so subsequent commands find it automatically.


How DRM is configured

DRM stores its configuration — releases, connections, solutions, and projects — in a local database (SQLite or JSON) that lives in your install path. You define your releases and connections by editing this database directly.

The configuration follows this structure:

Release
  └── Solution (the deployment type: MSSQL / Liquibase / Flyway)
        ├── Connections (named environments: dev, test, prod)
        └── Projects (target databases to deploy to)
Enter fullscreen mode Exit fullscreen mode

Here is what a minimal MSSQL release looks like in JSON format:

{
  "id": 1,
  "name": "Release 1.0 — add orders table",
  "is_active": true,
  "solutions": [
    {
      "name": "Orders schema solution",
      "solution_type_id": 1,
      "path": "../mssql/orders",
      "connections": [
        {
          "name": "dev",
          "connection_type_id": 1,
          "connection_string": "Server=127.0.0.1;User Id=drm;Password=drm;Encrypt=False;"
        },
        {
          "name": "prod",
          "connection_type_id": 1,
          "connection_string": "Server=prod-db.internal;User Id=drm;Password=secret;Encrypt=True;"
        }
      ],
      "projects": [
        {
          "name": "OrdersDB",
          "targets_compare_db": "OrdersDB",
          "targets_type_id": 1,
          "targets_list": "[\"OrdersDB\"]",
          "fail_on_error": true
        }
      ]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Solution types:
| solution_type_id | Type |
|---|---|
| 1 | Microsoft SQL Server (DACPAC) |
| 2 | Liquibase |
| 3 | Flyway |

Connection types:
| connection_type_id | Type |
|---|---|
| 1 | MSSQL |
| 2 | Oracle |
| 3 | PostgreSQL |

For PostgreSQL with Liquibase, the connection string uses JDBC format:

{
  "name": "dev",
  "connection_type_id": 3,
  "connection_string": "url=jdbc:postgresql://127.0.0.1:5432/mydb;username=drm;password=drm;"
}
Enter fullscreen mode Exit fullscreen mode

For Oracle with Flyway:

{
  "name": "dev",
  "connection_type_id": 2,
  "connection_string": "url=jdbc:oracle:thin:@//127.0.0.1:1521/FREEPDB1;username=drm;password=drm;"
}
Enter fullscreen mode Exit fullscreen mode

If you are using encryption (-p during install), encrypt your connection string passwords first:

drm-cli crypto --encrypt -t "mypassword" -p myencryptionkey
Enter fullscreen mode Exit fullscreen mode

Then use the encrypted value in the connection string.


Step 1: Dry-run before you deploy

Once your release is configured, generate and review the deployment plan without touching any database:

drm-cli deploy -c dev -r 1 --dryrun
Enter fullscreen mode Exit fullscreen mode

DRM generates the full execution plan — every SQL statement, in order — and outputs it for review. You see exactly what will run, against which connection, before anything executes.

Dry-run is especially valuable when:

  • Production has data that staging does not
  • You want a second pair of eyes on the output before approving
  • You need a deployment artifact for change management sign-off

Step 2: Deploy

Once you have reviewed the dry-run output:

drm-cli deploy -c prod -r 1 --deploy
Enter fullscreen mode Exit fullscreen mode

DRM executes the release scripts in order, logs every action, and records the deployment in its history.


Step 3: Align an environment

When environments drift — a hotfix applied directly, or a staging environment that has fallen behind — the --align flag brings a database up to a specific release state:

drm-cli deploy -c staging -r 1 --align
Enter fullscreen mode Exit fullscreen mode

Deploying to multiple databases in parallel

DRM supports parallel deployments across multiple target databases within a single project. To deploy release 12 to four databases at once with a concurrency limit of 2:

{
  "name": "MultiDB project",
  "targets_type_id": 1,
  "targets_list": "[\"DB_A\", \"DB_B\", \"DB_C\", \"DB_D\"]",
  "max_degree_in_parallel": 2,
  "timeout_in_min": 10,
  "fail_on_error": false
}
Enter fullscreen mode Exit fullscreen mode

You can also define priority order (targets_priority) and exclusions (targets_exclude), or use a SQL query to dynamically resolve the target list (targets_type_id: 2).


CI/CD integration

GitHub Actions

name: Deploy Database Release

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'

      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Install drm-cli
        run: npm install -g @d-band-drm/drm-cli

      - name: Install DRM
        run: drm-cli install -f /opt/drm -d sqlite -p ${{ secrets.DRM_KEY }}

      - name: Dry run
        run: drm-cli deploy -c ${{ vars.DRM_CONNECTION }} -r ${{ vars.RELEASE_ID }} --dryrun

      - name: Deploy
        run: drm-cli deploy -c ${{ vars.DRM_CONNECTION }} -r ${{ vars.RELEASE_ID }} --deploy
Enter fullscreen mode Exit fullscreen mode

Store your encryption key as a secret. Connection names and release IDs can be pipeline variables.


AI integration (v1.2.1+)

DRM ships with an MCP server and a natural-language agent:

# Start the MCP server (integrates with Claude Desktop / Claude Code)
DRM_SECRET=myencryptionkey npm run mcp

# Natural language agent (requires ANTHROPIC_API_KEY)
ANTHROPIC_API_KEY=sk-ant-... DRM_SECRET=myencryptionkey node ai/agent/agent.js "list all releases"
ANTHROPIC_API_KEY=sk-ant-... DRM_SECRET=myencryptionkey node ai/agent/agent.js "run dryrun for connection dev release 1"
Enter fullscreen mode Exit fullscreen mode

With the MCP server running inside Claude Code, you can use built-in slash commands:

Skill What it does
/drm-status Installation health + last 5 deployments
/drm-release [id] List releases or detail one
/drm-plan <conn> <rel> Dryrun + structured plan
/drm-deploy <conn> <rel> Guided: dryrun → confirm → deploy

Command reference

Command What it does
drm-cli install -f <path> -d sqlite -p <key> Install DRM
drm-cli deploy -c <conn> -r <id> --dryrun Preview deployment
drm-cli deploy -c <conn> -r <id> --deploy Execute release
drm-cli deploy -c <conn> -r <id> --align Align environment to release
drm-cli crypto --encrypt -t <text> -p <key> Encrypt a value
drm-cli crypto --changepassword -p <old> -n <new> Rotate encryption key
drm-cli uninstall Remove DRM

Resources

Top comments (0)