{"id":15010429,"url":"https://github.com/abogoyavlensky/automigrate","last_synced_at":"2025-04-09T22:51:25.921Z","repository":{"id":148005504,"uuid":"339197520","full_name":"abogoyavlensky/automigrate","owner":"abogoyavlensky","description":":robot: Auto-generated database migrations for Clojure","archived":false,"fork":false,"pushed_at":"2025-02-07T03:38:31.000Z","size":352,"stargazers_count":77,"open_issues_count":2,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-09T22:51:14.615Z","etag":null,"topics":["auto-migrations","automigrate","clj","clojure","database","migrations","postgresql"],"latest_commit_sha":null,"homepage":"https://cljdoc.org/d/net.clojars.abogoyavlensky/automigrate","language":"Clojure","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/abogoyavlensky.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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}},"created_at":"2021-02-15T20:25:00.000Z","updated_at":"2025-02-07T03:38:33.000Z","dependencies_parsed_at":"2024-01-23T00:41:02.534Z","dependency_job_id":"eedf17ac-147a-4874-872c-cca7e7e3db7a","html_url":"https://github.com/abogoyavlensky/automigrate","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abogoyavlensky%2Fautomigrate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abogoyavlensky%2Fautomigrate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abogoyavlensky%2Fautomigrate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abogoyavlensky%2Fautomigrate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/abogoyavlensky","download_url":"https://codeload.github.com/abogoyavlensky/automigrate/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248125643,"owners_count":21051766,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["auto-migrations","automigrate","clj","clojure","database","migrations","postgresql"],"created_at":"2024-09-24T19:34:09.808Z","updated_at":"2025-04-09T22:51:25.874Z","avatar_url":"https://github.com/abogoyavlensky.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Automigrate\n\n[![CI](https://github.com/abogoyavlensky/automigrate/actions/workflows/master.yaml/badge.svg?branch=master)](https://github.com/abogoyavlensky/automigrate/actions/workflows/master.yaml)\n[![cljdoc badge](https://cljdoc.org/badge/net.clojars.abogoyavlensky/automigrate)](https://cljdoc.org/jump/release/net.clojars.abogoyavlensky/automigrate)\n\nAuto-generated database schema migrations for Clojure. Define models as plain EDN data \nand create database schema migrations automatically based on changes to the models.\n\n\n## Features\n\n- **declaratively** define db schema as **models** in EDN;\n- create migrations **automatically** based on model changes;\n- **migrate** db schema in forward and backward directions;\n- manage migrations for: tables, indexes, constraints, enum types;\n- view actual SQL or human-readable description for a migration;\n- optionally add a custom SQL migration for specific cases;\n- use with PostgreSQL :information_source: [*other databases are planned*] .\n\n### Quick overview\n\nhttps://github.com/abogoyavlensky/automigrate/assets/1375411/880db134-f2ed-46b4-9e77-72e326b6bf56\n\n## State\n\nProject is in **alpha** state till the `1.0.0` version and is not yet ready for production use. \nBreaking changes are possible.\n\n\n## Usage\n\n### Installation\n\n[![Clojars Project](https://img.shields.io/clojars/v/net.clojars.abogoyavlensky/automigrate.svg)](https://clojars.org/net.clojars.abogoyavlensky/automigrate)\n\n#### Setup database connection\n\nBefore running migrations we need to set database URL with `DATABASE_URL` env var, for example:\n\n```shell\nexport DATABASE_URL=\"jdbc:postgresql://localhost:5432/mydb?user=myuser\u0026password=secret\"\n```\n\n***Note:** There is an ability to change the name of the environment variable using command argument: `:jdbc-url-env-var`.\nAlternatively, instead of env var we can use `:jdbc-url` argument to setup the database URL directly for commands.*\n\n#### tools.deps -X option\n\n*deps.edn*\n```clojure\n{:deps {org.clojure/clojure {:mvn/version \"1.11.2\"}\n        org.postgresql/postgresql {:mvn/version \"42.3.1\"}}\n :paths [... \"resources\"]\n :aliases \n {...\n  :migrations {:extra-deps {net.clojars.abogoyavlensky/automigrate {:mvn/version \"\u003cVERSION\u003e\"}}\n               :ns-default automigrate.core}}}\n```\n\nNow we can create `resources/db/models.edn` file with a map and run commands:\n\n```shell\n$ clojure -X:migrations list\n$ clojure -X:migrations make\n$ clojure -X:migrations migrate\n$ clojure -X:migrations explain :number 1\n$ clojure -X:migrations help\n```\n\n#### Leiningen\n\n*project.clj*\n```clojure\n(defproject myprj \"0.1.0-SNAPSHOT\"\n  :dependencies [[org.clojure/clojure \"1.11.2\"]\n                 [org.postgresql/postgresql \"42.7.3\"]]\n  :resource-paths [\"resources\"]\n  :profiles {...\n             :migrations\n             {:dependencies [[net.clojars.abogoyavlensky/automigrate \"\u003cVERSION\u003e\"]]\n              :main automigrate.core}}\n  :aliases {\"migrations\" [\"with-profile\" \"+migrations\" \"run\"]})\n```\n\nUsage example:\n```clojure\n$ lein migrations list\n$ lein migrations make\n$ lein migrations migrate\n$ lein migrations explain --number 1\n$ lein migrations help\n```\n***Note:** For lein there is the same CLI-interface with the same commands and options, but\ninstead of keywords (e.g.`:number`) for option names you should use  `--...` (e.g. `--number`).*\n\n### Getting started\n\nAfter configuration, you need to create `models.edn` file with first model. \nThen you will be able to make migration and migrate db schema.\nBy default, the path for models file is `resources/db/models.edn`.\n\nA model is the representation of a database table which is described in EDN structure.\nLet's do it step by step.\n\n#### Add model\n\n*resources/db/models.edn*\n```clojure\n{:book [[:id :serial {:primary-key true}]\n        [:name [:varchar 255] {:null false}]\n        [:description :text]]}\n```\n\n#### Make migration\n```shell\n$ clojure -X:migrations make\nCreated migration: resources/db/migrations/0001_auto_create_table_book.edn\nActions:\n  - create table book\n```\n\nA migration file will be created at `resources/db/migrations` by default.\nThe pattern for migration file name is: `\u003cnumber\u003e_auto_\u003cautogenerated migration name\u003e.edn`.\nThe migration can contain multiple actions. Every action will be converted to a SQL query at migration time.\nThe migration at `resources/db/migrations/0001_auto_create_table_book.edn` looks like:\n\n```clojure\n({:action :create-table,\n  :model-name :book,\n  :fields\n  {:id {:primary-key true, :type :serial},\n   :name {:null false, :type [:varchar 255]},\n   :description {:type :text}}})\n```\n\n#### Migrate\nExisting migrations will be applied one by one in order of migration number:\n\n```shell\n$ clojure -X:migrations migrate\nApplying 0001_auto_create_table_book...\n0001_auto_create_table_book successfully applied.\n```\n\nThat's it. In the database you can see a newly created table called `book` with defined columns \nand one entry in table `automigrate_migrations` with new migration `0001_auto_create_table_book`.\n\n#### List and explain migrations\n\nTo view status of existing migrations you can run:\n```shell\n$ clojure -X:migrations list\nExisting migrations:\n[x] 0001_auto_create_table_book.edn\n```\n\nTo view raw SQL for existing migration you can run command `explain` with appropriate number: \n\n```shell\n$ clojure -X:migrations explain :number 1\nSQL for forward migration 0001_auto_create_table_book.edn:\n\nBEGIN;\n\nCREATE TABLE book (\n  id SERIAL PRIMARY KEY,\n  name VARCHAR(255) NOT NULL,\n  description TEXT\n);\n\nCOMMIT;\n```\n\nAll SQL queries of the migration are wrapped by a transaction.\n\n:information_source: *For a slightly more complex example please check [models.edn](/examples/books/resources/db/models.edn)\nand [README.md](/examples/books/README.md) from the `examples` dir of this repo.* \n\n## Documentation\n\n### Model definition\n\nModels are represented as a map with the model name as a keyword key and the value describing the model itself.\nA model's definition could be a vector of vectors in the simple case of just defining fields.\nAs we saw in the previous example:\n\n```clojure\n{:book [[:id :serial {:primary-key true}]\n        [:name [:varchar 255] {:null false}]\n        [:description :text]]}\n```\n\nOr it could be a map with keys `:fields`, `:indexes` (*optional*) and `:types` (*optional*). Each of these is also a vector of vectors. \nThe same model from above could be described as a map:\n\n```clojure\n{:book {:fields [[:id :serial {:primary-key true}]\n                [:name [:varchar 255] {:null false}]\n                [:description :text]]}}\n```\n\n#### Fields\n\nEach field is a vector of three elements: `[:field-name :field-type {:some-option :option-value}]`. \nThe third element is optional, but name and type are required.\n\nThe first element is the name of a field and must be a keyword.\n\n##### Field types\nThe second element could be a keyword or a vector of keyword and params. \nAvailable field types are matched with PostgreSQL built-in data types\nand presented in the following table:\n\n| Field type                                  | Description                                                                                                                                                                 |\n|---------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `:integer`                                  |                                                                                                                                                                             |\n| `:smallint`                                 |                                                                                                                                                                             |\n| `:bigint`                                   |                                                                                                                                                                             |\n| `:float`                                    |                                                                                                                                                                             |\n| `:real`                                     |                                                                                                                                                                             |\n| `:serial`                                   | Auto-incremented pg integer field.                                                                                                                                          |\n| `:bigserial`                                | Auto-incremented pg bigint field.                                                                                                                                           |\n| `:smallserial`                              | Auto-incremented pg serial2 field.                                                                                                                                          |\n| `:numeric` or `[:numeric \u003cpos-int\u003e? \u003cint\u003e]` | Numeric type with optional precision and scale params. Default value could be set as numeric string, bigdec, float, int and nil: `\"10.22\"`, `10.22M`, `10`, `10.22`, `nil`. |\n| `:decimal` or `[:decimal \u003cpos-int\u003e? \u003cint\u003e]` | Numeric type with optional precision and scale params. Same as `:numeric`.                                                                                                  |\n| `:uuid`                                     |                                                                                                                                                                             |\n| `:boolean`                                  |                                                                                                                                                                             |\n| `:text`                                     |                                                                                                                                                                             |\n| `:time` or `[:time \u003cint\u003e]`                  |                                                                                                                                                                             |\n| `:timetz` or `[:timetz \u003cint\u003e]`              |                                                                                                                                                                             |\n| `:timestamp` or `[:timestamp \u003cint\u003e]`        |                                                                                                                                                                             |\n| `:timestamptz`  or `[:timestamptz \u003cint\u003e]`   |                                                                                                                                                                             |\n| `:interval` or `[:interval \u003cint\u003e]`          |                                                                                                                                                                             |\n| `:date`                                     |                                                                                                                                                                             |\n| `:point`                                    |                                                                                                                                                                             |\n| `:json`                                     |                                                                                                                                                                             |\n| `:jsonb`                                    |                                                                                                                                                                             |\n| `:varchar` or `[:varchar \u003cpos-int\u003e]`        | Second element is the length of value.                                                                                                                                      |\n| `:char` or `[:char \u003cpos-int\u003e]`              | Second element is the length of value.                                                                                                                                      |\n| `:float` or `[:float \u003cpos-int\u003e]`            | Second element is the minimum acceptable precision in binary digits.                                                                                                        |\n| `[:enum \u003cenum-type-name\u003e]`                  | To use enum type you should define it in `:types` section in model.                                                                                                         |\n| `:box`                                      |                                                                                                                                                                             |\n| `:bytea`                                    |                                                                                                                                                                             |\n| `:cidr`                                     |                                                                                                                                                                             |\n| `:circle`                                   |                                                                                                                                                                             |\n| `:double-precision`                         |                                                                                                                                                                             |\n| `:inet`                                     |                                                                                                                                                                             |\n| `:line`                                     |                                                                                                                                                                             |\n| `:lseg`                                     |                                                                                                                                                                             |\n| `:macaddr`                                  |                                                                                                                                                                             |\n| `:macaddr8`                                 |                                                                                                                                                                             |\n| `:money`                                    |                                                                                                                                                                             | \n| `:path`                                     |                                                                                                                                                                             |\n| `:pg_lsn`                                   |                                                                                                                                                                             |\n| `:pg_snapshot`                              |                                                                                                                                                                             |\n| `:polygon`                                  |                                                                                                                                                                             |\n| `:tsquery`                                  |                                                                                                                                                                             |\n| `:tsvector`                                 |                                                                                                                                                                             |\n| `:txid_snapshot`                            |                                                                                                                                                                             |\n| `:xml`                                      |                                                                                                                                                                             |\n| `:bit` or `[:bit \u003cpos-int\u003e]`                |                                                                                                                                                                             | \n| `:varbit` or `[:varbit \u003cpos-int\u003e]`          |                                                                                                                                                                             |\n\nDoc reference to the PostgreSQL built-in general-purpose data types: \n[https://www.postgresql.org/docs/current/datatype.html#DATATYPE-TABLE](https://www.postgresql.org/docs/current/datatype.html#DATATYPE-TABLE) \n\n###### Notes\n\n- _`\u003c...\u003e?` - param is optional._\n- _`or` - an alternative definition of type._\n\n:information_source: *There are fixed field types because `automigrate` \nvalidates type of field and default value to have errors as early as possible \nbefore running migration against database.*\n\n##### Field options\n\nOptions value is a map where key is the name of the option and value is the available option value.\nAvailable options are presented in the table below:\n\n| Field option   | Description                                                                                   | Required? | Value                                                                                                                          |\n|----------------|-----------------------------------------------------------------------------------------------|-----------|--------------------------------------------------------------------------------------------------------------------------------|\n| `:null`        | Set to `false` for non-nullable field. Field is nullable by default if the option is not set. | `false`   | `boolean?`                                                                                                                     |\n| `:primary-key` | Set to `true` for making primary key field.                                                   | `false`   | `true?`                                                                                                                        |\n| `:unique`      | Set to `true` to add unique constraint for a field.                                           | `false`   | `true?`                                                                                                                        |\n| `:default`     | Default value for a field.                                                                    | `false`   | `boolean?`, `integer?`, `float?`, `decimal?`, `string?`, `nil?`, or fn defined as `[:keyword \u003cinteger? or float? or string?\u003e]` |\n| `:foreign-key` | Set to namespaced keyword to point to a primary key field from another model.                 | `false`   | `:another-model/field-name`                                                                                                    |\n| `:on-delete`   | Specify delete action for `:foreign-key`.                                                     | `false`   | `:cascade`, `:set-null`, `:set-default`, `:restrict`, `:no-action`                                                             |\n| `:on-update`   | Specify update action for `:foreign-key`.                                                     | `false`   | `:cascade`, `:set-null`, `:set-default`, `:restrict`, `:no-action`                                                             |\n| `:check`       | Set condition in Honeysql format to create custom CHECK for a column.                         | `false`   | Example: `[:and [:\u003e :month 0] [:\u003c= :month 12]]`                                                                                |\n| `:array`       | Can be added to any field type to make it array.                                              | `false`   | `string?`, examples: `\"[]\"`, `\"[][]\"`, `[][10][3]`                                                                             |\n| `:comment`     | Add a comment on the field.                                                                   | `false`   | `string?`                                                                                                                      |\n\n\n#### Indexes\n\nEach index is a vector of three elements: \n`[:name-of-index :type-of-index {:fields [:field-from-model-to-index] :unique boolean? :where [...]}]`\nName, type and `:fields` in options are required.\n\nThe first element is the name of an index and must be a keyword.\n\n##### Index types\n\nThe second element is an index type and must be a keyword of available index types:\n\n| Field type |\n|------------|\n| `:btree`   |\n| `:gin`     |\n| `:gist`    |\n| `:spgist`  |\n| `:brin`    |\n| `:hash`    |\n\n##### Index options\n\nThe options value is a map where key is the name of the option and value is the available option value.\nThe option `:fields` is required, others are optional.  \nAvailable options are presented in the table below:\n\n\n| Field option | Description                                                           | Required? | Value                      |\n|--------------|-----------------------------------------------------------------------|-----------|----------------------------|\n| `:fields`    | Vector of fields as keywords. Index will be created for those fields. | `true`    | [`:field-name` ...]        |\n| `:unique`    | Set to `true` if index should be unique.                              | `false`   | `true?`                    |\n| `:where`     | Set condition in Honeysql format to create partial index.             | `false`   | Example: `[:\u003e amount 10]`  |\n\n#### Types\n\n:information_source: _At the moment only Enum type is supported._\n\nEach type is a vector of three elements: `[:name-of-type :type-of-type {...}]`\nName, type-of-type and options are required.\n\nThe first element is the name of a type and must be a keyword.\n\n##### Type of type\n\nThe second element is a type of type and must be a keyword of available types:\n\n\n| Field type |\n|------------|\n| `:enum`    |\n\n\n##### Enum type\nEach enum type is a vector of three elements: `[:name-of-type :enum {:choices [\u003cstr\u003e]}]`\n\nOptions for enum type must contain the `:choices` value with vector of strings. \n`:choices` represent enum values for the type.\n\nAn example of model definition with enum type:\n```clojure\n{:account {:fields [[:id :serial]\n                    [:role [:enum :account-role]]]\n           :types [[:account-role :enum {:choices [\"admin\" \"customer\"]}]]}}\n```\n\nLimitations:\n\n- `:choices` can't be empty;\n- values in `:choices` must be unique for the particular type;\n- **removing** a value from `:choices` of existing type is not supported;\n- **re-ordering** values in `:choices` of existing type is not supported;\n        \n### CLI interface\n\nAvailable commands are: \n\n| Command   | Description                                                       |\n|-----------|-------------------------------------------------------------------|\n| `make`    | Create migration for new changes in models file.                  |\n| `migrate` | Apply a change described in the  migration to database.           |\n| `list`    | Show list of existing migrations with status.                     |\n| `explain` | Show a migration in SQL or human-readable format.                 |\n| `help`    | Show short documentation for Automigrate or a particular command. |\n\nCommon args for all commands:\n\n| Argument            | Description                                                               | Required? | Possible values                                                                                  | Default value                                               |\n|---------------------|---------------------------------------------------------------------------|-----------|--------------------------------------------------------------------------------------------------|-------------------------------------------------------------|\n| `:jdbc-url`         | Database connection defined as JDBC-url.                                  | `false`   | string jdbc url (example: `\"jdbc:postgresql://localhost:5432/mydb?user=myuser\u0026password=secret\"`) | Read env var (`DATABASE_URL` or set as `:jdbc-url-env-var`) |\n| `:jdbc-url-env-var` | Name of environment variable for jdbc-url.                                | `false`   | string jdbc url (example: `DB_URL`)                                                              | `DATABASE_URL`                                              |\n| `:models-file`      | Path to models file, relative to the `resources` dir.                     | `false`   | string path (example: `\"path/to/models.edn\"`)                                                    | `\"db/models.edn\"`                                           |\n| `:migrations-dir`   | Path to store migrations dir, relative to the `resources` dir.            | `false`   | string path (example: `\"path/to/migrations\"`)                                                    | `\"db/migrations\"`                                           |\n| `:resources-dir`    | Path to resources dir to create migrations dir when it doesn't exist yet. | `false`   | string path (example: `\"path/to/resources\"`)                                                     | `\"resources\"`                                               |\n| `:migrations-table` | Model name for storing applied migrations.                                | `false`   | string (example: `\"migrations\"`)                                                                 | `\"automigrate_migrations\"`                                  |\n\n### `make`\n\nCreate migration for new changes in models file.\nIt detects the creating, updating and deleting of tables, columns and indexes.\nEach migration is wrapped by transaction by default.\n\n*Specific args:*\n\n| Argument          | Description                                              | Required?                                            | Possible values                               | Default value                                           |\n|-------------------|----------------------------------------------------------|------------------------------------------------------|-----------------------------------------------|---------------------------------------------------------|\n| `:type`           | Type of migration file.                                  | `false`                                              | `:empty-sql`                                  | *not provided*, migration will be created automatically |\n| `:name`           | Custom name for migration file separated by underscores. | `false` *(:warning: required for `:empty-sql` type)* | string (example: `\"add_custom_trigger\"`)      | *generated automatically by first migration action*     |\n\n##### Examples\n\nCreate migration automatically with auto-generated name:\n\n```shell\n$ clojure -X:migrations :make\nCreated migration: resources/db/migrations/0001_auto_create_table_book.edn\nActions:\n  ...\n```\n\nCreate migration automatically with custom name:\n\n```shell\n$ clojure -X:migrations make :name create_table_author\nCreated migration: resources/db/migrations/0002_create_table_author.edn\nActions:\n  ...\n```\n\nCreate empty SQL migration with custom name:\n\n```shell\n$ clojure -X:migrations make :type :empty-sql :name add_custom_trigger\nCreated migration: resources/db/migrations/0003_add_custom_trigger.sql\n```\n\nTry to create migration without new changes in models:\n\n```shell\n$ clojure -X:migrations make\nThere are no changes in models.\n```\n\n\n### `migrate`\n\nApplies change described in migration to database.\nApplies all unapplied migrations by number order if arg `:number` is not presented in command.\nThrows error for same migration number.\n\nBackward migration is fully implemented. For auto-generated and SQL migrations, it is possible to revert migration and to delete appropriate entry from migrations table.\nDatabase changes will be reverted. \n\nIn forward direction if specified migration `:number` is **included**, meaning if, for example, `:number 3` the migration with number 3 **will be applied**.\nIn backward migration the `:number` is **excluded**, so all migrations until the specified number will be reverted but not the target one. \nFor instance if we have 3 migrations as applied, and want to revert just the 3d and 2d ones, we can run `migrate` command with `:number 1`. \n3d and 3d migrations will be reverted, but the first one will stay applied.     \n\n*Specific args:*\n\n| Argument  | Description                                                                                                                                                                 | Required? | Possible values                                 | Default value                                    |\n|-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|-------------------------------------------------|--------------------------------------------------|\n| `:number` | Number of migration which should be a target point. In forward direction, migration by number will by applied. In backward direction, migration by number will be reverted. | `false`   | integer (example: `1` for migration `0001_...`) | *not provided*, last migration number by default |\n\n##### Examples\n\nMigrate forward all unapplied migrations:\n```shell\n$ clojure -X:migrations migrate\nAppyling 0001_auto_create_table_book...\n0001_auto_create_table_book successfully applied.\nAppyling 0002_create_table_author...\n0002_create_table_author successfully applied.\nAppyling 0003_add_custom_trigger...\n0003_add_custom_trigger successfully applied.\n```\n\nMigrate forward up to particular migration number (*included*):\n```shell\n$ clojure -X:migrations migrate :number 2\nAppyling 0001_auto_create_table_book...\n0001_auto_create_table_book successfully applied.\nAppyling 0002_create_table_author...\n0002_create_table_author successfully applied.\n```\n\nMigrate backward down to particular migration number (*excluded*):\n```shell\n$ clojure -X:migrations migrate :number 1\nReverting 0002_create_table_author...\n0002_create_table_author successfully reverted.\n```\n\nMigrate backward to initial state of database:\n```shell\n$ clojure -X:migrations migrate :number 0\nReverting 0003_add_custom_trigger...\n0003_add_custom_trigger successfully reverted.\nReverting 0002_create_table_author...\n0002_create_table_author successfully reverted.\nReverting 0001_auto_create_table_book...\n0001_auto_create_table_book successfully reverted.\n```\n\nTry to migrate already migrated migrations:\n```shell\n$ clojure -X:migrations migrate\nNothing to migrate.\n```\n\nTry to migrate up to not existing migration:\n```shell\n$ clojure -X:migrations migrate :number 10\n-- ERROR -------------------------------------\n\nInvalid target migration number.\n```\n\n### `list`\n\nPrint out list of existing migrations with statuses displayed as\nboxes before migration name:\n- `[x]` - applied;\n- `[ ]` - not applied.\n\n*No specific args.*\n\n##### Examples:\n\nView list of partially applied migrations:\n```shell\n$ clojure -X:migrations list\nExisting migrations:\n[x] 0001_auto_create_table_book.edn\n[ ] 0002_create_table_author.edn\n[ ] 0003_add_custom_trigger.sql\n```\n\n### `explain`\n\nPrint out actual raw SQL for particular migration by number.\n\n*Specific args:*\n\n| Argument     | Description                                       | Required? | Possible values                                 | Default value  |\n|--------------|---------------------------------------------------|-----------|-------------------------------------------------|----------------|\n| `:number`    | Number of migration which should be explained.    | `true`    | integer (example: `1` for migration `0001_...`) | *not provided* |\n| `:direction` | Direction in which migration should be explained. | `false`   | `:forward`, `:backward`                         | `:forward`     |\n| `:format`    | Format of explanation.                            | `false`   | `:sql`, `:human`                                | `:sql`         |\n\n##### Examples:\n\nView raw SQL for migration in forward direction:\n```shell\n$ clojure -X:migrations explain :number 1\nSQL for forward migration 0001_auto_create_table_book.edn:\n\nBEGIN;\n\nCREATE TABLE book (\n  id SERIAL PRIMARY KEY,\n  name VARCHAR(255) NOT NULL,\n  description TEXT\n);\n\nCOMMIT;\n```\n\nView raw SQL for migration in backward direction:\n```shell\n$ clojure -X:migrations explain :number 1 :direction backward\nSQL for backward migration 0001_auto_create_table_book.edn:\n\nBEGIN;\n\nDROP TABLE IF EXISTS book;\n\nCOMMIT;\n```\n\n### `help`\n\nYou can print short doc info for a particular command or the tool itself by running `help` command.\n\n*Args:*\n\n| Argument | Description   | Required? | Possible values                               | Default value                                          |\n|----------|---------------|-----------|-----------------------------------------------|--------------------------------------------------------|\n| `:cmd`   | Command name. | `false`   | `make`, `migrate`, `list`, `explain`, `help`  | *not provided*, by default prints doc for all commands |\n\n\n##### Examples \n\nPrint doc for all available commands:\n\n```shell\n$ clojure -X:migrations help\nAuto-generated database migrations for Clojure.\n\nAvailable commands:\n...\n```\n\nPrint doc for a particular command:\n\n```shell\n$ clojure -X:migrations help :cmd make\nCreate a new migration based on changes to the models.\n\nAvailable options:\n...\n```\n\n\n### Custom SQL migration \n\nThere are some specific cases which are not yet supported by auto-migrations. \nThere are cases when you need to add simple data migration.\nYou can add a custom SQL migration which contains raw SQL for forward and backward directions separately in single SQL-file.\nFor that you can run the following command for making empty SQL migration with custom name:\n\n```shell\n$ clojure -X:migrations make :type :empty-sql :name make_all_accounts_active\nCreated migration: resources/db/migrations/0003_make_all_accounts_active.sql\n```\n\nThe newly created file will look like:\n\n```sql\n-- FORWARD\n\n\n-- BACKWARD\n\n```\n\nYou can fill it with two block of queries for forward and backward migration. \nBackward migration block is not mandatory and can be empty. \nFor example:\n\n```sql\n-- FORWARD\n\nUPDATE account\nSET is_active = true;\n\n-- BACKWARD\n\nUPDATE account\nSET is_active = false;\n\n```\n\nThen migrate it as usual:\n```shell\n$ clojure -X:migrations migrate\nAppyling 0003_make_all_accounts_active...\n0003_make_all_accounts_active successfully applied.\n```\n\n\n### Use in production\n\n:warning: *The library is not yet ready for production use. \nBut it is really appreciated if you try it out! :wink:*\n\nIn production build you can use `DATABASE_URL` env variable to set up database connection\nfor migrations. There are some options we have to run migrations. \n\n#### Inside application as a part of the system start\n\nAn example for Integrant database component:\n```clojure\n(ns myprj.main\n  (:require [automigrate.core :as automigrate]\n            [integrant.core :as ig]\n            [hikari-cp.core :as cp])\n\n  (defmethod ig/init-key ::db\n     [_ options]\n     (automigrate/migrate)\n     (cp/make-datasource options)))\n```\n\n#### Without uberjar\nIf you do not build a jar-file and use clojure cli tool or lein to run the app then you can use the same alias \nas it is described in the installation section of this doc.\n\n```shell\n$ clojure -X:migrations migrate\n```\n\n#### With uberjar\n\nIf you build jar-file then you can implement additional option to run migration via main, for instance:\n\n```clojure\n(ns myprj.main\n  (:gen-class)\n  (:require [automigrate.core :as automigrate]))\n\n(defn- run-system\n  []\n  ...)\n\n(defn -main\n  \"Run application system in production.\"\n  [\u0026 [command]]\n  (case command\n    \"migrations\" (automigrate/migrate)\n    (run-system)))\n```\n\nThen build jar-file and run migrations \n```shell\n$ java -jar target/standalone.jar migrations \nAppyling ...\n```\n\nor run the app:\n```shell\n$ java -jar target/standalone.jar\n```\n\n## Roadmap\n\n- [x] Enum type of fields.\n- [x] All built-in data types.\n- [x] Array data types.\n- [x] Comment on field.\n- [x] Partial indexes.\n- [x] Auto-generated backward migration.\n- [x] Field level CHECK constraints.\n- [x] Leiningen support.\n- [ ] Support for SQLite/MySQL.\n- [ ] Model level constraints.\n- [ ] Optimized auto-generated SQL queries.\n- [ ] Standalone tool using GraalVM.\n- [ ] Visual representation of DB schema.\n\n\n### Things still in design\n\n- How to handle common configuration conveniently (separated edn file?)?\n- More consistent and helpful messages for users, maybe using `fipp` library.\n- Ability to separate models by multiple files.\n- Move transformations out of clojure spec conformers or replace `spec` with `malli`.\n- Simplify model definition just as map with key `:type` instead of vector of 3 items. \n- Disable field types validation at all, or add ability to set arbitrary custom type.\n- Handle of model/field renaming.\n\n\n## Inspired by\n\n- [Django Migrations](https://docs.djangoproject.com/en/4.0/topics/migrations/)\n- [Prisma Migrate](https://www.prisma.io/migrate)\n\n### Thanks to projects\n- [Honey SQL](https://github.com/seancorfield/honeysql)\n- [Dependency](https://github.com/weavejester/dependency)\n- [Differ](https://github.com/robinheghan/differ)\n\n\n## Resources\n\n- [Announcing automigrate](https://bogoyavlensky.com/blog/announcing-automigrate/) (blog post) \n- [Designing a database schema for a budget tracker with Automigrate](https://bogoyavlensky.com/blog/db-schema-for-budget-tracker-with-automigrate/) (blog post) \n\n## Projects\n\n- [clojure-kamal-example](https://github.com/abogoyavlensky/clojure-kamal-example)\n\n## Development\n\n### Install system deps\n\nInstall [mise-en-place](https://mise.jdx.dev/getting-started.html#quickstart) and run:\n\n```shell\nmise install\n```\n\n### Run locally\n\n```shell\nmake up  # run docker compose with databases for development\nmake repl  # run builtin repl with dev aliases; also you could use any repl you want\nmake test  # run whole tests locally against testing database started by docker compose\nmake fmt  # run formatting in action mode\nmake lint  # run linting\nmake outdated  # run checking new versions of deps in force mode\n```\n\n### Release new version\n\n```shell\nmake install-snapshot :patch  # build and install locally a new version of lib based on latest git tag and using semver\nmake deploy-snapshot :patch  # build and deploy to Clojars next snapshot version from local machine\nmake release :patch  # bump git tag version by semver rules and push to remote repo\n```\n\n\n## License\n\nCopyright © 2021 Andrey Bogoyavlenskiy\n\nDistributed under the MIT License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabogoyavlensky%2Fautomigrate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fabogoyavlensky%2Fautomigrate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabogoyavlensky%2Fautomigrate/lists"}