wordmove-ng 6.0.1 → 6.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f35d0da6f7b15e8f492351b76ea90e096fe73b5c94cf55d9e8f10a0e93eb3802
4
- data.tar.gz: cebf5ecc1de41bf581c5b07cdf4446e87ead5b73c891393b60cfa7a6d702c14c
3
+ metadata.gz: 6b938b4ef6d1ed30ecf48f1cbe1d48ba2f1a2ef223247189a3da3a0c1b840238
4
+ data.tar.gz: a3ffcdb129f255a82a7257f7d50a9d7c8b56195fd7e8a169ff04cceed8216281
5
5
  SHA512:
6
- metadata.gz: dcb6cddeb0d95335204a1a40b2f429573ec114d0cffc7375e16af0eec4d2201df92c9c8f2040e809c610933853a1e71cf116ff3b3525a22f25d6b4da26366ac5
7
- data.tar.gz: 35b2e3a3b29fb0e2ca9d6c128781cc0ca2feaa758b8efa49eeb71402c0ff2e683cb3bfcbbb8bf5d54d0d172ffd524a09579c7794b6da5a3da8638dc725c0bd8e
6
+ metadata.gz: 7d8d9baa3cecea48ac16a2853c927695eb1ad865bc0df9efc01f5ba6d034e96afab4322eeae3da3b9a605584a5fe44ed4ff58c3995daea9d4b9f08de616720ac
7
+ data.tar.gz: 211ca5b52895d6fef092985a0bec08cd7bad97845c6579187699856e1d79339d510da596a60247bc3aa4296b1bdb7e85859cebbeda51a853b36335bdb86723b0
@@ -13,7 +13,10 @@
13
13
  # - rubygems.org: pending trusted publisher for gem wordmove-ng, repository
14
14
  # tekgnosis-net/wordmove-ng, workflow release.yml, environment release
15
15
  # - GitHub: environment "release"; Actions setting "Allow GitHub Actions to
16
- # create and approve pull requests" enabled (needed by release-please)
16
+ # create and approve pull requests" enabled; repository secret
17
+ # RELEASE_PLEASE_TOKEN holding a fine-grained PAT scoped to this repository
18
+ # with Contents and Pull requests read/write (so release PRs run the checks
19
+ # that branch protection requires)
17
20
 
18
21
  name: Release
19
22
 
@@ -37,9 +40,14 @@ jobs:
37
40
  release_created: ${{ steps.release.outputs.release_created }}
38
41
  tag_name: ${{ steps.release.outputs.tag_name }}
39
42
  steps:
43
+ # A fine-grained PAT (secret RELEASE_PLEASE_TOKEN, contents + pull-requests
44
+ # read/write) instead of the built-in token: PRs opened with the built-in
45
+ # token never trigger the Tests workflow, which the branch protection on
46
+ # master requires before a release PR can be merged.
40
47
  - uses: googleapis/release-please-action@v4
41
48
  id: release
42
49
  with:
50
+ token: ${{ secrets.RELEASE_PLEASE_TOKEN || github.token }}
43
51
  config-file: release-please-config.json
44
52
  manifest-file: .release-please-manifest.json
45
53
 
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "6.0.1"
2
+ ".": "6.0.2"
3
3
  }
data/CHANGELOG.md CHANGED
@@ -5,6 +5,13 @@ All notable changes to wordmove-ng are documented here. The project follows
5
5
  are listed at https://github.com/welaika/wordmove/releases; the kokiddp fork's changes
6
6
  between 5.2.2 and this project are summarised under 6.0.0.
7
7
 
8
+ ## [6.0.2](https://github.com/tekgnosis-net/wordmove-ng/compare/v6.0.1...v6.0.2) (2026-09-12)
9
+
10
+
11
+ ### Documentation
12
+
13
+ * add cron automation guide and contrib/wordmove-sync.sh ([#4](https://github.com/tekgnosis-net/wordmove-ng/issues/4)) ([dbe3612](https://github.com/tekgnosis-net/wordmove-ng/commit/dbe36126976f9f5a0f0793208aed89bd5c7fc787))
14
+
8
15
  ## [6.0.1](https://github.com/tekgnosis-net/wordmove-ng/compare/v6.0.0...v6.0.1) (2026-09-09)
9
16
 
10
17
 
data/CLAUDE.md CHANGED
@@ -103,7 +103,8 @@ Update the relevant page in the same commit as any user-facing behaviour change.
103
103
  `docs:`, `chore:`, `ci:`, `test:`, `refactor:`). release-please derives the version bump
104
104
  and CHANGELOG from them; never edit `lib/wordmove/version.rb` or `CHANGELOG.md` by hand.
105
105
  Releases are published to rubygems.org by `.github/workflows/release.yml` (trusted
106
- publishing, no secrets).
106
+ publishing). `master` is protected for everyone including admins: branch, open a PR, let
107
+ the six `test` checks pass, merge. release-please needs the `RELEASE_PLEASE_TOKEN` secret.
107
108
 
108
109
  - Every shell fragment that includes user/config data goes through `Shellwords.escape`
109
110
  (or `Shellwords.split` for user-supplied option strings). Secrets must never appear in
data/README.md CHANGED
@@ -246,12 +246,17 @@ production:
246
246
  File sync mirrors the source: files missing on the source are deleted on the destination.
247
247
  Put anything you need to keep in `exclude`, which is always relative to `wordpress_path`.
248
248
 
249
+ Scheduled syncs from cron or CI work out of the box; `contrib/wordmove-sync.sh` handles the
250
+ environment cron lacks (Ruby version manager, `PATH`, no agent, no TTY, overlap lock, logs).
251
+ See [Automation and cron](https://tekgnosis-net.github.io/wordmove-ng/automation/).
252
+
249
253
  The documentation site has the full reference:
250
254
  [Configuration](https://tekgnosis-net.github.io/wordmove-ng/configuration/),
251
255
  [Usage and flags](https://tekgnosis-net.github.io/wordmove-ng/usage/),
252
256
  [Database sync](https://tekgnosis-net.github.io/wordmove-ng/database-sync/),
253
257
  [Hooks](https://tekgnosis-net.github.io/wordmove-ng/hooks/),
254
258
  [Multiple environments](https://tekgnosis-net.github.io/wordmove-ng/environments/),
259
+ [Automation and cron](https://tekgnosis-net.github.io/wordmove-ng/automation/),
255
260
  [Troubleshooting](https://tekgnosis-net.github.io/wordmove-ng/troubleshooting/).
256
261
 
257
262
  ## Logging
@@ -333,6 +338,11 @@ One-time setup for a new maintainer or a fork:
333
338
  - In the GitHub repository settings create an environment named `release`, and under
334
339
  *Actions → General* enable "Allow GitHub Actions to create and approve pull requests"
335
340
  so release-please can open its PR.
341
+ - Create a fine-grained personal access token scoped to this repository with *Contents* and
342
+ *Pull requests* set to read and write, and store it as the repository secret
343
+ `RELEASE_PLEASE_TOKEN`. Pull requests opened with the built-in Actions token never trigger
344
+ other workflows, so without it the release PR cannot satisfy the required `test` checks on
345
+ the protected `master` branch.
336
346
 
337
347
  ## Credits and licence
338
348
 
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Run a wordmove-ng sync from cron (or any non interactive context).
4
+ #
5
+ # Copy this file somewhere in your PATH, adjust the settings block or override
6
+ # the variables from the environment, then add a crontab entry such as
7
+ #
8
+ # MAILTO=you@example.com
9
+ # 30 2 * * * /usr/local/bin/wordmove-sync.sh
10
+ #
11
+ # Cron provides almost no environment: no rvm/rbenv shims, no ssh-agent, no TTY.
12
+ # This script makes ruby and the peer tools findable, relies on a key that
13
+ # needs no passphrase (see the docs page "Automation"), refuses to overlap
14
+ # with a still-running sync, and writes a plain-text log per run.
15
+ #
16
+ # Test it the way cron will run it before trusting the schedule:
17
+ # env -i HOME=$HOME SHELL=/bin/bash PATH=/usr/bin:/bin /usr/local/bin/wordmove-sync.sh
18
+ set -euo pipefail
19
+
20
+ # ---- settings (override with environment variables) --------------------------
21
+ SITE_DIR="${SITE_DIR:-/path/to/wordpress}" # directory holding movefile.yml
22
+ ENVIRONMENT="${ENVIRONMENT:-production}" # remote environment (-e)
23
+ ACTION="${ACTION:-pull}" # pull or push
24
+ COMPONENTS="${COMPONENTS:---all --no-db}" # component flags
25
+ LOG_DIR="${LOG_DIR:-$HOME/log/wordmove}"
26
+ KEEP_LOGS="${KEEP_LOGS:-30}" # how many run logs to keep
27
+ RUBY_VERSION="${RUBY_VERSION:-}" # e.g. ruby-3.4.9 for rvm; empty = whatever is on PATH
28
+ # -----------------------------------------------------------------------------
29
+
30
+ case "$ACTION" in
31
+ pull|push) ;;
32
+ *) echo "ACTION must be pull or push, got '$ACTION'" >&2; exit 2 ;;
33
+ esac
34
+
35
+ # Ruby version managers are not loaded by cron.
36
+ if [ -n "$RUBY_VERSION" ] && [ -s "$HOME/.rvm/scripts/rvm" ]; then
37
+ # shellcheck disable=SC1091
38
+ source "$HOME/.rvm/scripts/rvm"
39
+ rvm use "$RUBY_VERSION" >/dev/null
40
+ elif [ -d "$HOME/.rbenv/shims" ]; then
41
+ export PATH="$HOME/.rbenv/shims:$PATH"
42
+ fi
43
+
44
+ # Peer tools wordmove-ng shells out to (ssh, rsync, mysqldump, wp, ...).
45
+ export PATH="$HOME/bin:$HOME/.local/bin:/usr/local/bin:/usr/bin:/bin:$PATH"
46
+ export LC_ALL="${LC_ALL:-C.UTF-8}"
47
+
48
+ command -v wordmove-ng >/dev/null || { echo "wordmove-ng not found in PATH" >&2; exit 2; }
49
+
50
+ mkdir -p "$LOG_DIR"
51
+ LOG="$LOG_DIR/${ENVIRONMENT}-$(date +%Y%m%d-%H%M%S).log"
52
+ LOCK_FILE="${LOCK_FILE:-${TMPDIR:-/tmp}/wordmove-${ENVIRONMENT}.lock}"
53
+
54
+ cd "$SITE_DIR"
55
+
56
+ # Refuse to start while a previous run is still active.
57
+ exec 9>"$LOCK_FILE"
58
+ if ! flock -n 9; then
59
+ echo "$(date '+%F %T') previous run still active, skipping" >>"$LOG"
60
+ exit 0
61
+ fi
62
+
63
+ # The output is coloured regardless of TTY; strip the escape codes for the log.
64
+ # shellcheck disable=SC2086 # COMPONENTS is a list of flags on purpose
65
+ {
66
+ echo "== $(date '+%F %T') wordmove-ng $ACTION -e $ENVIRONMENT $COMPONENTS"
67
+ wordmove-ng "$ACTION" -e "$ENVIRONMENT" $COMPONENTS
68
+ echo "== $(date '+%F %T') finished, exit 0"
69
+ } 2>&1 | sed 's/\x1b\[[0-9;]*m//g' >>"$LOG"
70
+
71
+ # Rotate logs: keep the newest KEEP_LOGS files for this environment.
72
+ find "$LOG_DIR" -maxdepth 1 -name "${ENVIRONMENT}-*.log" -printf '%T@ %p\n' \
73
+ | sort -rn | tail -n +"$((KEEP_LOGS + 1))" | cut -d' ' -f2- | xargs -r rm -f
@@ -0,0 +1,166 @@
1
+ ---
2
+ title: Automation and cron
3
+ nav_order: 10
4
+ ---
5
+
6
+ # Automation and cron
7
+ {: .no_toc }
8
+
9
+ wordmove-ng never prompts, so it runs unattended: from cron, systemd timers, CI runners or deploy scripts. What differs from an interactive shell is the environment, and this page covers exactly that.
10
+
11
+ 1. TOC
12
+ {:toc}
13
+
14
+ ## What cron does not give you
15
+
16
+ - **No version manager.** rvm and rbenv shims are loaded by your shell profile, which cron does not read. `ruby`, `gem` executables and the gemset must be made visible explicitly.
17
+ - **No ssh-agent.** There is no agent socket, so the SSH key must be usable without one: a key without a passphrase, or a key held by a system agent you start yourself.
18
+ - **A minimal `PATH`.** `rsync`, `mysqldump`, `wp` and friends installed under `~/bin`, `/usr/local/bin` or a MAMP/Local directory are not found unless you add them.
19
+ - **No TTY.** Output is still coloured; strip the escape codes when logging to a file.
20
+
21
+ `wordmove-ng` itself is already safe here: SSH runs with `BatchMode=yes`, a failing key authentication is an error rather than a hanging prompt, prerequisites are checked before any side effect, and every failure exits non-zero.
22
+
23
+ ## Prepare once
24
+
25
+ ### A key cron can use
26
+ {: .no_toc }
27
+
28
+ Create a dedicated key without a passphrase and restrict what it can do on the server:
29
+
30
+ ```bash
31
+ ssh-keygen -t ed25519 -N '' -f ~/.ssh/wordmove_cron_ed25519 -C wordmove-cron
32
+ ```
33
+
34
+ In the remote `~/.ssh/authorized_keys`, prefix the public key with restrictions:
35
+
36
+ ```
37
+ from="203.0.113.10",no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-ed25519 AAAA… wordmove-cron
38
+ ```
39
+
40
+ Then point the movefile at a `~/.ssh/config` entry that names the key, so the movefile stays free of machine-specific paths:
41
+
42
+ ```
43
+ Host prod-wordmove
44
+ Hostname example.com
45
+ User deploy
46
+ IdentityFile ~/.ssh/wordmove_cron_ed25519
47
+ IdentitiesOnly yes
48
+ ```
49
+
50
+ ```yaml
51
+ production:
52
+ ssh:
53
+ host: prod-wordmove
54
+ ```
55
+
56
+ ### Secrets in `.env`
57
+ {: .no_toc }
58
+
59
+ Keep database passwords in a `.env` file next to `movefile.yml` with mode `600`, referenced as `<%= ENV['PROD_DB_PASS'] %>`. Never put them in the crontab, which is readable by other means and shows up in logs.
60
+
61
+ ### Guard rails in the movefile
62
+ {: .no_toc }
63
+
64
+ A scheduled job should not be able to do more than it was written for, even if the script is edited later. Use `forbid`:
65
+
66
+ ```yaml
67
+ production:
68
+ forbid:
69
+ push:
70
+ db: true # a file-sync job must never push the database
71
+ uploads: true
72
+ ```
73
+
74
+ and `exclude` for anything that lives on one side only, because syncs **mirror** the source: a scheduled `pull --all` overwrites local changes, a scheduled `push --all` overwrites uploads added through the WordPress admin since the last run.
75
+
76
+ ## The script
77
+
78
+ The repository ships [`contrib/wordmove-sync.sh`](https://github.com/tekgnosis-net/wordmove-ng/blob/master/contrib/wordmove-sync.sh). Copy it to `/usr/local/bin/`, adjust the settings block at the top or override the variables from the environment. It
79
+
80
+ - loads rvm (`RUBY_VERSION=ruby-3.4.9`) or rbenv, and extends `PATH` for the peer tools;
81
+ - refuses to start while a previous run holds the lock, so overlapping runs cannot happen;
82
+ - writes one plain-text log per run to `~/log/wordmove/` with colour codes stripped, and keeps the last 30;
83
+ - exits non-zero when the sync fails, so cron mails you when `MAILTO` is set.
84
+
85
+ ```bash
86
+ #!/usr/bin/env bash
87
+ set -euo pipefail
88
+
89
+ SITE_DIR="${SITE_DIR:-/path/to/wordpress}" # directory holding movefile.yml
90
+ ENVIRONMENT="${ENVIRONMENT:-production}"
91
+ ACTION="${ACTION:-pull}" # pull or push
92
+ COMPONENTS="${COMPONENTS:---all --no-db}"
93
+ LOG_DIR="${LOG_DIR:-$HOME/log/wordmove}"
94
+ RUBY_VERSION="${RUBY_VERSION:-}" # e.g. ruby-3.4.9 for rvm
95
+
96
+ if [ -n "$RUBY_VERSION" ] && [ -s "$HOME/.rvm/scripts/rvm" ]; then
97
+ source "$HOME/.rvm/scripts/rvm"; rvm use "$RUBY_VERSION" >/dev/null
98
+ elif [ -d "$HOME/.rbenv/shims" ]; then
99
+ export PATH="$HOME/.rbenv/shims:$PATH"
100
+ fi
101
+ export PATH="$HOME/bin:$HOME/.local/bin:/usr/local/bin:/usr/bin:/bin:$PATH"
102
+
103
+ mkdir -p "$LOG_DIR"
104
+ LOG="$LOG_DIR/${ENVIRONMENT}-$(date +%Y%m%d-%H%M%S).log"
105
+ cd "$SITE_DIR"
106
+
107
+ exec 9>"/tmp/wordmove-${ENVIRONMENT}.lock"
108
+ flock -n 9 || { echo "$(date '+%F %T') previous run still active" >>"$LOG"; exit 0; }
109
+
110
+ {
111
+ echo "== $(date '+%F %T') wordmove-ng $ACTION -e $ENVIRONMENT $COMPONENTS"
112
+ wordmove-ng "$ACTION" -e "$ENVIRONMENT" $COMPONENTS
113
+ echo "== $(date '+%F %T') finished"
114
+ } 2>&1 | sed 's/\x1b\[[0-9;]*m//g' >>"$LOG"
115
+ ```
116
+
117
+ (abridged; the shipped file adds validation and log rotation)
118
+
119
+ ## Crontab
120
+
121
+ Nightly at 02:30 local time, with failures mailed to you:
122
+
123
+ ```
124
+ MAILTO=you@example.com
125
+ 30 2 * * * SITE_DIR=/home/me/sites/example RUBY_VERSION=ruby-3.4.9 /usr/local/bin/wordmove-sync.sh
126
+ ```
127
+
128
+ Several sites or environments are just several lines with different variables. Stagger them so their windows do not overlap on the same remote.
129
+
130
+ ## Test it the way cron runs it
131
+
132
+ A missing `PATH` entry or an unreadable key only shows up under cron's empty environment, so reproduce that before trusting the schedule:
133
+
134
+ ```bash
135
+ env -i HOME=$HOME SHELL=/bin/bash PATH=/usr/bin:/bin \
136
+ SITE_DIR=/home/me/sites/example RUBY_VERSION=ruby-3.4.9 /usr/local/bin/wordmove-sync.sh
137
+ echo "exit $?"
138
+ tail -n 40 ~/log/wordmove/production-*.log
139
+ ```
140
+
141
+ Running `wordmove-ng doctor` under the same `env -i` prefix is the quickest check that the key and every tool resolve without an agent.
142
+
143
+ ## Database jobs
144
+
145
+ For a scheduled database push enable `global.maintenance_mode` so the seconds between import and `wp search-replace` show visitors the maintenance page instead of the wrong URLs. Scheduled database pulls are the common case (a nightly copy of production for development) and need nothing special; each run leaves a `local-backup-<timestamp>.sql.gz` in `wp-content/`, so prune those occasionally.
146
+
147
+ ## systemd timer instead of cron
148
+
149
+ The same script works as a `oneshot` service:
150
+
151
+ ```ini
152
+ # ~/.config/systemd/user/wordmove-sync.service
153
+ [Service]
154
+ Type=oneshot
155
+ Environment=SITE_DIR=/home/me/sites/example RUBY_VERSION=ruby-3.4.9
156
+ ExecStart=/usr/local/bin/wordmove-sync.sh
157
+
158
+ # ~/.config/systemd/user/wordmove-sync.timer
159
+ [Timer]
160
+ OnCalendar=*-*-* 02:30:00
161
+ Persistent=true
162
+ [Install]
163
+ WantedBy=timers.target
164
+ ```
165
+
166
+ `systemctl --user enable --now wordmove-sync.timer`, and `journalctl --user -u wordmove-sync` for the output. Enable lingering (`loginctl enable-linger $USER`) so user timers run without a login session.
data/docs/contributing.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Contributing and releasing
3
- nav_order: 11
3
+ nav_order: 12
4
4
  ---
5
5
 
6
6
  # Contributing and releasing
@@ -42,6 +42,8 @@ Releases are automated by [release-please](https://github.com/googleapis/release
42
42
 
43
43
  Pushing a `v*` tag by hand at the head of `master` triggers the same publish job.
44
44
 
45
+ `master` is protected: changes land through pull requests whose `test` checks pass, and the rules apply to administrators too. release-please opens its PR with a fine-grained personal access token (secret `RELEASE_PLEASE_TOKEN`) because PRs opened with the built-in Actions token never trigger the checks.
46
+
45
47
  ## This documentation
46
48
 
47
49
  The site is built from the `docs/` folder on `master` with Jekyll and the just-the-docs theme by `.github/workflows/pages.yml`. Preview locally with
data/docs/index.md CHANGED
@@ -51,6 +51,7 @@ It is the maintained, independent successor of [Wordmove](https://github.com/wel
51
51
  | know what each flag does | [Usage and flags]({{ site.baseurl }}/usage/) |
52
52
  | know exactly what happens to my database | [Database sync]({{ site.baseurl }}/database-sync/) |
53
53
  | run commands before or after a deploy | [Hooks]({{ site.baseurl }}/hooks/) |
54
+ | schedule syncs from cron or CI | [Automation and cron]({{ site.baseurl }}/automation/) |
54
55
  | fix a problem | [Troubleshooting]({{ site.baseurl }}/troubleshooting/) |
55
56
 
56
57
  ## Credits
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Troubleshooting
3
- nav_order: 10
3
+ nav_order: 11
4
4
  ---
5
5
 
6
6
  # Troubleshooting and FAQ
data/docs/usage.md CHANGED
@@ -109,6 +109,10 @@ exclude:
109
109
 
110
110
  The same works for themes and any other folder.
111
111
 
112
+ ## Running unattended
113
+
114
+ wordmove-ng never prompts, so it can run from cron, systemd timers or CI. The environment those give you is the only difference; see [Automation and cron]({{ site.baseurl }}/automation/) for a ready-made script and the SSH key setup.
115
+
112
116
  ## Rsync options in a continuous delivery context
113
117
 
114
118
  The default rsync flags are `-rlpt`. On a CI runner every file is freshly checked out, so `--times` triggers a full transfer every time and `--perms` may not be what you want:
@@ -1,3 +1,3 @@
1
1
  module Wordmove
2
- VERSION = "6.0.1".freeze
2
+ VERSION = "6.0.2".freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wordmove-ng
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.1
4
+ version: 6.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kumar
@@ -251,11 +251,13 @@ files:
251
251
  - bin/setup
252
252
  - bin/thor
253
253
  - bin/wordmove-ng
254
+ - contrib/wordmove-sync.sh
254
255
  - deploy/deploy.sh
255
256
  - docs/Gemfile
256
257
  - docs/README.md
257
258
  - docs/_config.yml
258
259
  - docs/assets/images/wordmove-ng.png
260
+ - docs/automation.md
259
261
  - docs/configuration.md
260
262
  - docs/contributing.md
261
263
  - docs/database-sync.md