{"id":13879097,"url":"https://github.com/procore-oss/handcuffs","last_synced_at":"2025-12-16T04:04:30.431Z","repository":{"id":39656142,"uuid":"58003958","full_name":"procore-oss/handcuffs","owner":"procore-oss","description":"A Ruby gem for running Active Record migrations in phases","archived":false,"fork":false,"pushed_at":"2025-06-10T00:16:20.000Z","size":185,"stargazers_count":93,"open_issues_count":7,"forks_count":7,"subscribers_count":149,"default_branch":"main","last_synced_at":"2025-07-11T17:36:54.925Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Ruby","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/procore-oss.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2016-05-03T21:53:09.000Z","updated_at":"2025-06-06T16:54:36.000Z","dependencies_parsed_at":"2024-02-15T10:06:05.892Z","dependency_job_id":"16e2b4e0-d2d8-4095-a802-29884ac9ffd7","html_url":"https://github.com/procore-oss/handcuffs","commit_stats":null,"previous_names":["procore/handcuffs"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/procore-oss/handcuffs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/procore-oss%2Fhandcuffs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/procore-oss%2Fhandcuffs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/procore-oss%2Fhandcuffs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/procore-oss%2Fhandcuffs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/procore-oss","download_url":"https://codeload.github.com/procore-oss/handcuffs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/procore-oss%2Fhandcuffs/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265521426,"owners_count":23781500,"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":[],"created_at":"2024-08-06T08:02:09.744Z","updated_at":"2025-12-16T04:04:25.378Z","avatar_url":"https://github.com/procore-oss.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# Handcuffs\n\n[![Test](https://github.com/procore-oss/handcuffs/actions/workflows/test.yaml/badge.svg?branch=main)](https://github.com/procore-oss/handcuffs/actions/workflows/test.yaml)\n[![Gem Version](https://badge.fury.io/rb/handcuffs.svg)](https://badge.fury.io/rb/handcuffs)\n[![Discord](https://img.shields.io/badge/Chat-EDEDED?logo=discord)](https://discord.gg/PbntEMmWws)\n\nHandcuffs provides an easy way to run [Ruby on Rails](https://rubyonrails.org/) migrations in phases using a simple process:\n\n1. Define a set of named phases in the order in which they should be run\n2. Tag migrations with one of the defined phase names\n3. Run migrations by phase at start, end or outside of application deployment\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'handcuffs'\n```\n\nAnd then execute:\n\n```bash\nbundle\n```\n\nOr install it directly on the current system using:\n\n```bash\ngem install handcuffs\n```\n\n## Usage\n\n### Configuration\n\nCreate a handcuffs initializer and define the migration phases in the order in which they should be run. You should also define a default phase for pre-existing \"untagged\" migrations, or if you want the option to tag only custom phases.\n\nThe most basic configuration is an array of phase names, and using the first one as the default:\n\n```ruby\n# config/initializers/handcuffs.rb\n\nHandcuffs.configure do |config|\n  # pre_restart migrations will/must run before post_restart migrations\n  config.phases = [:pre_restart, :post_restart]\n  config.default_phase = :pre_restart\nend\n```\n\nIf you have more complex or asynchrous workflows, you can use an alternate hash notation that allows prerequisite stages to be specified explicitly:\n\n```ruby\n# config/initializers/handcuffs.rb\n\nHandcuffs.configure do |config|\n  config.phases = {\n    # Prevent running post_restart migrations if there are outstanding\n    # pre_restart migrations\n    post_restart: [:pre_restart],\n    # Require pre_restarts before data_migrations, but do not enforce ordering\n    # between data_migrations and post_restarts\n    data_migrations: [:pre_restart],\n    # pre_restarts have no prerequisite phases\n    pre_restart: []\n  }\nend\n```\nThe default phase order in this case is determined by [Tsort](https://github.com/ruby/tsort) (topological sort). In order to validate the configuration and expected phase order it is recommended that you check the phase configuration after any changes using the rake task:\n\n```ruby\nrake handcuffs:phase_order\n```\n\nThis will display the default order in which phases will be run and list the prerequisites of each phase. It will raise an error if there are any circular dependencies or if any prerequisite is not a valid phase name.\n\n### Tagging Migrations\n\nOnce configured, you can assign each migration to one of the defined phases using the `phase` setter method:\n\n```ruby\n# db/migrate/20240318230933_add_on_sale_column.rb\n\nclass AddOnSaleColumn \u003c ActiveRecord::Migration[7.0]\n\n  phase :pre_restart\n\n  def change\n    add_column :products, :on_sale, :boolean\n  end\nend\n```\n\n```ruby\n# db/migrate/20240318230988_add_on_sale_index\n\nclass AddOnSaleIndex \u003c ActiveRecord::Migration[7.0]\n\n  phase :post_restart\n\n  def change\n    add_index :products, :on_sale, algorithm: :concurrently\n  end\nend\n```\n### Running Migrations In Phases\n\nAfter Handcuffs is configured and migrations are properly tagged, you can then run migrations in phases using the `handcuffs:migrate` rake task with the specific phase to be run:\n\n```bash\nrake 'handcuffs:migrate[pre_restart]'\n```\n\nor\n\n```bash\nrake 'handcuffs:migrate[post_restart]'\n```\n\n*Note:* If you run phases out of order, or attempt to run a phase before outstanding migrations with a prerequisite phase have been run, a `HandcuffsPhaseOutOfOrderError` will be raised.\n\n### Running All Migrations\n\nIn CI and local developement you may want to run all phases at one time.\n\nHandcuffs offers a single command that will run all migrations in phases and in the configured order:\n\n```bash\nrake 'handcuffs:migrate[all]'\n```\n\nThis differs from running `rake db:migrate` in that migrations will be run in batches corresponding to the _order that the phases are defined in the handcuffs config_. Again, you can use `rake handcuffs:phase_order` to preview the order ahead of time.\n\nOf course, you can always run `rake db:migrate` at any time to run all migrations using the Rails default ordering and without regard to Handcuffs phase if you wish.\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at \u003chttps://github.com/procore-oss/handcuffs\u003e. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.\n\n## Running Tests Locally\n\nThe specs for handcuffs are in the dummy application at `/spec/dummy/spec`. The spec suite requires PostgreSQL. To run it you will have to set the environment variables `POSTGRES_DB_USERNAME` and `POSTGRES_DB_PASSWORD`.\n\nWe use [appraisal](https://github.com/thoughtbot/appraisal) to run our test suite against all Rails versions that we support, as a means of quickly identifying potential regressions. To do this locally, first run `bundle exec appraisal install` to ensure all required dependencies are setup, and then run `bundle exec appraisal rspec`.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n\n## About Procore\n\n\u003cimg\n  src=\"https://raw.githubusercontent.com/procore-oss/.github/main/procorelightlogo.png\"\n  alt=\"Procore Open Source\"\n  width=\"250px\"\n/\u003e\n\nHandcuffs is maintained by Procore Technologies.\n\nProcore - building the software that builds the world.\n\nLearn more about the #1 most widely used construction management software at [procore.com](https://www.procore.com/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprocore-oss%2Fhandcuffs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprocore-oss%2Fhandcuffs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprocore-oss%2Fhandcuffs/lists"}