wordmove-ng 6.0.0 → 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 +4 -4
- data/.github/workflows/pages.yml +52 -0
- data/.github/workflows/release.yml +15 -2
- data/.gitignore +2 -0
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +15 -1
- data/CLAUDE.md +9 -1
- data/README.md +26 -9
- data/contrib/wordmove-sync.sh +73 -0
- data/docs/Gemfile +4 -0
- data/docs/README.md +10 -0
- data/docs/_config.yml +47 -0
- data/docs/assets/images/wordmove-ng.png +0 -0
- data/docs/automation.md +166 -0
- data/docs/configuration.md +345 -0
- data/docs/contributing.md +55 -0
- data/docs/database-sync.md +91 -0
- data/docs/environments.md +65 -0
- data/docs/hooks.md +103 -0
- data/docs/index.md +59 -0
- data/docs/installation.md +104 -0
- data/docs/quick-start.md +81 -0
- data/docs/troubleshooting.md +141 -0
- data/docs/upgrading.md +61 -0
- data/docs/usage.md +129 -0
- data/lib/wordmove/version.rb +1 -1
- data/wordmove-ng.gemspec +1 -1
- metadata +20 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6b938b4ef6d1ed30ecf48f1cbe1d48ba2f1a2ef223247189a3da3a0c1b840238
|
|
4
|
+
data.tar.gz: a3ffcdb129f255a82a7257f7d50a9d7c8b56195fd7e8a169ff04cceed8216281
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7d8d9baa3cecea48ac16a2853c927695eb1ad865bc0df9efc01f5ba6d034e96afab4322eeae3da3b9a605584a5fe44ed4ff58c3995daea9d4b9f08de616720ac
|
|
7
|
+
data.tar.gz: 211ca5b52895d6fef092985a0bec08cd7bad97845c6579187699856e1d79339d510da596a60247bc3aa4296b1bdb7e85859cebbeda51a853b36335bdb86723b0
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Builds the docs/ site with Jekyll + just-the-docs and deploys it to GitHub
|
|
2
|
+
# Pages. Requires the repository's Pages source to be set to "GitHub Actions".
|
|
3
|
+
|
|
4
|
+
name: Docs
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches: [master]
|
|
9
|
+
paths:
|
|
10
|
+
- "docs/**"
|
|
11
|
+
- ".github/workflows/pages.yml"
|
|
12
|
+
workflow_dispatch:
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
pages: write
|
|
17
|
+
id-token: write
|
|
18
|
+
|
|
19
|
+
concurrency:
|
|
20
|
+
group: pages
|
|
21
|
+
cancel-in-progress: true
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
build:
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v5
|
|
28
|
+
- uses: ruby/setup-ruby@v1
|
|
29
|
+
with:
|
|
30
|
+
ruby-version: "3.4"
|
|
31
|
+
bundler-cache: true
|
|
32
|
+
working-directory: docs
|
|
33
|
+
- uses: actions/configure-pages@v5
|
|
34
|
+
id: pages
|
|
35
|
+
- name: Build
|
|
36
|
+
working-directory: docs
|
|
37
|
+
env:
|
|
38
|
+
JEKYLL_ENV: production
|
|
39
|
+
run: bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}"
|
|
40
|
+
- uses: actions/upload-pages-artifact@v3
|
|
41
|
+
with:
|
|
42
|
+
path: docs/_site
|
|
43
|
+
|
|
44
|
+
deploy:
|
|
45
|
+
needs: build
|
|
46
|
+
runs-on: ubuntu-latest
|
|
47
|
+
environment:
|
|
48
|
+
name: github-pages
|
|
49
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/deploy-pages@v4
|
|
52
|
+
id: deployment
|
|
@@ -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
|
|
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
|
|
|
@@ -97,4 +105,9 @@ jobs:
|
|
|
97
105
|
env:
|
|
98
106
|
GH_TOKEN: ${{ github.token }}
|
|
99
107
|
TAG: ${{ needs.release-please.outputs.tag_name || github.ref_name }}
|
|
100
|
-
|
|
108
|
+
# release-please creates the release itself; a hand-pushed tag has none yet.
|
|
109
|
+
run: |
|
|
110
|
+
if ! gh release view "$TAG" >/dev/null 2>&1; then
|
|
111
|
+
gh release create "$TAG" --title "$TAG" --notes "See CHANGELOG.md for details."
|
|
112
|
+
fi
|
|
113
|
+
gh release upload "$TAG" pkg/*.gem --clobber
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,21 @@ 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.0 (
|
|
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
|
+
|
|
15
|
+
## [6.0.1](https://github.com/tekgnosis-net/wordmove-ng/compare/v6.0.0...v6.0.1) (2026-09-09)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Documentation
|
|
19
|
+
|
|
20
|
+
* add the documentation site on GitHub Pages ([3b3e70a](https://github.com/tekgnosis-net/wordmove-ng/commit/3b3e70ae4ed548778634aa115cf0b64d53cfb727))
|
|
21
|
+
|
|
22
|
+
## 6.0.0 (2026-09-09)
|
|
9
23
|
|
|
10
24
|
First release as an independent project. Breaking changes are marked **breaking**.
|
|
11
25
|
|
data/CLAUDE.md
CHANGED
|
@@ -90,13 +90,21 @@ Key pieces:
|
|
|
90
90
|
`global.collation_fallbacks`, `global.charset_fallbacks`. Add new toggles as movefile keys
|
|
91
91
|
with an env override and document them in README + CHANGELOG in the same commit.
|
|
92
92
|
|
|
93
|
+
## Docs site
|
|
94
|
+
|
|
95
|
+
`docs/` is a Jekyll + just-the-docs site published to https://tekgnosis-net.github.io/wordmove-ng/
|
|
96
|
+
by `.github/workflows/pages.yml` on pushes to `master` that touch `docs/`. Internal links use
|
|
97
|
+
`{{ site.baseurl }}/<slug>/` (pretty permalinks). Preview: `cd docs && bundle exec jekyll serve`.
|
|
98
|
+
Update the relevant page in the same commit as any user-facing behaviour change.
|
|
99
|
+
|
|
93
100
|
## Conventions worth knowing
|
|
94
101
|
|
|
95
102
|
- **Commit messages are Conventional Commits** (`feat:`, `fix:`, `feat!:`/`BREAKING CHANGE:`,
|
|
96
103
|
`docs:`, `chore:`, `ci:`, `test:`, `refactor:`). release-please derives the version bump
|
|
97
104
|
and CHANGELOG from them; never edit `lib/wordmove/version.rb` or `CHANGELOG.md` by hand.
|
|
98
105
|
Releases are published to rubygems.org by `.github/workflows/release.yml` (trusted
|
|
99
|
-
publishing,
|
|
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.
|
|
100
108
|
|
|
101
109
|
- Every shell fragment that includes user/config data goes through `Shellwords.escape`
|
|
102
110
|
(or `Shellwords.split` for user-supplied option strings). Secrets must never appear in
|
data/README.md
CHANGED
|
@@ -12,7 +12,11 @@ by weLaika, continued through the [kokiddp fork](https://github.com/kokiddp/word
|
|
|
12
12
|
The movefile format, commands and flags are unchanged. What changed is under the hood, and
|
|
13
13
|
in a few places where the old behaviour was unsafe. See [Upgrading from wordmove 5.x](#upgrading-from-wordmove-5x).
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
**Full documentation: <https://tekgnosis-net.github.io/wordmove-ng/>**
|
|
16
|
+
|
|
17
|
+
[](https://github.com/tekgnosis-net/wordmove-ng/actions/workflows/ruby.yml?query=branch%3Amaster)
|
|
18
|
+
[](https://rubygems.org/gems/wordmove-ng)
|
|
19
|
+
[](https://tekgnosis-net.github.io/wordmove-ng/)
|
|
16
20
|
|
|
17
21
|
## Highlights
|
|
18
22
|
|
|
@@ -37,7 +41,7 @@ Ruby 3.0 or newer is required.
|
|
|
37
41
|
gem install wordmove-ng
|
|
38
42
|
```
|
|
39
43
|
|
|
40
|
-
|
|
44
|
+
To run the latest master instead of a release:
|
|
41
45
|
|
|
42
46
|
```bash
|
|
43
47
|
gem install specific_install
|
|
@@ -242,12 +246,18 @@ production:
|
|
|
242
246
|
File sync mirrors the source: files missing on the source are deleted on the destination.
|
|
243
247
|
Put anything you need to keep in `exclude`, which is always relative to `wordpress_path`.
|
|
244
248
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
[
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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
|
+
|
|
253
|
+
The documentation site has the full reference:
|
|
254
|
+
[Configuration](https://tekgnosis-net.github.io/wordmove-ng/configuration/),
|
|
255
|
+
[Usage and flags](https://tekgnosis-net.github.io/wordmove-ng/usage/),
|
|
256
|
+
[Database sync](https://tekgnosis-net.github.io/wordmove-ng/database-sync/),
|
|
257
|
+
[Hooks](https://tekgnosis-net.github.io/wordmove-ng/hooks/),
|
|
258
|
+
[Multiple environments](https://tekgnosis-net.github.io/wordmove-ng/environments/),
|
|
259
|
+
[Automation and cron](https://tekgnosis-net.github.io/wordmove-ng/automation/),
|
|
260
|
+
[Troubleshooting](https://tekgnosis-net.github.io/wordmove-ng/troubleshooting/).
|
|
251
261
|
|
|
252
262
|
## Logging
|
|
253
263
|
|
|
@@ -315,7 +325,9 @@ stored anywhere.
|
|
|
315
325
|
the GitHub release.
|
|
316
326
|
|
|
317
327
|
Pushing a `v*` tag by hand at the head of `master` triggers the same publish job; that is
|
|
318
|
-
how 6.0.0
|
|
328
|
+
how 6.0.0 was cut. Merge the release PR only when there is something worth shipping:
|
|
329
|
+
release-please keeps proposing a patch release for any Conventional Commit, including
|
|
330
|
+
`ci:` and `docs:` ones.
|
|
319
331
|
|
|
320
332
|
One-time setup for a new maintainer or a fork:
|
|
321
333
|
|
|
@@ -326,6 +338,11 @@ One-time setup for a new maintainer or a fork:
|
|
|
326
338
|
- In the GitHub repository settings create an environment named `release`, and under
|
|
327
339
|
*Actions → General* enable "Allow GitHub Actions to create and approve pull requests"
|
|
328
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.
|
|
329
346
|
|
|
330
347
|
## Credits and licence
|
|
331
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
|
data/docs/Gemfile
ADDED
data/docs/README.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Documentation site, published to https://tekgnosis-net.github.io/wordmove-ng/ by
|
|
2
|
+
`.github/workflows/pages.yml` on every push to `master`.
|
|
3
|
+
|
|
4
|
+
Preview locally:
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
cd docs
|
|
8
|
+
bundle install
|
|
9
|
+
bundle exec jekyll serve # http://127.0.0.1:4000/wordmove-ng/
|
|
10
|
+
```
|
data/docs/_config.yml
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
title: wordmove-ng
|
|
2
|
+
description: Move WordPress sites between environments over SSH, database included.
|
|
3
|
+
url: https://tekgnosis-net.github.io
|
|
4
|
+
baseurl: /wordmove-ng
|
|
5
|
+
repository: tekgnosis-net/wordmove-ng
|
|
6
|
+
|
|
7
|
+
theme: just-the-docs
|
|
8
|
+
permalink: pretty
|
|
9
|
+
color_scheme: light
|
|
10
|
+
logo: /assets/images/wordmove-ng.png
|
|
11
|
+
favicon_ico: /assets/images/wordmove-ng.png
|
|
12
|
+
search_enabled: true
|
|
13
|
+
heading_anchors: true
|
|
14
|
+
|
|
15
|
+
aux_links:
|
|
16
|
+
GitHub: https://github.com/tekgnosis-net/wordmove-ng
|
|
17
|
+
RubyGems: https://rubygems.org/gems/wordmove-ng
|
|
18
|
+
aux_links_new_tab: true
|
|
19
|
+
|
|
20
|
+
nav_external_links:
|
|
21
|
+
- title: Changelog
|
|
22
|
+
url: https://github.com/tekgnosis-net/wordmove-ng/blob/master/CHANGELOG.md
|
|
23
|
+
|
|
24
|
+
footer_content: 'Copyright © 2013-2019 weLaika Soc. Coop. (Wordmove), 2026 tekgnosis.net (wordmove-ng). Distributed under the <a href="https://github.com/tekgnosis-net/wordmove-ng/blob/master/LICENSE">MIT license</a>.'
|
|
25
|
+
|
|
26
|
+
callouts:
|
|
27
|
+
note:
|
|
28
|
+
title: Note
|
|
29
|
+
color: blue
|
|
30
|
+
warning:
|
|
31
|
+
title: Warning
|
|
32
|
+
color: yellow
|
|
33
|
+
danger:
|
|
34
|
+
title: Danger
|
|
35
|
+
color: red
|
|
36
|
+
|
|
37
|
+
exclude:
|
|
38
|
+
- Gemfile
|
|
39
|
+
- Gemfile.lock
|
|
40
|
+
- vendor/
|
|
41
|
+
- README.md
|
|
42
|
+
|
|
43
|
+
defaults:
|
|
44
|
+
- scope:
|
|
45
|
+
path: ""
|
|
46
|
+
values:
|
|
47
|
+
layout: default
|
|
Binary file
|
data/docs/automation.md
ADDED
|
@@ -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.
|