{"id":15814129,"url":"https://github.com/cchexcode/qop","last_synced_at":"2026-01-19T21:33:30.736Z","repository":{"id":248793935,"uuid":"829794772","full_name":"cchexcode/qop","owner":"cchexcode","description":"Database migrations for savages.","archived":false,"fork":false,"pushed_at":"2025-09-24T16:08:58.000Z","size":423,"stargazers_count":27,"open_issues_count":5,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-09-24T18:12:23.851Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cchexcode.png","metadata":{"files":{"readme":"docs/README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-07-17T03:03:43.000Z","updated_at":"2025-09-24T16:08:55.000Z","dependencies_parsed_at":"2024-10-26T09:48:44.160Z","dependency_job_id":"3d00c5ae-84de-450b-8098-892eb41a8344","html_url":"https://github.com/cchexcode/qop","commit_stats":null,"previous_names":["replicadse/qop","cchexcode/qop"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/cchexcode/qop","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cchexcode%2Fqop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cchexcode%2Fqop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cchexcode%2Fqop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cchexcode%2Fqop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cchexcode","download_url":"https://codeload.github.com/cchexcode/qop/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cchexcode%2Fqop/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28585515,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-19T20:45:59.482Z","status":"ssl_error","status_checked_at":"2026-01-19T20:45:41.500Z","response_time":67,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-05T04:22:46.507Z","updated_at":"2026-01-19T21:33:30.730Z","avatar_url":"https://github.com/cchexcode.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# qop - A simple database migration tool\n\n`qop` is a command-line tool for managing database migrations for PostgreSQL and SQLite. It's designed to be simple, straightforward, and easy to use. The software respects semantic versioning and will only introduce breaking changes in new `major` versions once passing the `1.0.0` version. While being in-development, breaking changes CAN occur in new `minor` versions.\n\n## Features\n\n*   Backend-agnostic design (supports PostgreSQL and SQLite)\n*   Simple migration file format (`up.sql`, `down.sql`, `meta.toml`)\n*   Migration metadata support (comments, locking status)\n*   Migration locking system to prevent accidental reverts\n*   Timestamp-based migration IDs\n*   Command-line interface for managing migrations\n*   Comprehensive audit logging of all migration operations\n*   No interactive UI; all confirmations happen via CLI prompts or can be bypassed with `--yes`\n\n## Installation\n\n```bash\ncargo install qop\n# or\ncargo install --path .\n```\n\n## Migrations\n\nPlease find more information about migration from one version to another in the dedicated [release notes](https://github.com/cchexcode/qop/blob/master/docs/releases/).\n\n## Build features\n\n`qop` is built with Cargo feature flags to include only the subsystems you need. SQLite support is enabled by default.\n\n- Default features\n  - Enabled: `sub+sqlite`\n  - Disabled: `sub+postgres` (optional)\n\n- Enable PostgreSQL (keeping default SQLite):\n\n```bash\ncargo build --features \"sub+postgres\"\n```\n\n- PostgreSQL only (no SQLite):\n\n```bash\ncargo build --no-default-features --features \"sub+postgres\"\n```\n\n- SQLite only (default):\n\n```bash\ncargo build            # or: cargo build --features \"sub+sqlite\"\n```\n\n- No subsystems (not allowed):\n\n```bash\ncargo build --no-default-features   # Fails at compile time with a clear error\n```\n\nNotes:\n- Enabling a subsystem feature also enables only the matching `sqlx` backend internally, keeping binaries small.\n- Runtime uses Tokio and Rustls TLS by default. No `sqlx` macros are required.\n\n## Getting Started\n\n1.  **Create a migrations directory and config file:**\n    - Create a directory to hold your migrations (for example, `migrations/`). Place your `qop.toml` inside this directory. The tool expects migration folders (like `id=.../`) to live alongside `qop.toml`.\n    - Generate a sample config for your database:\n      - PostgreSQL:\n        ```bash\n        qop subsystem postgres config init -p migrations/qop.toml -c \"postgresql://postgres:password@localhost:5432/postgres\"\n        ```\n      - SQLite:\n        ```bash\n        qop subsystem sqlite config init -p migrations/qop.toml -d ./app.db\n        ```\n\n2.  **Initialize the migration table:**\n    ```bash\n    qop subsystem postgres init -p migrations/qop.toml\n    qop subsystem sqlite   init -p migrations/qop.toml\n    ```\n\n3.  **Create your first migration:**\n    ```bash\n    qop subsystem postgres new -p migrations/qop.toml    # For PostgreSQL\n    qop subsystem sqlite   new -p migrations/qop.toml    # For SQLite\n    ```\n    This will create a new directory with `up.sql` and `down.sql` files.\n\n4.  **Apply the migration:**\n    ```bash\n    qop subsystem postgres up -p migrations/qop.toml     # For PostgreSQL\n    qop subsystem sqlite   up -p migrations/qop.toml     # For SQLite\n    ```\n\n## Configuration\n\n`qop` is configured using a `qop.toml` file. Here are examples for both supported backends:\n\n### PostgreSQL Configuration\n\n```toml\nversion = \"\u003e=0.1.0\"\n\n[subsystem.postgres]\nconnection = { static = \"postgresql://postgres:password@localhost:5432/postgres\" }\nschema = \"public\"\ntable_prefix = \"__qop\"\ntimeout = 30\n```\n\nYou can also use environment variables for the connection string:\n\n```toml\nversion = \"\u003e=0.1.0\"\n\n[subsystem.postgres]\nconnection = { from_env = \"DATABASE_URL\" }\nschema = \"public\"\ntable_prefix = \"__qop\"\ntimeout = 30\n```\n\n### SQLite Configuration\n\n```toml\nversion = \"\u003e=0.1.0\"\n\n[subsystem.sqlite]\nconnection = { static = \"sqlite:///path/to/database.db\" }\ntable_prefix = \"__qop\"\ntimeout = 30\n```\n\nOr with environment variables:\n\n```toml\nversion = \"\u003e=0.1.0\"\n\n[subsystem.sqlite]\nconnection = { from_env = \"DATABASE_URL\" }\ntable_prefix = \"__qop\"\ntimeout = 30\n```\n\nThe migration files live in the same directory as the `qop.toml` file (e.g., `migrations/`). Each migration is a folder named `id=\u003ctimestamp\u003e/` containing `up.sql`, `down.sql`, and `meta.toml`.\n\n## Usage\n\n`qop` provides several commands to manage your database migrations through subsystems.\n\n### `subsystem`\n\nThe core command for managing database-specific operations. Available aliases: `sub`, `s`\n\n```bash\nqop subsystem \u003cDATABASE\u003e \u003cCOMMAND\u003e\n```\n\n#### PostgreSQL Commands\n\nAll PostgreSQL operations are accessed through the `postgres` (alias: `pg`) subsystem:\n\n##### `qop subsystem postgres init`\n\nInitializes the migration table in your PostgreSQL database.\n\n```bash\nqop subsystem postgres init --path path/to/your/qop.toml\n```\n\n##### `qop subsystem postgres new`\n\nCreates a new migration directory with `up.sql`, `down.sql`, and `meta.toml` files.\n\n```bash\nqop subsystem postgres new --path path/to/your/qop.toml\n```\n\n**Arguments:**\n*   `-p, --path \u003cPATH\u003e`: Path to the `qop.toml` configuration file. (default: `qop.toml`)\n*   `-c, --comment \u003cCOMMENT\u003e`: Custom comment for the migration\n*   `--lock`: Mark migration as locked (cannot be reverted without --unlock)\n\nThis will create a directory structure like:\n```\nmigrations/\n└── id=1678886400000/\n    ├── up.sql\n    ├── down.sql\n    └── meta.toml\n```\n\n##### `qop subsystem postgres up`\n\nApplies pending migrations. By default, it applies all pending migrations.\n\n```bash\nqop subsystem postgres up --path path/to/your/qop.toml\n```\n\n**Arguments:**\n*   `-p, --path \u003cPATH\u003e`: Path to the `qop.toml` configuration file. (default: `qop.toml`)\n*   `-c, --count \u003cCOUNT\u003e`: The number of migrations to apply. If not specified, all pending migrations are applied.\n*   `-t, --timeout \u003cTIMEOUT\u003e`: Statement timeout in seconds.\n*   `--dry`: Execute migration in a transaction but rollback instead of committing\n*   `-y, --yes`: Skip confirmation prompts and apply migrations automatically\n\n##### `qop subsystem postgres down`\n\nReverts applied migrations. By default, it reverts the last applied migration.\n\n```bash\nqop subsystem postgres down --path path/to/your/qop.toml\n```\n\n**Arguments:**\n*   `-p, --path \u003cPATH\u003e`: Path to the `qop.toml` configuration file. (default: `qop.toml`)\n*   `-c, --count \u003cCOUNT\u003e`: The number of migrations to revert. (default: 1)\n*   `-t, --timeout \u003cTIMEOUT\u003e`: Statement timeout in seconds.\n*   `-r, --remote`: Use the `down.sql` from the database instead of the local file.\n*   `--dry`: Execute migration in a transaction but rollback instead of committing\n*   `--unlock`: Allow reverting locked migrations\n*   `-y, --yes`: Skip confirmation prompts and revert migrations automatically\n\n##### `qop subsystem postgres list`\n\nLists all migrations, showing their status (applied or not) and when they were applied.\n\n```bash\nqop subsystem postgres list --path path/to/your/qop.toml\n```\n\n**Arguments:**\n*   `-o, --output \u003cFORMAT\u003e`: Output format (`human` or `json`). (default: `human`)\n\n##### `qop subsystem postgres history`\n\nManages migration history with commands for syncing and fixing migration order.\n\n###### `qop subsystem postgres history sync`\n\nUpserts all remote migrations locally. This is useful for syncing migrations across multiple developers.\n\n```bash\nqop subsystem postgres history sync --path path/to/your/qop.toml\n```\n\n###### `qop subsystem postgres history fix`\n\nShuffles all non-run local migrations to the end of the chain. This is useful when you have created migrations out of order.\n\n```bash\nqop subsystem postgres history fix --path path/to/your/qop.toml\n```\n\n##### `qop subsystem postgres diff`\n\nShows the raw SQL content of pending migrations without applying them.\n\n```bash\nqop subsystem postgres diff --path path/to/your/qop.toml\n```\n\nThis command outputs the exact SQL content for each pending migration using the same formatted preview as the interactive diff (with headers and separators).\n\n##### `qop subsystem postgres apply`\n\nApplies or reverts a specific migration by ID.\n\n###### `qop subsystem postgres apply up`\n\nApplies a specific migration.\n\n```bash\nqop subsystem postgres apply up \u003cID\u003e --path path/to/your/qop.toml\n```\n\n**Arguments:**\n*   `\u003cID\u003e`: Migration ID to apply (required)\n*   `-t, --timeout \u003cTIMEOUT\u003e`: Statement timeout in seconds.\n*   `--dry`: Execute migration in a transaction but rollback instead of committing\n*   `--lock`: Mark applied migration as locked (cannot be reverted without --unlock)\n*   `-y, --yes`: Skip confirmation prompts and apply migration automatically\n\n###### `qop subsystem postgres apply down`\n\nReverts a specific migration.\n\n```bash\nqop subsystem postgres apply down \u003cID\u003e --path path/to/your/qop.toml\n```\n\n**Arguments:**\n*   `\u003cID\u003e`: Migration ID to revert (required)\n*   `-t, --timeout \u003cTIMEOUT\u003e`: Statement timeout in seconds.\n*   `-r, --remote`: Use the `down.sql` from the database instead of the local file.\n*   `--dry`: Execute migration in a transaction but rollback instead of committing\n*   `--unlock`: Allow reverting locked migrations\n*   `-y, --yes`: Skip confirmation prompts and revert migration automatically\n\n#### SQLite Commands\n\nAll SQLite operations are accessed through the `sqlite` (alias: `sql`) subsystem and support the same commands as PostgreSQL:\n\n##### `qop subsystem sqlite init`\n\nInitializes the migration table in your SQLite database.\n\n```bash\nqop subsystem sqlite init --path path/to/your/qop.toml\n```\n\n##### `qop subsystem sqlite new`\n\nCreates a new migration directory with `up.sql`, `down.sql`, and `meta.toml` files.\n\n```bash\nqop subsystem sqlite new --path path/to/your/qop.toml\n```\n\n**Arguments:**\n*   `-p, --path \u003cPATH\u003e`: Path to the `qop.toml` configuration file. (default: `qop.toml`)\n*   `-c, --comment \u003cCOMMENT\u003e`: Custom comment for the migration\n*   `--lock`: Mark migration as locked (cannot be reverted without --unlock)\n\n##### `qop subsystem sqlite up`\n\nApplies pending migrations.\n\n```bash\nqop subsystem sqlite up --path path/to/your/qop.toml\n```\n\n**Arguments:**\n*   `-p, --path \u003cPATH\u003e`: Path to the `qop.toml` configuration file. (default: `qop.toml`)\n*   `-c, --count \u003cCOUNT\u003e`: The number of migrations to apply.\n*   `-t, --timeout \u003cTIMEOUT\u003e`: Statement timeout in seconds.\n*   `--dry`: Execute migration in a transaction but rollback instead of committing\n*   `-y, --yes`: Skip confirmation prompts and apply migrations automatically\n\n##### `qop subsystem sqlite down`\n\nReverts applied migrations.\n\n```bash\nqop subsystem sqlite down --path path/to/your/qop.toml\n```\n\n**Arguments:**\n*   `-p, --path \u003cPATH\u003e`: Path to the `qop.toml` configuration file. (default: `qop.toml`)\n*   `-c, --count \u003cCOUNT\u003e`: The number of migrations to revert.\n*   `-t, --timeout \u003cTIMEOUT\u003e`: Statement timeout in seconds.\n*   `-r, --remote`: Use the `down.sql` from the database instead of the local file.\n*   `--dry`: Execute migration in a transaction but rollback instead of committing\n*   `--unlock`: Allow reverting locked migrations\n*   `-y, --yes`: Skip confirmation prompts and revert migrations automatically\n\n##### `qop subsystem sqlite list`\n\nLists all migrations, showing their status and when they were applied.\n\n```bash\nqop subsystem sqlite list --path path/to/your/qop.toml\n```\n\n**Arguments:**\n*   `-o, --output \u003cFORMAT\u003e`: Output format (`human` or `json`). (default: `human`)\n\n##### `qop subsystem sqlite history sync`\n\nUpserts all remote migrations locally.\n\n```bash\nqop subsystem sqlite history sync --path path/to/your/qop.toml\n```\n\n##### `qop subsystem sqlite history fix`\n\nShuffles all non-run local migrations to the end of the chain.\n\n```bash\nqop subsystem sqlite history fix --path path/to/your/qop.toml\n```\n\n##### `qop subsystem sqlite diff`\n\nShows the raw SQL content of pending migrations without applying them.\n\n```bash\nqop subsystem sqlite diff --path path/to/your/qop.toml\n```\n\nThis command outputs the exact SQL content for each pending migration using the same formatted preview as the interactive diff (with headers and separators).\n\n##### `qop subsystem sqlite apply up`\n\nApplies a specific migration by ID.\n\n```bash\nqop subsystem sqlite apply up \u003cID\u003e --path path/to/your/qop.toml\n```\n\n**Arguments:**\n*   `\u003cID\u003e`: Migration ID to apply (required)\n*   `-t, --timeout \u003cTIMEOUT\u003e`: Statement timeout in seconds.\n*   `--dry`: Execute migration in a transaction but rollback instead of committing\n*   `--lock`: Mark applied migration as locked (cannot be reverted without --unlock)\n*   `-y, --yes`: Skip confirmation prompts and apply migration automatically\n\n##### `qop subsystem sqlite apply down`\n\nReverts a specific migration by ID.\n\n```bash\nqop subsystem sqlite apply down \u003cID\u003e --path path/to/your/qop.toml\n```\n\n**Arguments:**\n*   `\u003cID\u003e`: Migration ID to revert (required)\n*   `-t, --timeout \u003cTIMEOUT\u003e`: Statement timeout in seconds.\n*   `-r, --remote`: Use the `down.sql` from the database instead of the local file.\n*   `--dry`: Execute migration in a transaction but rollback instead of committing\n*   `--unlock`: Allow reverting locked migrations\n*   `-y, --yes`: Skip confirmation prompts and revert migration automatically\n\n### `man`\n\nRenders the manual.\n\n#### `qop man`\n\n```bash\nqop man --out docs/manual --format markdown\n```\n\n**Arguments:**\n*   `-o, --out \u003cPATH\u003e`: Path to write documentation to (required)\n*   `-f, --format \u003cFORMAT\u003e`: Format for the documentation. Can be `manpages` or `markdown` (required)\n\n### `autocomplete`\n\nRenders shell completion scripts.\n\n#### `qop autocomplete`\n\n```bash\nqop autocomplete --out completions --shell zsh\n```\n\n**Arguments:**\n*   `-o, --out \u003cPATH\u003e`: Path to write completion script to (required)\n*   `-s, --shell \u003cSHELL\u003e`: The shell to generate completions for (`bash`, `zsh`, `fish`, `elvish`, `powershell`) (required)\n\n## Migration Preview and Safety Features\n\n### Preview SQL during confirmation\n\nDuring confirmation prompts, type `d` or `diff` to preview the exact SQL for the operation:\n\n```bash\n# Apply pending migrations (press 'd' at the prompt to preview SQL)\nqop subsystem postgres up -p migrations/qop.toml\n\n# Revert last migration (press 'd' at the prompt to preview SQL)\nqop subsystem postgres down -p migrations/qop.toml\n```\n\nThe preview shows the raw SQL content exactly as it will be executed, with no additional formatting.\n\n### Diff command\n\nYou can also print pending SQL without prompts using the diff command:\n\n```bash\nqop subsystem postgres diff -p migrations/qop.toml\nqop subsystem sqlite   diff -p migrations/qop.toml\n```\n\n**Example Output:**\n```sql\nCREATE TABLE users (\n    id SERIAL PRIMARY KEY,\n    email VARCHAR(255) UNIQUE NOT NULL,\n    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE INDEX idx_users_email ON users(email);\n```\n\nThe output contains only the SQL statements from your migration files, making it easy to redirect to files or pipe to other tools.\n\n### Automated mode\n\n**Skip confirmations with `--yes`:**\n```bash\n# Apply all pending migrations without prompts\nqop subsystem postgres up --yes\n\n# Revert last migration without prompts\nqop subsystem postgres down --yes\n```\n\nThe `--dry` flag is now available for all migration commands and executes migrations in a transaction that is rolled back instead of committed, allowing you to test migrations safely.\n\n### Practical Examples\n\n**Development Workflow:**\n```bash\n# 1. Check what migrations are pending\nqop subsystem postgres diff\n\n# 2. Apply with confirmation\nqop subsystem postgres up\n```\n\n**CI/CD Pipeline:**\n```bash\n# Apply all pending migrations automatically\nqop subsystem postgres up --yes\n```\n\n**Debugging:**\n```bash\n# Save pending SQL to a file for review\nqop subsystem postgres diff \u003e pending_migrations.sql\n\n# Apply a specific migration\nqop subsystem postgres apply up 123456789\n```\n\n**Database Rollback:**\n```bash\n# Preview what will be rolled back (press 'd' at the prompt)\nqop subsystem postgres down\n\n# Rollback for real\nqop subsystem postgres down --yes\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcchexcode%2Fqop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcchexcode%2Fqop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcchexcode%2Fqop/lists"}