wordmove-ng 6.0.0
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 +7 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
- data/.github/ISSUE_TEMPLATE/config.yml +11 -0
- data/.github/stale.yml +18 -0
- data/.github/workflows/release.yml +100 -0
- data/.github/workflows/ruby.yml +41 -0
- data/.gitignore +21 -0
- data/.release-please-manifest.json +3 -0
- data/.rspec +2 -0
- data/.rubocop.yml +50 -0
- data/.rubocop_todo.yml +89 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.vscode/launch.json +73 -0
- data/CHANGELOG.md +78 -0
- data/CLAUDE.md +114 -0
- data/CONTRIBUTING.md +114 -0
- data/Gemfile +14 -0
- data/LICENSE +22 -0
- data/README.md +340 -0
- data/Rakefile +7 -0
- data/assets/images/wordmove-ng.png +0 -0
- data/bin/bundle +105 -0
- data/bin/bundler +17 -0
- data/bin/byebug +29 -0
- data/bin/coderay +29 -0
- data/bin/console +16 -0
- data/bin/htmldiff +29 -0
- data/bin/kwalify +29 -0
- data/bin/ldiff +29 -0
- data/bin/pry +29 -0
- data/bin/rake +29 -0
- data/bin/rspec +29 -0
- data/bin/rubocop +29 -0
- data/bin/ruby-parse +29 -0
- data/bin/ruby-rewrite +29 -0
- data/bin/rumoji +29 -0
- data/bin/setup +7 -0
- data/bin/thor +29 -0
- data/bin/wordmove-ng +10 -0
- data/deploy/deploy.sh +3 -0
- data/exe/wordmove-ng +6 -0
- data/lib/wordmove/assets/wordmove_schema_global.yml +16 -0
- data/lib/wordmove/assets/wordmove_schema_local.yml +31 -0
- data/lib/wordmove/assets/wordmove_schema_remote.yml +167 -0
- data/lib/wordmove/cli.rb +134 -0
- data/lib/wordmove/deployer/base.rb +356 -0
- data/lib/wordmove/deployer/ssh.rb +339 -0
- data/lib/wordmove/doctor/movefile.rb +112 -0
- data/lib/wordmove/doctor/mysql.rb +136 -0
- data/lib/wordmove/doctor/rsync.rb +27 -0
- data/lib/wordmove/doctor/ssh.rb +90 -0
- data/lib/wordmove/doctor/wpcli.rb +43 -0
- data/lib/wordmove/doctor.rb +67 -0
- data/lib/wordmove/environments_list.rb +68 -0
- data/lib/wordmove/exceptions.rb +10 -0
- data/lib/wordmove/generators/movefile.rb +50 -0
- data/lib/wordmove/generators/movefile.yml +104 -0
- data/lib/wordmove/generators/movefile_adapter.rb +188 -0
- data/lib/wordmove/guardian.rb +37 -0
- data/lib/wordmove/hook.rb +128 -0
- data/lib/wordmove/logger.rb +165 -0
- data/lib/wordmove/movefile.rb +129 -0
- data/lib/wordmove/prerequisites.rb +57 -0
- data/lib/wordmove/sql_adapter/wpcli.rb +72 -0
- data/lib/wordmove/ssh_runner.rb +110 -0
- data/lib/wordmove/version.rb +3 -0
- data/lib/wordmove/wordpress_directory/path.rb +11 -0
- data/lib/wordmove/wordpress_directory.rb +41 -0
- data/lib/wordmove-ng.rb +4 -0
- data/lib/wordmove.rb +51 -0
- data/release-please-config.json +25 -0
- data/wordmove-ng.gemspec +62 -0
- metadata +318 -0
data/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
Welcome to the contributor guide. If you can't find important information you're welcome
|
|
2
|
+
to edit this page or [open a discussion](https://github.com/tekgnosis-net/wordmove-ng/discussions/new?category=general) to talk with maintainers.
|
|
3
|
+
|
|
4
|
+
In this guide you'll find informations about:
|
|
5
|
+
* [Bug reporting](#bug-reporting)
|
|
6
|
+
* [Development](#development)
|
|
7
|
+
* [Maintainer tasks](#maintainer-tasks)
|
|
8
|
+
|
|
9
|
+
### Bug reporting
|
|
10
|
+
|
|
11
|
+
wordmove-ng is an hard piece of software to debug and it is used by many users with many
|
|
12
|
+
different environments - Windows also, even if it isn't officially supported by the dev team.
|
|
13
|
+
|
|
14
|
+
So *please*, follow the issue template when reporting a bug.
|
|
15
|
+
|
|
16
|
+
If you're not sure if you're standing in front of a bug, please [open a discussion](https://github.com/tekgnosis-net/wordmove-ng/discussions/new?category=general)
|
|
17
|
+
labeling it as "Triage", possibly using this template to report your problem (note: GH's discussions does not support templates ATM):
|
|
18
|
+
|
|
19
|
+
```markdown
|
|
20
|
+
**Describe the bug**
|
|
21
|
+
|
|
22
|
+
> A clear and concise description of what the bug is.
|
|
23
|
+
|
|
24
|
+
**Wordmove command**
|
|
25
|
+
|
|
26
|
+
> Command used on the CLI: (e.g.: `wordmove-ng pull --all --no-db`)
|
|
27
|
+
|
|
28
|
+
**Expected behavior**
|
|
29
|
+
|
|
30
|
+
> A clear and concise description of what you expected to happen.
|
|
31
|
+
|
|
32
|
+
**movefile.yml**
|
|
33
|
+
|
|
34
|
+
> Paste (removing personal data) the interesting part, if any, of your `movefile.yml` formatting it inside a code block with `yml` syntax and double checking the indentation.
|
|
35
|
+
|
|
36
|
+
**Exception/trace**
|
|
37
|
+
|
|
38
|
+
> Paste (removing personal data) the entire trace of error/exception you encountered, if any
|
|
39
|
+
|
|
40
|
+
**Environment (please complete the following information):**
|
|
41
|
+
|
|
42
|
+
- OS:
|
|
43
|
+
- Ruby: (`ruby --version`)
|
|
44
|
+
- wordmove-ng: (`wordmove-ng --version`)
|
|
45
|
+
|
|
46
|
+
**Doctor**
|
|
47
|
+
|
|
48
|
+
* [x] running the `wordmove-ng doctor` command returns all green
|
|
49
|
+
|
|
50
|
+
> (If it is not, report the error you got.)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
As a general advise: only the latest stable wordmove-ng release is supported. Bugs in the legacy `wordmove` 5.x gem belong to its own tracker.
|
|
54
|
+
We'd appreciate your help opening an in depth report if you'd find that an older version is working
|
|
55
|
+
better for you.
|
|
56
|
+
|
|
57
|
+
Thank you all for your support and for the love <3
|
|
58
|
+
|
|
59
|
+
### Development
|
|
60
|
+
|
|
61
|
+
#### Get Wordmove
|
|
62
|
+
|
|
63
|
+
* fork wordmove-ng
|
|
64
|
+
* clone your own repo
|
|
65
|
+
* be sure to check-out the right branch, usually `master`
|
|
66
|
+
|
|
67
|
+
##### Installing Ruby
|
|
68
|
+
|
|
69
|
+
To install ruby, please, use [rbenv](https://github.com/rbenv/rbenv) or [RVM](https://rvm.io).
|
|
70
|
+
|
|
71
|
+
##### Contribute
|
|
72
|
+
|
|
73
|
+
* run `bundle install` to install gem dependencies
|
|
74
|
+
* `git checkout -b my_feature_or_fix_name`
|
|
75
|
+
* code, commit, push and send a pull request on GitHub
|
|
76
|
+
|
|
77
|
+
> Never bump the version or edit CHANGELOG.md by hand: both are generated by
|
|
78
|
+
> release-please from the commit messages. Write [Conventional Commits](https://www.conventionalcommits.org):
|
|
79
|
+
> `fix: ...` (patch), `feat: ...` (minor), `feat!: ...` or a `BREAKING CHANGE:` footer (major),
|
|
80
|
+
> plus `docs:`, `chore:`, `ci:`, `test:`, `refactor:` for everything that does not ship a change.
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
##### Test Wordmove
|
|
84
|
+
|
|
85
|
+
Wordmove has a decent test coverage. We _require_ that pull requests does not break tests launched by the CI.
|
|
86
|
+
In order to launch tests on you dev machine
|
|
87
|
+
|
|
88
|
+
```fish
|
|
89
|
+
bundle exec rake
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The command will launch the test suite - written with RSpec - and rubocop. Ruby 3.0 or
|
|
93
|
+
newer is required; the CI matrix covers 3.0 through 4.0.
|
|
94
|
+
|
|
95
|
+
In order to use the gem locally you can install it
|
|
96
|
+
|
|
97
|
+
```fish
|
|
98
|
+
bundle exec rake install
|
|
99
|
+
wordmove-ng --version
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
or run the executable directly
|
|
103
|
+
|
|
104
|
+
```fish
|
|
105
|
+
bin/wordmove-ng --version
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Maintainer tasks
|
|
109
|
+
|
|
110
|
+
ToDo:
|
|
111
|
+
|
|
112
|
+
* [ ] versioning and version dumping
|
|
113
|
+
* [ ] changelog/release
|
|
114
|
+
* [ ] publishing the gem
|
data/Gemfile
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
source 'https://rubygems.org'
|
|
2
|
+
|
|
3
|
+
# Runtime dependencies live in wordmove-ng.gemspec
|
|
4
|
+
gemspec
|
|
5
|
+
|
|
6
|
+
group :development, :test do
|
|
7
|
+
gem "bundler", ">= 2.0", "< 5"
|
|
8
|
+
gem "priscilla", "~> 1.0"
|
|
9
|
+
gem "pry-byebug", "~> 3.1"
|
|
10
|
+
gem "rake", "~> 13.0"
|
|
11
|
+
gem "rspec", "~> 3.13"
|
|
12
|
+
gem "rubocop", "~> 1.90"
|
|
13
|
+
gem "simplecov", ">= 0.22", "< 2"
|
|
14
|
+
end
|
data/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2013-2019 weLaika Soc. Coop. (Wordmove)
|
|
4
|
+
Copyright (c) 2026 Kumar, tekgnosis.net (wordmove-ng changes)
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
# wordmove-ng
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
wordmove-ng moves WordPress sites between environments. One command pushes or pulls
|
|
6
|
+
the core, uploads, themes, plugins, mu-plugins, languages and the database between a
|
|
7
|
+
local install and any number of remote hosts over SSH, rewriting URLs and paths on the
|
|
8
|
+
target with wp-cli.
|
|
9
|
+
|
|
10
|
+
It is the maintained, independent successor of [Wordmove](https://github.com/welaika/wordmove)
|
|
11
|
+
by weLaika, continued through the [kokiddp fork](https://github.com/kokiddp/wordmove).
|
|
12
|
+
The movefile format, commands and flags are unchanged. What changed is under the hood, and
|
|
13
|
+
in a few places where the old behaviour was unsafe. See [Upgrading from wordmove 5.x](#upgrading-from-wordmove-5x).
|
|
14
|
+
|
|
15
|
+
[](https://github.com/tekgnosis-net/wordmove-ng/actions/workflows/ruby.yml)
|
|
16
|
+
|
|
17
|
+
## Highlights
|
|
18
|
+
|
|
19
|
+
- **No Ruby-side SSH.** Every remote operation goes through the system `ssh`, `scp` and
|
|
20
|
+
`rsync`, so your agent, `~/.ssh/config`, jump hosts and modern key types all just work,
|
|
21
|
+
and nothing breaks on a new OpenSSL or Ruby.
|
|
22
|
+
- **The source database is never written.** Push and pull both dump the source, import on
|
|
23
|
+
the target and run `wp search-replace` on the target.
|
|
24
|
+
- **Fails before it breaks anything.** Prerequisites on both sides are probed before any
|
|
25
|
+
backup, dump or import. `wordmove-ng doctor` checks SSH authentication and remote programs
|
|
26
|
+
per environment.
|
|
27
|
+
- **Optional maintenance mode** around the target's database replacement.
|
|
28
|
+
- MariaDB first (`mariadb`, `mariadb-dump`) with MySQL fallback, sandbox-header handling,
|
|
29
|
+
collation and charset normalisation, socket and port support.
|
|
30
|
+
- Runs on Ruby 3.0 through 4.0.
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
Ruby 3.0 or newer is required.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
gem install wordmove-ng
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Until the gem is on rubygems.org, or to run the latest master:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
gem install specific_install
|
|
44
|
+
gem specific_install https://github.com/tekgnosis-net/wordmove-ng.git
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
From a checkout:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
bundle install
|
|
51
|
+
bin/wordmove-ng --help
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Quick start
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
cd /path/to/your/wordpress # where wp-config.php lives
|
|
58
|
+
wordmove-ng init # writes movefile.yml, pre-filled from wp-config.php
|
|
59
|
+
wordmove-ng doctor # validates movefile.yml, local tools, SSH auth, remote tools
|
|
60
|
+
wordmove-ng pull -e staging --all
|
|
61
|
+
wordmove-ng push -e production -t -p # themes and plugins only
|
|
62
|
+
wordmove-ng push -e production -d -s # simulate a database push
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Run `wordmove-ng help` for all commands and `wordmove-ng help push` for the flags.
|
|
66
|
+
Component flags: `-w` core, `-u` uploads, `-t` themes, `-p` plugins, `-m` mu-plugins,
|
|
67
|
+
`-l` languages, `-d` database, `--all` everything (`--all --no-uploads` to exclude one).
|
|
68
|
+
`-s` simulates, `-e` picks the environment, `-c` points at another movefile.
|
|
69
|
+
|
|
70
|
+
## Peer dependencies
|
|
71
|
+
|
|
72
|
+
wordmove-ng is orchestration glue around standard tools.
|
|
73
|
+
|
|
74
|
+
**Locally**
|
|
75
|
+
|
|
76
|
+
| Program | Needed for |
|
|
77
|
+
| --- | --- |
|
|
78
|
+
| `ssh`, `scp`, `rsync` | every remote operation |
|
|
79
|
+
| `sshpass` | only when `ssh.password` is set in the movefile |
|
|
80
|
+
| `gzip` | database sync |
|
|
81
|
+
| `mysqldump` or `mariadb-dump` | `push -d` (dumping the local database) |
|
|
82
|
+
| `mysql` or `mariadb` | `pull -d` (importing into the local database) and `doctor` |
|
|
83
|
+
| `wp` ([WP-CLI](https://wp-cli.org)) | `pull -d` (adapting the local database) |
|
|
84
|
+
|
|
85
|
+
**On each remote**, in the `$PATH` of a non interactive login
|
|
86
|
+
|
|
87
|
+
| Program | Needed for |
|
|
88
|
+
| --- | --- |
|
|
89
|
+
| `rsync`, `gzip` | file and database sync |
|
|
90
|
+
| `mysqldump` or `mariadb-dump` | `pull -d` |
|
|
91
|
+
| `mysql` or `mariadb`, `wp` | `push -d` |
|
|
92
|
+
|
|
93
|
+
`wordmove-ng doctor` reports exactly what is missing where, and every database operation
|
|
94
|
+
re-checks before doing anything.
|
|
95
|
+
|
|
96
|
+
## SSH authentication
|
|
97
|
+
|
|
98
|
+
Everything uses the system `ssh` client. When no `ssh.password` is configured, connections run
|
|
99
|
+
with `BatchMode=yes`: a failing key authentication is reported as an error with the exact
|
|
100
|
+
command that failed, never an interactive password prompt.
|
|
101
|
+
|
|
102
|
+
- `ssh.host`, `ssh.user`, `ssh.port` map to the obvious ssh options. A host configured in
|
|
103
|
+
`~/.ssh/config` works with only `ssh.host` set.
|
|
104
|
+
- `ssh.password` is passed through `sshpass` to `ssh`, `scp` and `rsync`. Keys are strongly
|
|
105
|
+
preferred.
|
|
106
|
+
- `ssh.gateway` becomes `ssh -J`. The jump host must accept your key or agent; a
|
|
107
|
+
`gateway.password` cannot be honoured and is ignored (doctor warns).
|
|
108
|
+
- `ssh.rsync_options` are appended to the rsync command.
|
|
109
|
+
|
|
110
|
+
Remote commands always run through `sh -c`, so the remote login shell may be bash, zsh,
|
|
111
|
+
fish or csh.
|
|
112
|
+
|
|
113
|
+
## Database sync
|
|
114
|
+
|
|
115
|
+
Both directions have the same shape: dump the source, import the dump on the target, run
|
|
116
|
+
`wp search-replace` on the target for `vhost` and `wordpress_path`. The source database is
|
|
117
|
+
only ever read.
|
|
118
|
+
|
|
119
|
+
- `wordmove-ng pull -d`: back up the local database, dump the remote, import locally,
|
|
120
|
+
search-replace locally.
|
|
121
|
+
- `wordmove-ng push -d`: back up the remote database, dump locally, import on the remote,
|
|
122
|
+
search-replace on the remote over SSH.
|
|
123
|
+
|
|
124
|
+
Backups are written to the local `wp-content/` directory as timestamped `.sql.gz` files
|
|
125
|
+
before any import. If the remote adaptation fails after the import, the log names the
|
|
126
|
+
backup to restore from.
|
|
127
|
+
|
|
128
|
+
`wp search-replace` runs with `--all-tables --skip-columns=guid`, so every table in the
|
|
129
|
+
target database is adapted, including non WordPress tables sharing it, and post GUIDs are
|
|
130
|
+
left alone as WordPress recommends. `--no-adapt` skips the search-replace entirely.
|
|
131
|
+
|
|
132
|
+
If one of the four search terms is a prefix of another (for example a local vhost of
|
|
133
|
+
`https://site.test` and a remote of `https://site.test.example.com`) the shorter replacement
|
|
134
|
+
also rewrites the longer value. Doctor reports this as an error and the DB step warns.
|
|
135
|
+
|
|
136
|
+
### Maintenance mode
|
|
137
|
+
|
|
138
|
+
```yaml
|
|
139
|
+
global:
|
|
140
|
+
maintenance_mode: true
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
wraps the target's import and search-replace in `wp maintenance-mode activate` and
|
|
144
|
+
`deactivate`, so visitors see the WordPress maintenance page rather than a site pointing at
|
|
145
|
+
the other environment's URLs for a few seconds. Deactivation runs even when the adaptation
|
|
146
|
+
fails. `WORDMOVE_MAINTENANCE_MODE=1` forces it on for a single run. Default: off.
|
|
147
|
+
|
|
148
|
+
### MariaDB and MySQL compatibility
|
|
149
|
+
|
|
150
|
+
- `mariadb` and `mariadb-dump` are preferred when present, falling back to `mysql` and
|
|
151
|
+
`mysqldump`.
|
|
152
|
+
- Dumps starting with the MariaDB sandbox header (`/*!999999- enable the sandbox mode */`)
|
|
153
|
+
import cleanly on older servers.
|
|
154
|
+
- Imports run with `--binary-mode`, `SET FOREIGN_KEY_CHECKS=0` and a trailing `COMMIT;`,
|
|
155
|
+
unless `mysql_options` already sets binary mode.
|
|
156
|
+
- `database.socket` and `database.port` are first-class; `--socket` inside
|
|
157
|
+
`mysql_options` or `mysqldump_options` still works.
|
|
158
|
+
- Newer `utf8mb3` collations are rewritten to `utf8mb4_unicode_ci` and `utf8mb3` to
|
|
159
|
+
`utf8mb4` before import. Override or extend the mappings:
|
|
160
|
+
|
|
161
|
+
```yaml
|
|
162
|
+
global:
|
|
163
|
+
collation_fallbacks:
|
|
164
|
+
utf8mb3_uca1400_ai_ci: utf8mb4_unicode_ci
|
|
165
|
+
charset_fallbacks:
|
|
166
|
+
utf8mb3: utf8mb4
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
- `wp` is always called with `--allow-root`, so root-owned Docker installs work.
|
|
170
|
+
|
|
171
|
+
## `movefile.yml`
|
|
172
|
+
|
|
173
|
+
```yaml
|
|
174
|
+
global:
|
|
175
|
+
maintenance_mode: false
|
|
176
|
+
|
|
177
|
+
local:
|
|
178
|
+
vhost: http://vhost.local
|
|
179
|
+
wordpress_path: /home/john/sites/your_site
|
|
180
|
+
|
|
181
|
+
database:
|
|
182
|
+
name: database_name
|
|
183
|
+
user: user
|
|
184
|
+
password: password
|
|
185
|
+
host: localhost
|
|
186
|
+
# port: 3306
|
|
187
|
+
# socket: /path/to/mysql.sock
|
|
188
|
+
|
|
189
|
+
production:
|
|
190
|
+
vhost: https://example.com
|
|
191
|
+
wordpress_path: /var/www/your_site
|
|
192
|
+
|
|
193
|
+
database:
|
|
194
|
+
name: database_name
|
|
195
|
+
user: user
|
|
196
|
+
password: password
|
|
197
|
+
host: host
|
|
198
|
+
# mysqldump_options: --max_allowed_packet=50MB
|
|
199
|
+
# mysql_options: --protocol=TCP
|
|
200
|
+
|
|
201
|
+
exclude:
|
|
202
|
+
- ".git/"
|
|
203
|
+
- ".env"
|
|
204
|
+
- "node_modules/"
|
|
205
|
+
- "wp-config.php"
|
|
206
|
+
- "wp-content/*.sql.gz"
|
|
207
|
+
|
|
208
|
+
ssh:
|
|
209
|
+
host: host
|
|
210
|
+
user: user
|
|
211
|
+
# port: 22
|
|
212
|
+
# password: only with sshpass installed; prefer keys
|
|
213
|
+
# rsync_options: --verbose
|
|
214
|
+
# gateway:
|
|
215
|
+
# host: bastion.example.com
|
|
216
|
+
# user: jump
|
|
217
|
+
|
|
218
|
+
# forbid:
|
|
219
|
+
# push:
|
|
220
|
+
# db: true # never push the database to production
|
|
221
|
+
# hooks:
|
|
222
|
+
# push:
|
|
223
|
+
# before:
|
|
224
|
+
# - command: echo "about to push"
|
|
225
|
+
# where: local
|
|
226
|
+
# after:
|
|
227
|
+
# - command: wp cache flush
|
|
228
|
+
# where: remote
|
|
229
|
+
# raise: false
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Every first-level key other than `global` and `local` is a remote environment; pick one with
|
|
233
|
+
`-e`. `movefile.yml` is evaluated as ERB, so secrets can come from the environment or from
|
|
234
|
+
a `.env` / `.env.<environment>` file next to it:
|
|
235
|
+
|
|
236
|
+
```yaml
|
|
237
|
+
production:
|
|
238
|
+
database:
|
|
239
|
+
password: "<%= ENV['PROD_DB_PASS'] %>"
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
File sync mirrors the source: files missing on the source are deleted on the destination.
|
|
243
|
+
Put anything you need to keep in `exclude`, which is always relative to `wordpress_path`.
|
|
244
|
+
|
|
245
|
+
The wiki has the full reference:
|
|
246
|
+
[movefile.yml configurations explained](https://github.com/tekgnosis-net/wordmove-ng/wiki/movefile.yml-configurations-explained),
|
|
247
|
+
[Usage and flags explained](https://github.com/tekgnosis-net/wordmove-ng/wiki/Usage-and-flags-explained),
|
|
248
|
+
[Multiple environments explained](https://github.com/tekgnosis-net/wordmove-ng/wiki/Multiple-environments-explained),
|
|
249
|
+
[Hooks](https://github.com/tekgnosis-net/wordmove-ng/wiki/Hooks).
|
|
250
|
+
Where a wiki page still describes wordmove 5.x behaviour, this README wins.
|
|
251
|
+
|
|
252
|
+
## Logging
|
|
253
|
+
|
|
254
|
+
Long generated shell scripts are summarised into intent lines such as
|
|
255
|
+
`dump database my_db to ./wp-content/dump.sql`, `compress ./wp-content/dump.sql`,
|
|
256
|
+
`wp search-replace old.test -> new.test in /var/www/site` and
|
|
257
|
+
`import SQL dump ... (strip sandbox header, append COMMIT)`. Passwords, hosts, vhosts and
|
|
258
|
+
paths from the movefile are masked as `[secret]` in all output, including hook output.
|
|
259
|
+
|
|
260
|
+
## Upgrading from wordmove 5.x
|
|
261
|
+
|
|
262
|
+
This applies to both the original `wordmove` gem and the kokiddp fork. Your `movefile.yml`
|
|
263
|
+
keeps working with the exceptions below.
|
|
264
|
+
|
|
265
|
+
1. **Install the new gem and use the new command.** `gem install wordmove-ng`, then call
|
|
266
|
+
`wordmove-ng` instead of `wordmove`. Both gems can stay installed side by side.
|
|
267
|
+
2. **Ruby 3.0 or newer.** Ruby 2.6 and 2.7 are no longer supported.
|
|
268
|
+
3. **Put `wp` on your remotes.** `push -d` now adapts the database on the remote, so WP-CLI
|
|
269
|
+
must be installed there and be in the `$PATH` of a non interactive login. It is a single
|
|
270
|
+
PHAR file: [installing WP-CLI](https://wp-cli.org/#installing). Run `wordmove-ng doctor`
|
|
271
|
+
to confirm.
|
|
272
|
+
4. **FTP and SFTP are gone.** A movefile with an `ftp` block fails with a clear error. FTP
|
|
273
|
+
only ever existed for shell-less shared hosts, where the database had to be handled by
|
|
274
|
+
uploading temporary PHP scripts and URLs were rewritten in the dump with a regex. Keep the
|
|
275
|
+
legacy `wordmove` 5.x gem for such hosts, or move them to SSH.
|
|
276
|
+
5. **`global.sql_adapter` is ignored.** URL and path adaptation always uses wp-cli on the
|
|
277
|
+
target. The key is still accepted, with a warning, so old movefiles validate; remove it
|
|
278
|
+
when convenient. `database.charset` (removed in 3.0) is no longer accepted by the schema.
|
|
279
|
+
6. **Push no longer touches your local database.** Old versions ran `wp search-replace`
|
|
280
|
+
on the live local database during a push and restored it afterwards, which left the local
|
|
281
|
+
site pointing at production if anything failed in between. That is gone.
|
|
282
|
+
7. **SSH behaves like rsync did.** If rsync worked but the database step asked for a
|
|
283
|
+
password, the cause was the old Net::SSH library, which could not sign RSA keys with
|
|
284
|
+
SHA-2. That path no longer exists. If key authentication fails now, you get an error with
|
|
285
|
+
the failing `ssh` command instead of a prompt. `ssh.gateway.password` is ignored.
|
|
286
|
+
8. **Remote programs are checked up front.** Missing `gzip`, `mysql`, `mysqldump` or `wp`
|
|
287
|
+
on either side aborts before any backup or dump, listing what is missing.
|
|
288
|
+
9. **New, optional:** `global.maintenance_mode` (see above).
|
|
289
|
+
|
|
290
|
+
## Contributing
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
bundle install
|
|
294
|
+
bundle exec rake # specs + rubocop, what CI runs
|
|
295
|
+
bundle exec rspec spec/deployer/ssh_db_spec.rb # one file
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
CI runs on Ruby 3.0, 3.1, 3.2, 3.3, 3.4 and 4.0. Please keep this README updated when
|
|
299
|
+
changing user-facing behaviour, and write [Conventional Commits](https://www.conventionalcommits.org)
|
|
300
|
+
(`feat:`, `fix:`, `feat!:` for breaking changes): the changelog and version number are
|
|
301
|
+
generated from them. See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
302
|
+
|
|
303
|
+
### Releasing
|
|
304
|
+
|
|
305
|
+
Releases are automated by [release-please](https://github.com/googleapis/release-please)
|
|
306
|
+
and published to [rubygems.org](https://rubygems.org/gems/wordmove-ng) with
|
|
307
|
+
[trusted publishing](https://guides.rubygems.org/trusted-publishing/), so no API key is
|
|
308
|
+
stored anywhere.
|
|
309
|
+
|
|
310
|
+
1. Every push to `master` updates a "release PR" that bumps `lib/wordmove/version.rb` and
|
|
311
|
+
`CHANGELOG.md` according to the commits since the last release.
|
|
312
|
+
2. Merging that PR creates the `vX.Y.Z` tag and the GitHub release.
|
|
313
|
+
3. The `publish` job in `.github/workflows/release.yml` then runs the test suite on the
|
|
314
|
+
tagged commit, builds the gem, pushes it to rubygems.org and attaches the `.gem` file to
|
|
315
|
+
the GitHub release.
|
|
316
|
+
|
|
317
|
+
Pushing a `v*` tag by hand at the head of `master` triggers the same publish job; that is
|
|
318
|
+
how 6.0.0 is cut.
|
|
319
|
+
|
|
320
|
+
One-time setup for a new maintainer or a fork:
|
|
321
|
+
|
|
322
|
+
- On rubygems.org, under your profile's *Trusted publishers*, add a **pending** publisher
|
|
323
|
+
for gem `wordmove-ng`, repository owner `tekgnosis-net`, repository `wordmove-ng`,
|
|
324
|
+
workflow `release.yml`, environment `release`. It becomes a regular publisher, and you
|
|
325
|
+
the gem owner, after the first push.
|
|
326
|
+
- In the GitHub repository settings create an environment named `release`, and under
|
|
327
|
+
*Actions → General* enable "Allow GitHub Actions to create and approve pull requests"
|
|
328
|
+
so release-please can open its PR.
|
|
329
|
+
|
|
330
|
+
## Credits and licence
|
|
331
|
+
|
|
332
|
+
- Wordmove was created and maintained for a decade by [weLaika](https://dev.welaika.com):
|
|
333
|
+
Stefano Verna, Ju Liu, Fabrizio Monti, Alessandro Fazzi, Filippo Gangi Dino and
|
|
334
|
+
[many contributors](https://github.com/welaika/wordmove/graphs/contributors). The
|
|
335
|
+
workflow, movefile format and most of the code are theirs.
|
|
336
|
+
- [kokiddp](https://github.com/kokiddp/wordmove) kept it running on modern Ruby, OpenSSL 3
|
|
337
|
+
and MariaDB and added socket, collation and Docker support.
|
|
338
|
+
- wordmove-ng is maintained by [tekgnosis.net](https://github.com/tekgnosis-net).
|
|
339
|
+
|
|
340
|
+
MIT licence, see [LICENSE](LICENSE).
|
data/Rakefile
ADDED
|
Binary file
|
data/bin/bundle
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# This file was generated by Bundler.
|
|
6
|
+
#
|
|
7
|
+
# The application 'bundle' is installed as part of a gem, and
|
|
8
|
+
# this file is here to facilitate running it.
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
require "rubygems"
|
|
12
|
+
|
|
13
|
+
m = Module.new do
|
|
14
|
+
module_function
|
|
15
|
+
|
|
16
|
+
def invoked_as_script?
|
|
17
|
+
File.expand_path($0) == File.expand_path(__FILE__)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def env_var_version
|
|
21
|
+
ENV["BUNDLER_VERSION"]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def cli_arg_version
|
|
25
|
+
return unless invoked_as_script? # don't want to hijack other binstubs
|
|
26
|
+
return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
|
|
27
|
+
bundler_version = nil
|
|
28
|
+
update_index = nil
|
|
29
|
+
ARGV.each_with_index do |a, i|
|
|
30
|
+
if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
|
|
31
|
+
bundler_version = a
|
|
32
|
+
end
|
|
33
|
+
next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
|
|
34
|
+
bundler_version = $1 || ">= 0.a"
|
|
35
|
+
update_index = i
|
|
36
|
+
end
|
|
37
|
+
bundler_version
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def gemfile
|
|
41
|
+
gemfile = ENV["BUNDLE_GEMFILE"]
|
|
42
|
+
return gemfile if gemfile && !gemfile.empty?
|
|
43
|
+
|
|
44
|
+
File.expand_path("../../Gemfile", __FILE__)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def lockfile
|
|
48
|
+
lockfile =
|
|
49
|
+
case File.basename(gemfile)
|
|
50
|
+
when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
|
|
51
|
+
else "#{gemfile}.lock"
|
|
52
|
+
end
|
|
53
|
+
File.expand_path(lockfile)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def lockfile_version
|
|
57
|
+
return unless File.file?(lockfile)
|
|
58
|
+
lockfile_contents = File.read(lockfile)
|
|
59
|
+
return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
|
|
60
|
+
Regexp.last_match(1)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def bundler_version
|
|
64
|
+
@bundler_version ||= begin
|
|
65
|
+
env_var_version || cli_arg_version ||
|
|
66
|
+
lockfile_version || "#{Gem::Requirement.default}.a"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def load_bundler!
|
|
71
|
+
ENV["BUNDLE_GEMFILE"] ||= gemfile
|
|
72
|
+
|
|
73
|
+
# must dup string for RG < 1.8 compatibility
|
|
74
|
+
activate_bundler(bundler_version.dup)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def activate_bundler(bundler_version)
|
|
78
|
+
if Gem::Version.correct?(bundler_version) && Gem::Version.new(bundler_version).release < Gem::Version.new("2.0")
|
|
79
|
+
bundler_version = "< 2"
|
|
80
|
+
end
|
|
81
|
+
gem_error = activation_error_handling do
|
|
82
|
+
gem "bundler", bundler_version
|
|
83
|
+
end
|
|
84
|
+
return if gem_error.nil?
|
|
85
|
+
require_error = activation_error_handling do
|
|
86
|
+
require "bundler/version"
|
|
87
|
+
end
|
|
88
|
+
return if require_error.nil? && Gem::Requirement.new(bundler_version).satisfied_by?(Gem::Version.new(Bundler::VERSION))
|
|
89
|
+
warn "Activating bundler (#{bundler_version}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_version}'`"
|
|
90
|
+
exit 42
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def activation_error_handling
|
|
94
|
+
yield
|
|
95
|
+
nil
|
|
96
|
+
rescue StandardError, LoadError => e
|
|
97
|
+
e
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
m.load_bundler!
|
|
102
|
+
|
|
103
|
+
if m.invoked_as_script?
|
|
104
|
+
load Gem.bin_path("bundler", "bundle")
|
|
105
|
+
end
|
data/bin/bundler
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
#
|
|
4
|
+
# This file was generated by Bundler.
|
|
5
|
+
#
|
|
6
|
+
# The application 'bundler' is installed as part of a gem, and
|
|
7
|
+
# this file is here to facilitate running it.
|
|
8
|
+
#
|
|
9
|
+
|
|
10
|
+
require "pathname"
|
|
11
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
|
12
|
+
Pathname.new(__FILE__).realpath)
|
|
13
|
+
|
|
14
|
+
require "rubygems"
|
|
15
|
+
require "bundler/setup"
|
|
16
|
+
|
|
17
|
+
load Gem.bin_path("bundler", "bundler")
|
data/bin/byebug
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# This file was generated by Bundler.
|
|
6
|
+
#
|
|
7
|
+
# The application 'byebug' is installed as part of a gem, and
|
|
8
|
+
# this file is here to facilitate running it.
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
require "pathname"
|
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
|
13
|
+
Pathname.new(__FILE__).realpath)
|
|
14
|
+
|
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
|
16
|
+
|
|
17
|
+
if File.file?(bundle_binstub)
|
|
18
|
+
if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/
|
|
19
|
+
load(bundle_binstub)
|
|
20
|
+
else
|
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
require "rubygems"
|
|
27
|
+
require "bundler/setup"
|
|
28
|
+
|
|
29
|
+
load Gem.bin_path("byebug", "byebug")
|
data/bin/coderay
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# This file was generated by Bundler.
|
|
6
|
+
#
|
|
7
|
+
# The application 'coderay' is installed as part of a gem, and
|
|
8
|
+
# this file is here to facilitate running it.
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
require "pathname"
|
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
|
13
|
+
Pathname.new(__FILE__).realpath)
|
|
14
|
+
|
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
|
16
|
+
|
|
17
|
+
if File.file?(bundle_binstub)
|
|
18
|
+
if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/
|
|
19
|
+
load(bundle_binstub)
|
|
20
|
+
else
|
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
require "rubygems"
|
|
27
|
+
require "bundler/setup"
|
|
28
|
+
|
|
29
|
+
load Gem.bin_path("coderay", "coderay")
|