{"id":18402381,"url":"https://github.com/luchiniatwork/migrana","last_synced_at":"2025-06-20T16:14:46.680Z","repository":{"id":57713847,"uuid":"112020918","full_name":"luchiniatwork/migrana","owner":"luchiniatwork","description":"Migrana is a Datomic migration tool that gives you the control over how your Datomic database evolves.","archived":false,"fork":false,"pushed_at":"2018-07-24T23:26:54.000Z","size":28,"stargazers_count":26,"open_issues_count":3,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-06-12T01:59:07.613Z","etag":null,"topics":["clojure","database","datomic","migration","schema","schema-inference"],"latest_commit_sha":null,"homepage":"","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/luchiniatwork.png","metadata":{"files":{"readme":"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}},"created_at":"2017-11-25T17:12:50.000Z","updated_at":"2024-11-24T02:33:15.000Z","dependencies_parsed_at":"2022-08-25T10:30:59.267Z","dependency_job_id":null,"html_url":"https://github.com/luchiniatwork/migrana","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/luchiniatwork/migrana","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luchiniatwork%2Fmigrana","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luchiniatwork%2Fmigrana/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luchiniatwork%2Fmigrana/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luchiniatwork%2Fmigrana/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/luchiniatwork","download_url":"https://codeload.github.com/luchiniatwork/migrana/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luchiniatwork%2Fmigrana/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260976921,"owners_count":23091528,"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":["clojure","database","datomic","migration","schema","schema-inference"],"created_at":"2024-11-06T02:42:21.472Z","updated_at":"2025-06-20T16:14:41.636Z","avatar_url":"https://github.com/luchiniatwork.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Migrana\n\n[![Clojars Project](https://img.shields.io/clojars/v/migrana.svg)](https://clojars.org/migrana)\n\nMigrana is a Datomic migration tool loosely inspired in a mix of\n[conformity](https://github.com/rkneufeld/conformity) and\n[Rails' Active Record Migrations](http://edgeguides.rubyonrails.org/active_record_migrations.html).\n\nMigrana gives you the control over how your Datomic database evolves. It\nallows you to either write migrations and ensure that they run once and\nonly once or let Migrana infer schema evolution to you.\n\n## Motivation\n\nDatomic and its immutable nature simplifies migrations tremendously in\ncomparison to more traditional databases. However, every now and again, a\nfew things need change along the way. Two common scenarios are:\n\n1. When schema changes are not trivial and you end up needing to deal with\n   alterations that are not directly possible without a few interventions\n   beforehand (see [Altering Schema Attributes](http://docs.datomic.com/schema.html#altering-schema-attributes)\n   for more details). An example would be for instance setting an attribute\n   as `:db/unique`. Since uniqueness will be enforced, then making sure that\n   the transaction takes care of such conflictious cases will\n   guarantee that the new schema setting is applied successfully.\n2. When there are data transformations as part of the migration. An example\n   is a hypothetical situation for example where all entities that used to\n   have `:card/ratings` attribute now also need a default\n   `:card/has-been-rated?` attached to them).\n\n## Table of Contents\n\n* [Getting Started](#getting-started)\n* [Usage](#usage)\n* [Schema Inference](#schema-inference)\n* [Dry Running Migrations](#dry-running-migrations)\n* [Manual Migrations](#manual-migrations)\n* [Migrations as Code](#migrations-as-code)\n* [Options](#options)\n* [Bugs](#bugs)\n* [Help!](#help)\n\n## Getting Started\n\nThe recommended approach is to install Migrana as a user-level plugin on your lein\n`profiles.clj` file (`~/.lein/profiles.clj`). This will make Migrana available globally:\n\n```clojure\n{:user {:plugins [[migrana \"\u003cversion\u003e\"]]}}\n```\n\nThen run from anywhere:\n\n```\n$ lein help migrana\n```\n\nAlternatevely you can use Migrana as a project-level plugin putting\n`[migrana \"\u003cversion\u003e\"]` into the `:plugins` vector of your `project.clj`.\n\nThen run from your project root:\n\n```\n$ lein help migrana\n```\n\nLatest Migrana version:\n\n[![Clojars Project](http://clojars.org/migrana/latest-version.svg)](http://clojars.org/migrana)\n\n## Usage\n\nLet's assume you have a Datomic schema file on `resources/schema.edn`:\n\n```clojure\n[{:db/ident :person/name\n  :db/valueType :db.type/string\n  :db/cardinality :db.cardinality/one}\n {:db/ident :person/relationship-status\n  :db/valueType :db.type/ref\n  :db/cardinality :db.cardinality/one\n  :db/doc \"Whether the person is married or single.\"}\n {:db/ident :relationship-status/single}\n {:db/ident :relationship-status/married}\n {:db/ident :relationship-status/divorced}\n {:db/ident :relationship-status/widowed}]]\n```\n\nThen run:\n\n```\n$ lein migrana run datomic:dev://localhost:4334/my-db\n```\n\n_Note_: If you have the environment variable `DATOMIC_URI` set, you can call\n`lein migrana run` and it will connect to the DB specified there.\n\nMigrana will make sure that your schema is in the DB and will also create a\nmigration for you at `resources/migrations` with the timestamp\n`YYYYMMDDHHMMSS_schema_inference.edn`. I.e.:\n\n```\n$ ls resources/migrations\n20171124200143_schema_inference.edn\n```\n\nIf you run `lein migrana run` again, Migrana will not do anything because it\nwill detect that the schema file is unchanged and that it has already\nbeen asserted into the DB.\n\nIf you check the contents of `20171124200143_schema_inference.edn` you'll see:\n\n```clojure\n{:tx-data [{:db/ident :person/name\n            :db/valueType :db.type/string\n            :db/cardinality :db.cardinality/one}\n           {:db/ident :person/relationship-status\n            :db/valueType :db.type/ref\n            :db/cardinality :db.cardinality/one\n            :db/doc \"Whether the person is married or single.\"}\n           {:db/ident :relationship-status/single}\n           {:db/ident :relationship-status/married}\n           {:db/ident :relationship-status/divorced}\n           {:db/ident :relationship-status/widowed}]}\n```\n\nNow, let's suppose we need to add a new attribute for `:person/email`\nwhich we wish to be an identity and let's also add an index to `:person/name`.\nLet's change our `resources/schema.edn` to:\n\n```clojure\n[{:db/ident :person/name\n  :db/valueType :db.type/string\n  :db/index true\n  :db/cardinality :db.cardinality/one}\n {:db/ident :person/email\n  :db/valueType :db.type/string\n  :db/unique :db.type/identity\n  :db/cardinality :db.cardinality/one}\n {:db/ident :person/relationship-status\n  :db/valueType :db.type/ref\n  :db/cardinality :db.cardinality/one\n  :db/doc \"Whether the person is married or single.\"}\n {:db/ident :relationship-status/single}\n {:db/ident :relationship-status/married}\n {:db/ident :relationship-status/divorced}\n {:db/ident :relationship-status/widowed}]]\n```\n\nAfter the change we run `lein migrana run` again:\n\n```\n$ lein migrana run datomic:dev://localhost:4334/my-db\n```\n\nAnd let's check what Migrana has done to our `resources/migrations`:\n\n```\n$ ls resources/migrations\n20171124200143_schema_inference.edn 20171124200525_schema_inference.edn\n```\n\nWhen you check the content of the new file\n(`20171124200525_schema_inference.edn`), this is what you have:\n\n```clojure\n{:tx-data [{:db/ident :person/name\n            :db/index true}\n           {:db/ident :person/email\n            :db/valueType :db.type/string\n            :db/unique :db.type/identity\n            :db/cardinality :db.cardinality/one}]}\n```\n\n## Schema Inference\n\nYou probably noticed that, until now, the migration files are named \n`YYYYMMDDHHMMSS_schema_inference.edn`. That's because running `lein migrana run`\nas we have been doing will use its schema inference features (Migrana\nwill infer the migration required based on how the Datomic schema file\nhas changed).\n\nMigrana can detect only additions (such as we have seen before). Anything\nelse more advanced will require a manual migration.\n\n## Dry Running Migrations\n\nFor those situations where you don't want to run the migrations but would want to\nverify which migrations would run against your database, you can simply\nrun Migrana in the `dry-run` mode:\n\n```\n$ lein migrana dry-run\n```\n\n## Manual Migrations\n\nIn some cases, the schema inference done by Migrana is not enough. In such\ncases you can create a manual migration with:\n\n```\n$ lein migrana create retract_name\n```\n\nThis will create the migration `YYYYMMDDHHMMSS_retract_name.edn` in\nthe `resources/migrations` path:\n\n```\n$ ls resources/migrations\n20171124200143_schema_inference.edn 20171124200525_schema_inference.edn 20171124200733_retract_name.edn`\n```\n\nThe file will be empty and you can write your own migration steps in as a\nvector of the `:tx-data` map entry.\n\nAfter you edit the file, you can run `lein migrana` as usual and your\nmigration will be sent to the DB.\n\n## Migrations as Code\n\nIn some cases you might want to have code that interacts with your manual\nmigratation. In these cases, simply create an empty manual migration with:\n\n```\n$ lein migrana create code_as_migration\n```\n\nThen open the migration created in `resources/migrations` and edit to look\nlike something like this:\n\n```clojure\n{:tx-fn 'my-project.migrations/add-suffix-to-all-names}\n```\n\nThen in your `src/my_project/migrations.clj` you could have:\n\n```clojure\n(ns my-project.migrations\n  (:require [datomic.api :as d]))\n\n(defn add-suffix-to-all-names [conn]\n  (let [names (d/q '[:find (pull ?e [:db/id :person/name])\n                     :where\n                     [?e :person/name]]\n                   (d/db conn))]\n    (map (fn [n] {:db/id (:db/id (first n))\n                  :person/name (str (:person/name (first n)) \"Suffix\")})\n         names)))\n```\n\nAfter you edit the file, you can run `lein migrana run` as usual and your\nmigration will be sent to the DB.\n\n## Options\n\n```\n$ lein help migrana\nDatomic migration tool.\n\n  Syntax: lein migrana \u003csubtask\u003e \u003coptions\u003e\n\nSubtasks available:\ninfo      Shows current database information.\ncreate    Creates new manual migration.\ndry-run   Simulates what `run` would do.\nrun       Transacts pending migrations onto database.\nset-db    Sets the database timestamp.\n\nRun `lein help migrana $SUBTASK` for subtask details.\n```\n\nFor `dry-run`, `run`, and `set-db` you also have (NOT IMPLEMENTED YET\n[this issue](https://github.com/luchiniatwork/migrana/issues/3):\n\n```\nOptions for `run`, `dry-run`, and `set-db` commands:\n\n  -s, --schema SCHEMA_FILE          Schema file (default resources/schema.edn)\n  -m, --migrations MIGRATIONS_PATH  Migrations path (default resources/migrations/)\n      --no-inference                Runs with no schema change inference\n```\n\n## Bugs\n\nIf you find a bug, submit a\n[Github issue](https://github.com/luchiniatwork/migrana/issues).\n\n## Help\n\nThis project is looking for team members who can help this project succeed!\nIf you are interested in becoming a team member please open an issue.\n\n## License\n\nCopyright © 2017 Tiago Luchini\n\nDistributed under the MIT License. See LICENSE\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluchiniatwork%2Fmigrana","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fluchiniatwork%2Fmigrana","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluchiniatwork%2Fmigrana/lists"}