Help us shape the future of Go! Take the 2026 Go Developer Survey

opsql

command module
v0.0.24 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 15, 2026 License: MIT Imports: 1 Imported by: 0

README

opsql - Operational SQL Automation Tool

opsql is a CLI tool that helps manage operational SQL operations with YAML definitions. It provides dry-run capabilities, assertion validation, and integration with GitHub and Slack for safe database operations.

Features

  • Plan Mode (Dry-run): Execute SQL operations without permanent changes
  • Apply Mode: Execute SQL operations with actual database changes
  • YAML-based Configuration: Define operations in structured, reviewable format
  • Assertion Validation: Validate results against expected values
  • GitHub Integration: Automatic PR comments with execution results
  • Slack Notifications: Rich block-based notifications
  • Template Support: Use parameters in SQL with Go text/template
  • Multi-database Support: PostgreSQL and MySQL compatible

Installation

Using Go Install
go install github.com/pyama86/opsql@latest
From Source
git clone https://github.com/pyama86/opsql.git
cd opsql
go build -o opsql main.go

Quick Start

1. Create a YAML Configuration

Simple Format (Recommended):

version: 1
params:
  target_user_ids: "1,2,3"
operations:
  - sql: |
      SELECT id, email, status
      FROM users
      WHERE id IN ({{ .params.target_user_ids }})
      ORDER BY id
    expected:
      - id: 1
        email: "user1@example.com"
        status: "active"
      - id: 2
        email: "user2@example.com"
        status: "active"

  - sql: |
      UPDATE users
      SET status = 'inactive', updated_at = NOW()
      WHERE id IN ({{ .params.target_user_ids }})
        AND status = 'active'
    expected_changes:
      update: 2

Full Format (Legacy):

version: 1
params:
  target_user_ids: "1,2,3"
operations:
  - id: check_target_users
    description: "Check specific users before processing"
    type: select
    sql: |
      SELECT id, email, status
      FROM users
      WHERE id IN ({{ .params.target_user_ids }})
      ORDER BY id
    expected:
      - id: 1
        email: "user1@example.com"
        status: "active"

  - id: update_users_by_id_list
    description: "Update specific users to inactive status"
    type: update
    sql: |
      UPDATE users
      SET status = 'inactive', updated_at = NOW()
      WHERE id IN ({{ .params.target_user_ids }})
        AND status = 'active'
    expected_changes:
      update: 2
2. Set Environment Variables

You can set environment variables in two ways:

Option A: Using .env file (recommended)

# Copy the example file and edit it
cp .env.example .env
# Edit .env file with your actual values

Option B: Export environment variables

export DATABASE_DSN="postgres://user:password@localhost:5432/dbname"
# Optional: for GitHub integration
export GITHUB_TOKEN="ghp_xxxxxxxxxxxx"
# Optional: for Slack notifications
export SLACK_WEBHOOK_URL="https://hooks.slack.com/services/xxx/yyy/zzz"
3. Run Plan (Dry-run)
# Single configuration file
opsql run --config operations.yaml --dry-run

# Multiple configuration files (merged in order)
opsql run --config base.yaml --config additional.yaml --dry-run
4. Apply Changes
# Single configuration file
opsql run --config operations.yaml

# Multiple configuration files
opsql run --config base.yaml --config additional.yaml

Command Reference

run

Execute SQL operations with or without database changes.

opsql run [flags]

Flags:

  • -c, --config strings: YAML configuration file paths (required, can specify multiple)
  • -d, --dry-run: Execute in dry-run mode without making permanent changes
  • -e, --environment string: Environment name (e.g., dev, staging, prod)
  • --github-repo string: GitHub repository (owner/repo)
  • --github-pr int: GitHub PR number
  • --slack-webhook string: Slack webhook URL
  • --github-repo string: GitHub repository (owner/repo)
  • --github-pr int: GitHub PR number
  • --slack-webhook string: Slack webhook URL

Examples:

# Basic execution with single config
opsql run --config operations.yaml --dry-run

# Multiple configuration files
opsql run --config base.yaml --config env-specific.yaml --dry-run

# With GitHub PR integration
opsql run --config operations.yaml --dry-run --github-repo myorg/myrepo --github-pr 123

# With Slack notification
opsql run --config operations.yaml --dry-run --slack-webhook https://hooks.slack.com/services/xxx

Examples:

# Apply changes with single config
opsql run --config operations.yaml

# Apply changes with multiple configs
opsql run --config base.yaml --config env-specific.yaml

Multiple Configuration Files

opsql supports loading multiple configuration files that are merged together. This is useful for:

  • Separating base operations from environment-specific operations
  • Organizing operations by feature or module
  • Sharing common parameters across different operation sets
Usage
# Multiple files are merged in the order specified
opsql run --config base.yaml --config env-specific.yaml --config feature.yaml
Merging Behavior

When multiple configuration files are specified:

  1. Version: All files must have the same version number
  2. Parameters: Later files override parameters from earlier files
  3. Operations: Operations are appended in order from all files
  4. Operation IDs: Must be unique across all files (duplicates cause errors)
Example

base.yaml:

version: 1
params:
  database_name: "myapp"
  environment: "dev"
operations:
  - id: check_users
    sql: "SELECT COUNT(*) as cnt FROM users"
    expected:
      - cnt: 100

production.yaml:

version: 1
params:
  environment: "prod"  # Overrides base.yaml
operations:
  - id: prod_cleanup
    sql: "DELETE FROM logs WHERE created_at < NOW() - INTERVAL '30 days'"
    expected_changes:
      delete: 1000

The merged result will have environment: "prod" and both operations.

YAML Configuration Reference

Structure

Simple Format (Recommended):

version: 1 # Configuration version (required)
params: # Template parameters (optional)
  key: "value"
operations: # List of operations (required)
  - sql: | # SQL statement (required)
      SELECT * FROM table
    expected: # For SELECT operations (required for SELECT)
      - column: value
    expected_changes: # For DML operations (required for DML)
      insert|update|delete: count

Full Format (Legacy):

version: 1 # Configuration version (required)
params: # Template parameters (optional)
  key: "value"
operations: # List of operations (required)
  - id: "operation_id" # Unique identifier (optional)
    description: "desc" # Human-readable description (optional)
    type: "select|insert|update|delete" # Operation type (optional, auto-detected)
    sql: | # SQL statement (required)
      SELECT * FROM table
    expected: # For SELECT operations (required for SELECT)
      - column: value
    expected_changes: # For DML operations (required for DML)
      insert|update|delete: count
Auto-Detection Features
  • Operation Type: Automatically detected from SQL keywords (SELECT, INSERT, UPDATE, DELETE)
  • Operation ID: Auto-generated as operation_N if not specified
  • Description: Optional field for documentation purposes
Operation Types
SELECT Operations

Simple Format:

- sql: "SELECT id, email FROM users WHERE status = 'active'"
  expected:
    - id: 1
      email: "user1@example.com"
    - id: 2
      email: "user2@example.com"

Full Format:

- id: get_users
  description: "Get active users"
  type: select
  sql: "SELECT id, email FROM users WHERE status = 'active'"
  expected:
    - id: 1
      email: "user1@example.com"
    - id: 2
      email: "user2@example.com"
INSERT Operations

Simple Format:

- sql: "INSERT INTO logs (message, created_at) VALUES ('test', NOW())"
  expected_changes:
    insert: 1

Full Format:

- id: create_log
  description: "Create audit log"
  type: insert
  sql: "INSERT INTO logs (message, created_at) VALUES ('test', NOW())"
  expected_changes:
    insert: 1
UPDATE Operations

Simple Format:

- sql: "UPDATE users SET status = 'inactive' WHERE id IN (1,2,3)"
  expected_changes:
    update: 3

Full Format:

- id: update_status
  description: "Update user status"
  type: update
  sql: "UPDATE users SET status = 'inactive' WHERE id IN (1,2,3)"
  expected_changes:
    update: 3
DELETE Operations

Simple Format:

- sql: "DELETE FROM logs WHERE created_at < '2025-01-01'"
  expected_changes:
    delete: 100

Full Format:

- id: cleanup_logs
  description: "Delete old logs"
  type: delete
  sql: "DELETE FROM logs WHERE created_at < '2025-01-01'"
  expected_changes:
    delete: 100
Template Parameters

Use Go text/template syntax to substitute parameters:

params:
  cutoff_date: "2025-01-01"
  user_ids: "1,2,3,4,5"
operations:
  - id: example
    type: select
    sql: |
      SELECT * FROM users
      WHERE created_at >= '{{ .params.cutoff_date }}'
        AND id IN ({{ .params.user_ids }})

Environment Variables

.env File Support

opsql automatically loads environment variables from a .env file in the current directory if it exists. This is the recommended way to manage your configuration.

# Create your .env file from the example
cp .env.example .env
# Edit .env with your actual values

Note: The .env file is ignored by git to prevent accidental commits of sensitive information.

Required
  • DATABASE_DSN: Database connection string
    • PostgreSQL: postgres://user:password@host:port/dbname
    • MySQL: mysql://user:password@tcp(host:port)/dbname
Optional

Query Timeout:

  • OPSQL_QUERY_TIMEOUT: クエリの最大実行時間(秒単位)。デフォルトは30秒。MySQLではmax_execution_time、PostgreSQLではstatement_timeoutとして設定されます。

GitHub Integration (choose one):

Option A: Personal Access Token

  • GITHUB_TOKEN: GitHub personal access token for PR comments

Option B: GitHub App (recommended)

  • GITHUB_APP_ID or GITHUB_APP_CLIENT_ID: GitHub App ID
  • GITHUB_APP_INSTALLATION_ID: GitHub App Installation ID
  • GITHUB_APP_PRIVATE_KEY_PATH: Path to GitHub App private key file
  • GITHUB_APP_PRIVATE_KEY: GitHub App private key content (alternative to file path)

GitHub Actions (auto-detected):

  • GITHUB_REPOSITORY: GitHub repository (owner/repo) - auto-detected in GitHub Actions
  • GITHUB_REF: GitHub reference - auto-detected in GitHub Actions

Slack Integration:

  • SLACK_WEBHOOK_URL: Slack incoming webhook URL for notifications

GitHub Actions Integration

Example Workflow
name: Database Operations
on:
  pull_request:
    paths: ["db/operations/*.yaml"]

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

      - name: Setup Go
        uses: actions/setup-go@v4
        with:
          go-version: "1.21"

      - name: Install opsql
        run: go install github.com/pyama86/opsql@latest

      - name: Run opsql plan
        env:
          DATABASE_DSN: ${{ secrets.DATABASE_DSN }}
          # Option A: Personal Access Token
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          # Option B: GitHub App (recommended)
          GITHUB_APP_ID: ${{ secrets.GITHUB_APP_ID }}
          # GITHUB_APP_CLIENT_ID: ${{ secrets.GITHUB_APP_CLIENT_ID }}  # Alternative to GITHUB_APP_ID
          GITHUB_APP_INSTALLATION_ID: ${{ secrets.GITHUB_APP_INSTALLATION_ID }}
          GITHUB_APP_PRIVATE_KEY: ${{ secrets.GITHUB_APP_PRIVATE_KEY }}
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
        run: |
          opsql run \
            --config db/operations/maintenance.yaml \
            --dry-run \
            --github-repo ${{ github.repository }} \
            --github-pr ${{ github.event.number }}

  apply:
    runs-on: ubuntu-latest
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4

      - name: Setup Go
        uses: actions/setup-go@v4
        with:
          go-version: "1.21"

      - name: Install opsql
        run: go install github.com/pyama86/opsql@latest

      - name: Run opsql apply
        env:
          DATABASE_DSN: ${{ secrets.DATABASE_DSN }}
        run: opsql run --config db/operations/maintenance.yaml

Common Use Cases

Bulk Operations with IN Clauses

Simple Format:

params:
  target_user_ids: "1,2,3,4,5"
operations:
  - sql: |
      UPDATE users
      SET status = 'inactive'
      WHERE id IN ({{ .params.target_user_ids }})
    expected_changes:
      update: 5
Data Validation Before Changes

Simple Format:

operations:
  - sql: "SELECT COUNT(*) as cnt FROM users WHERE status = 'pending'"
    expected:
      - cnt: 10

  - sql: "UPDATE users SET status = 'active' WHERE status = 'pending'"
    expected_changes:
      update: 10
Cleanup Operations

Simple Format:

operations:
  - sql: |
      DELETE FROM user_sessions
      WHERE user_id NOT IN (SELECT id FROM users)
        AND created_at < NOW() - INTERVAL '30 days'
    expected_changes:
      delete: 150

Troubleshooting

Common Issues

Q: "DATABASE_DSN environment variable is required" error A: Set the DATABASE_DSN environment variable with your database connection string.

Q: "connection refused" error A: Check your database connection settings:

  • Verify the host and port are correct
  • Ensure the database is running
  • Check network connectivity
  • Verify credentials

Q: Assertion failures A: Review your expected values:

  • For SELECT: Check that expected rows match actual results exactly
  • For DML: Verify expected_changes counts match affected rows

Q: GitHub comment not posted A: Ensure:

  • GITHUB_TOKEN environment variable is set
  • Token has appropriate permissions
  • Repository and PR number are correct

Q: Slack notification not sent A: Verify:

  • SLACK_WEBHOOK_URL is correctly set
  • Webhook URL is valid and active
  • Network connectivity to Slack
Debug Mode

For detailed logging, you can examine the JSON output from plan/apply commands:

opsql plan --config operations.yaml | jq '.'

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests for new functionality
  5. Run tests: go test ./...
  6. Submit a pull request

License

MIT License - see LICENSE file for details.

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
cmd
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL