rails-worktree 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +113 -98
- data/lib/rails-worktree.rb +2 -0
- data/lib/worktree/cli.rb +76 -55
- data/lib/worktree/clone_database.rb +23 -0
- data/lib/worktree/commands/close.rb +71 -56
- data/lib/worktree/commands/create.rb +9 -24
- data/lib/worktree/commands/init.rb +240 -123
- data/lib/worktree/config.rb +114 -0
- data/lib/worktree/context.rb +7 -6
- data/lib/worktree/copy_sqlite.rb +7 -0
- data/lib/worktree/database_probe.rb +27 -0
- data/lib/worktree/installer.rb +151 -4
- data/lib/worktree/managed_block.rb +32 -0
- data/lib/worktree/push_hook.rb +15 -2
- data/lib/worktree/railtie.rb +3 -5
- data/lib/worktree/tasks.rb +5 -4
- data/lib/worktree/version.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2513c2eb6cd2e0d754ecdfb27482038c94204584a4b7edf076c15f36e9e7c9d8
|
|
4
|
+
data.tar.gz: cde0e84643776ce5e030c62ae4f6d5fd59635b6923b1401abddd660747c4dfdd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6b54cce1aea73f3e20c7c7e560395121fd984e2e848b671ffd7385291e566bc825e95eb7880c1d438b30a8fddb6cec59fb9b370e7f91b617972c6d616157a56b
|
|
7
|
+
data.tar.gz: ec8c82d608897e3addafec37509703a6a4529eb7edd65017d5383c355c98db66d3a7278533e0cf8ca979019b5e871f0c768f80731fa88ce5266e75204207175d
|
data/README.md
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
# Rails Worktree
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Provisions git worktrees for Rails projects: configuration files, node_modules and optional
|
|
4
|
+
isolated databases, in a checkout created by `bin/worktree`, plain git, herdr, Claude Code or Codex.
|
|
4
5
|
|
|
5
6
|
## Features
|
|
6
7
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
8
|
+
- `bin/worktree init` prepares any linked worktree at one of three levels, and is safe to repeat:
|
|
9
|
+
- `minimal`: copies gitignored configuration from the main checkout, creates directories, links `node_modules`.
|
|
10
|
+
- `test`: minimal plus your isolated test setup command, or an own test database.
|
|
11
|
+
- `full`: minimal plus own development and test databases. The development database is cloned
|
|
12
|
+
from the main one with `CREATE DATABASE ... TEMPLATE` (PostgreSQL), so it is ready in seconds.
|
|
13
|
+
- `bin/worktree create NAME BASE` creates a worktree under `.worktrees/` from an explicit base and provisions it.
|
|
14
|
+
- `bin/worktree install` writes the integration files other tools read: `.worktreeinclude` for Claude Code,
|
|
15
|
+
a `## Worktrees` block in `AGENTS.md`/`CLAUDE.md` for coding agents, and a herdr plugin.
|
|
16
|
+
- `exec` and `verify` run commands with worktree-local Bundler and binstubs and check the recorded base.
|
|
17
|
+
- `close` drops the worktree-local databases, removes the checkout and deletes its branch.
|
|
14
18
|
|
|
15
19
|
## Installation
|
|
16
20
|
|
|
@@ -22,126 +26,137 @@ group :development do
|
|
|
22
26
|
end
|
|
23
27
|
```
|
|
24
28
|
|
|
25
|
-
Run `bundle install`.
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
Run `bundle install`. When Rails loads in development the gem installs `bin/worktree` and the
|
|
30
|
+
integration files below. The same happens with `bin/worktree install` or `bundle exec rake worktree:install`
|
|
31
|
+
(`--force` / `FORCE=1` also replaces an existing launcher).
|
|
28
32
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
| File | Purpose |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| `bin/worktree` | Launcher that pins `BUNDLE_GEMFILE` to its own checkout before Ruby starts |
|
|
36
|
+
| `.gitignore` | Adds `/.worktrees/` |
|
|
37
|
+
| `.worktree.yml` | Project configuration (written once with the defaults, then yours) |
|
|
38
|
+
| `.worktreeinclude` | Generated from the `copy` list; Claude Code copies these files into every worktree it creates |
|
|
39
|
+
| `AGENTS.md`, `CLAUDE.md` | A marked `## Worktrees` block that tells agents to run `bin/worktree init`; a symlinked pair is updated once |
|
|
40
|
+
| `.worktree/herdr/` | A herdr plugin that runs `bin/worktree init --level minimal` on `worktree.created`; link it once with `herdr plugin link .worktree/herdr` |
|
|
33
41
|
|
|
34
|
-
|
|
42
|
+
Commit `.worktree.yml`, `.worktreeinclude`, the agents block and `.worktree/herdr/`. They are project configuration.
|
|
35
43
|
|
|
36
|
-
|
|
44
|
+
## Configuration
|
|
37
45
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
46
|
+
`.worktree.yml` in the repository root, all keys optional:
|
|
47
|
+
|
|
48
|
+
```yaml
|
|
49
|
+
level: full # default level for create and init
|
|
50
|
+
copy: # gitignored files to copy from the main checkout (globs)
|
|
51
|
+
- .env
|
|
52
|
+
- .env.*
|
|
53
|
+
- config/database.yml
|
|
54
|
+
- config/master.key
|
|
55
|
+
- config/credentials/*.key
|
|
56
|
+
- Procfile.dev
|
|
57
|
+
directories: [tmp/pids] # created in every provisioned checkout
|
|
58
|
+
touch: [tmp/caching-dev.txt] # touched in every provisioned checkout
|
|
59
|
+
node_modules: link # link (symlink to the main checkout), copy or skip
|
|
60
|
+
database:
|
|
61
|
+
development_variable: DATABASE_NAME # written to .env.development.local
|
|
62
|
+
test_variable: DATABASE_NAME # written to .env.test.local
|
|
63
|
+
template: true # clone the main development database (PostgreSQL TEMPLATE, SQLite copy)
|
|
64
|
+
template_max_gb: 5 # larger source databases load the schema instead
|
|
65
|
+
test_command: bin/agent-test-setup # --level test runs this instead of creating a test database
|
|
41
66
|
```
|
|
42
67
|
|
|
43
|
-
|
|
44
|
-
commit resolved from that reference is recorded; creation does not fetch or change your base branch.
|
|
45
|
-
Worktrees are created under `.worktrees/` in the repository root, including when invoked from a subdirectory.
|
|
68
|
+
### Databases
|
|
46
69
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
- Copied configuration files (`.env`, `database.yml`, `Procfile.dev`, credentials)
|
|
50
|
-
- Copied `node_modules`
|
|
51
|
-
- Migrated and seeded databases
|
|
70
|
+
Before it touches a database, `init` asks Rails how `config/database.yml` resolves in the worktree and
|
|
71
|
+
picks one of three paths:
|
|
52
72
|
|
|
53
|
-
|
|
73
|
+
- **SQLite files inside the checkout** (the `rails new` default, `storage/development.sqlite3`): every
|
|
74
|
+
checkout already has its own files. `full` copies the main development database into the worktree
|
|
75
|
+
(subject to `template` and `template_max_gb`) and prepares the test database; `test` prepares only the
|
|
76
|
+
test database. No `.env.*.local` files are written and `close` simply removes them with the checkout.
|
|
77
|
+
- **A database name read from the configured variable, with dotenv loaded**: the names go into
|
|
78
|
+
`.env.development.local` and `.env.test.local` and the databases are created as described below.
|
|
79
|
+
- **Anything else**, such as a fresh `rails new -d postgresql` app with fixed names: `init` warns and
|
|
80
|
+
skips the database setup, so the checkout shares the main databases and nothing in them is changed.
|
|
54
81
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
bin/worktree exec -- bundle exec rspec
|
|
58
|
-
bin/worktree exec -- bin/rails db:migrate
|
|
59
|
-
bin/worktree verify
|
|
60
|
-
```
|
|
82
|
+
To give PostgreSQL or MySQL worktrees their own databases, add `gem "dotenv-rails", groups: [:development, :test]`
|
|
83
|
+
and read the name from the configured variable in `config/database.yml`:
|
|
61
84
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
85
|
+
```yaml
|
|
86
|
+
development:
|
|
87
|
+
<<: *default
|
|
88
|
+
database: <%= ENV.fetch("DATABASE_NAME", "myapp_development") %>
|
|
89
|
+
test:
|
|
90
|
+
<<: *default
|
|
91
|
+
database: <%= ENV.fetch("DATABASE_NAME", "myapp_test") %>
|
|
92
|
+
```
|
|
68
93
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
94
|
+
Database names are `<prefix>_<worktree>_development` and `<prefix>_<worktree>_test`. The prefix comes from
|
|
95
|
+
`config/database.yml` in the main checkout, the worktree name from the directory, with other characters
|
|
96
|
+
replaced by `_`. The names go into `.env.development.local` and `.env.test.local`, which dotenv loads per
|
|
97
|
+
environment and which stay out of git. `database.yml` is copied unchanged. An application without
|
|
98
|
+
`db/schema.rb` or `db/structure.sql` yet gets `db:migrate` instead of `db:schema:load`.
|
|
74
99
|
|
|
75
|
-
|
|
76
|
-
so an inherited `RUBYOPT=-rbundler/setup` cannot activate the previous checkout's bundle first.
|
|
77
|
-
Both automatic installation and the Rake installer use the same generator. To replace an existing
|
|
78
|
-
launcher in a project after updating the gem:
|
|
100
|
+
## Usage
|
|
79
101
|
|
|
80
102
|
```bash
|
|
81
|
-
|
|
103
|
+
git fetch origin
|
|
104
|
+
bin/worktree create feature origin/main # full provisioning
|
|
105
|
+
bin/worktree create spike origin/main --level minimal # shares the main development database
|
|
106
|
+
bin/worktree create feature origin/main --seed # also run db:seed
|
|
107
|
+
|
|
108
|
+
# In a checkout another tool created (herdr, claude --worktree, Codex, git worktree add)
|
|
109
|
+
bin/worktree init # level from .worktree.yml
|
|
110
|
+
bin/worktree init --level test # plus the isolated test setup
|
|
111
|
+
bin/worktree init --base origin/main # also record the base and install the push guard
|
|
112
|
+
bin/worktree init --force # overwrite copied files with the main checkout's
|
|
113
|
+
|
|
114
|
+
bin/worktree close feature # from the main checkout, by directory or branch name
|
|
115
|
+
bin/worktree close # from inside the worktree
|
|
82
116
|
```
|
|
83
117
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
### Verify before pushing
|
|
118
|
+
The base for `create` is required and the resolved commit is recorded; creation does not fetch or change
|
|
119
|
+
your base branch. If the template clone cannot run, because the main development database is in use or larger
|
|
120
|
+
than `template_max_gb`, `init` creates the database and loads the schema instead and says so.
|
|
89
121
|
|
|
90
|
-
|
|
91
|
-
`
|
|
92
|
-
project files. The recorded commit remains fixed even if the base reference advances.
|
|
122
|
+
`close` drops only databases named in the worktree's `.env.development.local` and `.env.test.local`.
|
|
123
|
+
A `minimal` checkout shares the main databases and they stay untouched.
|
|
93
124
|
|
|
94
|
-
|
|
95
|
-
`core.hooksPath`. An existing hook is preserved as `pre-push.before-rails-worktree` and runs after
|
|
96
|
-
verification with the same arguments and input. Its exit status is preserved. Repeated installation
|
|
97
|
-
is idempotent; an unexpected existing backup causes an actionable error instead of overwriting it.
|
|
98
|
-
|
|
99
|
-
In managed worktrees the hook checks the branch, base ancestry, and bundle root, then prints the
|
|
100
|
-
change summary against the recorded base. Non-deletion updates must push the current HEAD; push
|
|
101
|
-
other branches from their own checkout. In checkouts without metadata, the wrapper only runs the
|
|
102
|
-
previous hook. Git hooks are shared by worktrees unless configured otherwise.
|
|
103
|
-
|
|
104
|
-
A branch mismatch tells you to switch back; a base mismatch tells you to rebase onto the recorded
|
|
105
|
-
base. These checks cannot decide whether you chose the right base or whether a change belongs in
|
|
106
|
-
the feature. Review the printed diff before publishing. Git's `--no-verify` still bypasses hooks.
|
|
107
|
-
|
|
108
|
-
### Close a worktree
|
|
125
|
+
## Verified execution
|
|
109
126
|
|
|
110
127
|
```bash
|
|
111
|
-
|
|
112
|
-
bin/worktree --
|
|
128
|
+
cd .worktrees/feature
|
|
129
|
+
bin/worktree exec -- bundle exec rspec
|
|
130
|
+
bin/worktree verify
|
|
113
131
|
```
|
|
114
132
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
133
|
+
`exec` runs from the worktree root with `BUNDLE_GEMFILE` pinned to this checkout, clears inherited Bundler
|
|
134
|
+
state and Git directory overrides, removes other repositories' `bin` directories from PATH and puts this
|
|
135
|
+
checkout's `bin` first. It rejects binstubs that resolve into another repository. It needs the metadata
|
|
136
|
+
that `create` or `init --base` records in the private `rails-worktree.json` under `git rev-parse --git-dir`;
|
|
137
|
+
checkouts without it skip the guard.
|
|
118
138
|
|
|
119
|
-
|
|
139
|
+
`create` and `init --base` also install a pre-push wrapper at Git's configured hook location. An existing
|
|
140
|
+
hook is preserved as `pre-push.before-rails-worktree` and runs after verification. In managed worktrees the
|
|
141
|
+
hook checks the branch, base ancestry and bundle root, then prints the change summary against the recorded
|
|
142
|
+
base. Git's `--no-verify` still bypasses hooks.
|
|
120
143
|
|
|
121
|
-
|
|
122
|
-
|
|
144
|
+
If `core.hooksPath` points at a directory that Git tracks (for example `script/hooks`), the gem does not
|
|
145
|
+
write there and prints a warning instead. Call the check from your own pre-push hook:
|
|
123
146
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
```yaml
|
|
127
|
-
development:
|
|
128
|
-
<<: *default
|
|
129
|
-
database: <%= ENV.fetch("DATABASE_NAME_DEVELOPMENT", "myapp_development") %>
|
|
130
|
-
test:
|
|
131
|
-
<<: *default
|
|
132
|
-
database: <%= ENV.fetch("DATABASE_NAME_TEST", "myapp_test") %>
|
|
147
|
+
```sh
|
|
148
|
+
bin/worktree verify --push || exit 1
|
|
133
149
|
```
|
|
134
150
|
|
|
135
|
-
|
|
136
|
-
1. Sets these variables in the worktree's `.env` file
|
|
137
|
-
2. Updates the worktree's `database.yml` to use them
|
|
138
|
-
3. Creates both development and test databases with unique names
|
|
151
|
+
It reads the pushed refs from standard input and does nothing in checkouts without worktree metadata.
|
|
139
152
|
|
|
140
153
|
## Requirements
|
|
141
154
|
|
|
142
|
-
- Ruby >=
|
|
143
|
-
- Rails project with
|
|
144
|
-
-
|
|
155
|
+
- Ruby >= 3.0
|
|
156
|
+
- Rails project with `bin/rails`
|
|
157
|
+
- For own databases: SQLite files in the checkout, or dotenv and a `database.yml` that reads the configured
|
|
158
|
+
variable (see Databases)
|
|
159
|
+
- PostgreSQL for template cloning; other server databases fall back to `db:create` and `db:schema:load`
|
|
145
160
|
|
|
146
161
|
## License
|
|
147
162
|
|
data/lib/rails-worktree.rb
CHANGED
data/lib/worktree/cli.rb
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
module RailsWorktree
|
|
2
2
|
class CLI
|
|
3
|
+
USAGE = <<~USAGE.freeze
|
|
4
|
+
Usage: worktree create <name> <base> [--level LEVEL] [--seed]
|
|
5
|
+
worktree <name> <base> [--level LEVEL] [--seed]
|
|
6
|
+
worktree init [name] [--level LEVEL] [--seed] [--base REF] [--force]
|
|
7
|
+
worktree close [name]
|
|
8
|
+
worktree exec -- <command> [arguments]
|
|
9
|
+
worktree verify [--push]
|
|
10
|
+
worktree install [--force]
|
|
11
|
+
|
|
12
|
+
Commands:
|
|
13
|
+
create NAME BASE Create .worktrees/NAME from BASE and provision it
|
|
14
|
+
init [NAME] Provision the current checkout, whoever created it (safe to repeat)
|
|
15
|
+
close [NAME] Drop the worktree-local databases, remove the checkout and its branch
|
|
16
|
+
exec -- COMMAND Run a command with verified worktree context
|
|
17
|
+
verify Check the recorded base and bundle root
|
|
18
|
+
install Write bin/worktree, .worktree.yml, .worktreeinclude, the AGENTS.md block
|
|
19
|
+
and the herdr plugin into this repository
|
|
20
|
+
|
|
21
|
+
Options:
|
|
22
|
+
--level LEVEL minimal (config files), test (plus test setup) or full (plus own databases).
|
|
23
|
+
Default: the level in .worktree.yml, or full
|
|
24
|
+
--seed Seed the development database (full level)
|
|
25
|
+
--base REF Record REF as the base and install the push guard (init)
|
|
26
|
+
--force Overwrite copied files (init) or the launcher and plugin files (install)
|
|
27
|
+
|
|
28
|
+
Examples:
|
|
29
|
+
worktree create feature origin/main
|
|
30
|
+
worktree create spike origin/main --level minimal
|
|
31
|
+
worktree init --level minimal # inside a herdr, Claude Code or Codex checkout
|
|
32
|
+
worktree close feature
|
|
33
|
+
USAGE
|
|
34
|
+
|
|
3
35
|
def self.run(args)
|
|
4
36
|
new(args).run
|
|
5
37
|
end
|
|
@@ -9,33 +41,33 @@ module RailsWorktree
|
|
|
9
41
|
end
|
|
10
42
|
|
|
11
43
|
def run
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
case @args.first
|
|
44
|
+
command = @args.shift
|
|
45
|
+
case command
|
|
46
|
+
when nil, "--help", "-h", "help"
|
|
47
|
+
puts USAGE
|
|
48
|
+
exit 1 if command.nil?
|
|
18
49
|
when "exec"
|
|
19
|
-
|
|
50
|
+
Commands::Exec.new(@args).run
|
|
20
51
|
when "verify"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
52
|
+
Context.new.verify(push: @args.include?("--push"))
|
|
53
|
+
when "install"
|
|
54
|
+
positional, options = parse_options
|
|
55
|
+
raise Error, "install takes no arguments" unless positional.empty?
|
|
56
|
+
Installer.install(Context.new.root, replace: options[:force], quiet: false)
|
|
57
|
+
puts "✓ rails-worktree is installed. Link the herdr plugin once with: herdr plugin link #{Installer::HERDR_DIR}"
|
|
58
|
+
when "create"
|
|
59
|
+
positional, options = parse_options
|
|
60
|
+
Commands::Create.new(positional, level: options[:level], seed: options[:seed]).run
|
|
61
|
+
when "init", "--init"
|
|
62
|
+
positional, options = parse_options
|
|
63
|
+
Commands::Init.new(positional, level: options[:level], seed: options[:seed], base: options[:base], force: options[:force]).run
|
|
64
|
+
when "close", "--close"
|
|
65
|
+
positional, _options = parse_options
|
|
66
|
+
Commands::Close.new(positional).run
|
|
36
67
|
else
|
|
37
|
-
|
|
38
|
-
|
|
68
|
+
@args.unshift(command)
|
|
69
|
+
positional, options = parse_options
|
|
70
|
+
Commands::Create.new(positional, level: options[:level], seed: options[:seed]).run
|
|
39
71
|
end
|
|
40
72
|
rescue Error => error
|
|
41
73
|
warn "Error: #{error.message}"
|
|
@@ -44,37 +76,26 @@ module RailsWorktree
|
|
|
44
76
|
|
|
45
77
|
private
|
|
46
78
|
|
|
47
|
-
def
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
Arguments:
|
|
69
|
-
<name> Name of the worktree (required)
|
|
70
|
-
<base-branch> Explicit branch or commit to create worktree from (required)
|
|
71
|
-
|
|
72
|
-
Examples:
|
|
73
|
-
worktree feature-branch origin/main # Create from an explicit base
|
|
74
|
-
worktree feature-branch origin/main --skip-seeds # Create without seeding database
|
|
75
|
-
worktree --close feature-branch # Close worktree from main repo
|
|
76
|
-
worktree --close # Close worktree from within it
|
|
77
|
-
USAGE
|
|
79
|
+
def parse_options
|
|
80
|
+
positional = []
|
|
81
|
+
options = {seed: false, force: false}
|
|
82
|
+
until @args.empty?
|
|
83
|
+
argument = @args.shift
|
|
84
|
+
case argument
|
|
85
|
+
when "--seed" then options[:seed] = true
|
|
86
|
+
when "--skip-seeds" then options[:seed] = false
|
|
87
|
+
when "--force" then options[:force] = true
|
|
88
|
+
when /\A--(level|base)(?:=(.*))?\z/
|
|
89
|
+
value = Regexp.last_match(2) || @args.shift
|
|
90
|
+
raise Error, "#{argument} needs a value" if value.nil? || value.start_with?("--")
|
|
91
|
+
options[Regexp.last_match(1).to_sym] = value
|
|
92
|
+
when /\A--/
|
|
93
|
+
raise Error, "Unknown option #{argument}"
|
|
94
|
+
else
|
|
95
|
+
positional << argument
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
[positional, options]
|
|
78
99
|
end
|
|
79
100
|
end
|
|
80
101
|
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Runs inside `bin/rails runner` in the worktree, without the worktree database
|
|
2
|
+
# override, so the configured development database is the main checkout's.
|
|
3
|
+
# Usage: bin/rails runner clone_database.rb TARGET
|
|
4
|
+
# RAILS_WORKTREE_TEMPLATE_MAX_BYTES caps the source size; a larger database is not cloned.
|
|
5
|
+
target = ARGV.fetch(0)
|
|
6
|
+
max_bytes = ENV.fetch("RAILS_WORKTREE_TEMPLATE_MAX_BYTES", "0").to_i
|
|
7
|
+
config = ActiveRecord::Base.connection_db_config.configuration_hash
|
|
8
|
+
source = config[:database]
|
|
9
|
+
abort "Template cloning needs PostgreSQL (adapter #{config[:adapter]})." unless config[:adapter].to_s.include?("postg")
|
|
10
|
+
abort "Source and target databases are the same: #{source}" if source == target
|
|
11
|
+
|
|
12
|
+
ActiveRecord::Base.establish_connection(config.merge(database: "postgres"))
|
|
13
|
+
connection = ActiveRecord::Base.connection
|
|
14
|
+
if connection.select_value("SELECT 1 FROM pg_database WHERE datname = #{connection.quote(target)}")
|
|
15
|
+
puts "Database #{target} already exists"
|
|
16
|
+
else
|
|
17
|
+
size = connection.select_value("SELECT pg_database_size(#{connection.quote(source)})").to_i
|
|
18
|
+
if max_bytes.positive? && size > max_bytes
|
|
19
|
+
abort format("Source database %s is %.1f GB, above database.template_max_gb in .worktree.yml; not cloning.", source, size / 1024.0**3)
|
|
20
|
+
end
|
|
21
|
+
connection.execute("CREATE DATABASE #{connection.quote_table_name(target)} TEMPLATE #{connection.quote_table_name(source)}")
|
|
22
|
+
puts "Cloned #{source} into #{target}"
|
|
23
|
+
end
|