Drift detection
Drift is a change made directly on a site (in wp-admin, by a plugin, by a teammate) that your repository does not know about. lps diff compares your tracked files with an environment and reports the difference, so a scheduled job can warn you before the next lps push overwrites it.
The webhook variant below needs no token: the job only reads from the site, using the site credentials already stored in your project configuration. You can also report to Loopress instead, with a token and no webhook.
How it works
Section titled “How it works”lps diff --env <name> --jsonprints a machine readable report.- The exit code is
0when in sync,1on drift,2when a resource could not be compared. - The report has this shape:
{ "drift": true, "left": "production", "right": "local files", "resources": { "snippet": { "added": [], "changed": [], "removed": [] } }}A resource that could not be compared carries an error string.
- Configure the project once on your machine (
lps project config). - Store your
config.json($XDG_CONFIG_HOME/loopress/config.json, or~/.config/loopress/config.json) as a repository secret namedLOOPRESS_CONFIG. - Store the webhook URL (Slack, Discord, or any endpoint accepting JSON) as a secret named
DRIFT_WEBHOOK_URL. - Add the workflow below to
.github/workflows/drift.yml.
GitHub Actions template
Section titled “GitHub Actions template”name: Drift detection
on: schedule: - cron: "0 6 * * *" workflow_dispatch:
jobs: drift: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- uses: actions/setup-node@v4 with: node-version: 24
- run: npm install -g @loopress/cli
- name: Restore site credentials env: LOOPRESS_CONFIG: ${{ secrets.LOOPRESS_CONFIG }} run: | mkdir -p ~/.config/loopress printf '%s' "$LOOPRESS_CONFIG" > ~/.config/loopress/config.json
- name: Compare production with the repository id: diff run: | set +e lps diff --env production --json > drift.json echo "code=$?" >> "$GITHUB_OUTPUT"
- name: Notify the webhook if: steps.diff.outputs.code != '0' env: WEBHOOK_URL: ${{ secrets.DRIFT_WEBHOOK_URL }} CODE: ${{ steps.diff.outputs.code }} run: | if [ "$CODE" = "1" ]; then TEXT="Drift detected on production in ${GITHUB_REPOSITORY}" else TEXT="Drift check inconclusive on production in ${GITHUB_REPOSITORY}" fi jq -n --arg text "$TEXT" --arg run "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" \ --slurpfile report drift.json '{text: ($text + ": " + $run), report: $report[0]}' \ | curl -fsS -X POST -H 'Content-Type: application/json' --data @- "$WEBHOOK_URL"
- name: Keep the report if: always() uses: actions/upload-artifact@v4 with: name: drift-report path: drift.jsonThe payload has a text field (what Slack and Discord-compatible endpoints display) plus the full report. Adapt the jq expression if your endpoint expects another shape.
Report to Loopress
Section titled “Report to Loopress”Instead of (or next to) a webhook, the job can send each result to your Loopress console, which shows the drift status of every environment with the date of the last check. No webhook and no jq step: with a token present, lps diff reports by itself.
- Create a token in the console and store it as a repository secret named
LOOPRESS_TOKEN. - Make sure the environment’s URL in the console matches the
urlin your site credentials: the report is attached to the environment with that exact URL. - Store
config.jsonasLOOPRESS_CONFIG, as above.
name: Drift detection
on: schedule: - cron: "0 6 * * *" workflow_dispatch:
jobs: drift: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- uses: actions/setup-node@v4 with: node-version: 24
- run: npm install -g @loopress/cli
- name: Restore site credentials env: LOOPRESS_CONFIG: ${{ secrets.LOOPRESS_CONFIG }} run: | mkdir -p ~/.config/loopress printf '%s' "$LOOPRESS_CONFIG" > ~/.config/loopress/config.json
- name: Compare production with the repository env: LOOPRESS_TOKEN: ${{ secrets.LOOPRESS_TOKEN }} run: lps diff --env productionReporting is best effort: it never changes the exit code nor the --json output, and a failure to reach Loopress is silent. The exit code still fails the job on drift (1) or when the comparison is inconclusive (2); add || true to the command if you only want the console to show the result. The report sends the site URL, status, and exit code, plus the resource, ID, and kind of change (added, changed, removed) for each item. It never sends the content of the diff.
Variations
Section titled “Variations”- Compare two environments instead of local files:
lps diff --env staging --against production --json. - Limit the comparison:
--only snippet --only acf, or--skip composer. - To fail the workflow on drift instead of notifying, run
lps diff --env productionalone: the non-zero exit code fails the job.