Skip to content

ci(deploy): only POST plugin activation when not already active#61

Merged
JohnRDOrazio merged 2 commits intomainfrom
fix/idempotent-plugin-activation
May 1, 2026
Merged

ci(deploy): only POST plugin activation when not already active#61
JohnRDOrazio merged 2 commits intomainfrom
fix/idempotent-plugin-activation

Conversation

@JohnRDOrazio
Copy link
Copy Markdown
Member

@JohnRDOrazio JohnRDOrazio commented May 1, 2026

Refs #41.

Update after VPS investigation: this PR's original framing claimed plugin activation triggers flush_rewrite_rules() and rewrites .htaccess, causing a brief Plesk-nginx mirror lag and the 404 race. That hypothesis turned out to be wrong.htaccess on the VPS hasn't been modified since 2026-02-18, and the queue worker has had zero HTTP 404s in 4+ days of journal data. See the latest comment on #41 for the full revised analysis.

This PR is still useful as code hygiene — eliminating redundant flush_rewrite_rules() invocations is correct regardless — but it's no longer expected to materially move the deploy-time error rate. The dominant deploy-time symptom is HTTP 504 from PHP-FPM pool exhaustion, not 404 from a routing race. That needs a different fix (worker throttling, FPM pool sizing, opcache settings).

Why the change is still worth merging

  1. The activation API call really does do extra work when invoked on an already-active plugin: WordPress walks each plugin file's activation hook, fires do_action( 'activate_<file>' ), and may invoke any register_activation_hook callbacks the plugins define. Skipping the call when status is already "active" removes that work cleanly.
  2. It removes one variable from the deploy → so when we later isolate the FPM-saturation cause, we know plugin activation isn't contributing.
  3. It makes the deploy log more informative — Skip <plugin>: already active vs. Activate <plugin> (was inactive): HTTP 200 shows what state actually changed.

Change

Read each plugin's current status first via GET /wp/v2/plugins/<slug> and only POST when status isn't already "active". Tarballs still get extracted and plugin code updates land; only the no-op activation request is skipped.

The fallback parser uses "unknown" if JSON parsing fails, which forces the POST path — safe default, preserves current behavior on transient response oddities.

Test plan

  • Trigger a manual workflow_dispatch deploy. Logs should show Skip <plugin>: already active for both plugins (since they were both active prior to this PR).
  • Toggle one plugin to inactive in WP admin, run a deploy, confirm it activates correctly with Activate <plugin> (was inactive): HTTP 200.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores
    • Added a per-plugin status check before activation; skips activation when status is already "active"
    • Treats non-2xx status fetches as deployment failures and emits explicit errors
    • Updated deployment logging to report prior plugin status alongside activation request results

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 1, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b89ac2cf-ebd4-4e54-b9ba-6bab543a80c8

📥 Commits

Reviewing files that changed from the base of the PR and between deb673d and 0a2387f.

📒 Files selected for processing (1)
  • .github/workflows/deploy.yml
✅ Files skipped from review due to trivial changes (1)
  • .github/workflows/deploy.yml

📝 Walkthrough

Walkthrough

The deploy workflow's plugin activation step now GETs each plugin to read its current status (failing on non-2xx), JSON-parses status with "unknown" fallback, skips the POST when already "active", and updates logs to include the prior status alongside POST HTTP results.

Changes

Plugin Activation Idempotency

Layer / File(s) Summary
Control Flow / Activation
.github/workflows/deploy.yml
Activation changed from unconditional POST to: GET plugin resource, parse status, skip POST if status == "active", else POST {"status":"active"}.
Error Handling
.github/workflows/deploy.yml
Fail the deploy when the GET returns a non-2xx HTTP code; treat JSON parse failures with a default "unknown".
Logging / Telemetry
.github/workflows/deploy.yml
Activation log now reports the previously observed status (was $CURRENT_STATUS) along with the POST HTTP status instead of only POST result.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

Poem

🐰 A hop, a check, a gentle peek,
I read the status, mild not meek.
If "active" greets me, I skip the race,
Else I nudge it forward—soft and paced.
Deploys stay calm, the rabbit's grace.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'ci(deploy): only POST plugin activation when not already active' is a clear, specific summary that directly captures the main change: adding conditional logic to skip redundant plugin activation requests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/idempotent-plugin-activation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

@codacy-production
Copy link
Copy Markdown

codacy-production Bot commented May 1, 2026

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

Each POST /wp/v2/plugins/<slug> with {"status":"active"} triggers
WordPress's flush_rewrite_rules(), which rewrites .htaccess. Plesk's
nginx mirrors .htaccess into its own internal rewrite-rule list with
a small lag during regeneration; during that lag, /wp-json/* requests
fall through nginx's try_files ladder and 404 from the filesystem
layer.

The cdcf-queue-worker.service systemd unit on the same VPS fires 5
parallel POSTs every 15 seconds against /wp-json/cdcf/v1/process-queue,
so it consistently hits the lag window during deploys, producing the
~30-50% 404 wave reported in #41.

Skipping the activation POST when the plugin is already active
eliminates the redundant flush_rewrite_rules() calls for the common
case where deploys don't change activation state. Tarballs still get
extracted and plugin code updates land; only the no-op rewrite-rule
flush is removed.

Pausing the queue worker during deploys would be a complementary fix
but isn't viable from the existing deploy automation surface.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@JohnRDOrazio JohnRDOrazio force-pushed the fix/idempotent-plugin-activation branch from 0ba6c61 to deb673d Compare May 1, 2026 22:13
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/deploy.yml:
- Around line 343-345: The curl calls that set CURRENT (curl -s -w
'%{http_code}' -X GET "$WP_URL/$SLUG" -H "Authorization: Basic $AUTH") and the
activation POST calls should include explicit timeouts to avoid hanging; update
those curl invocations (the one assigning CURRENT and the activation POST block
around lines 370-374) to add --connect-timeout and --max-time (or -m) options
with sensible values so the workflow will fail fast on network issues while
preserving error handling.
- Around line 334-336: The explanatory comment referencing
flush_rewrite_rules(), WordPress, Plesk's nginx mirror and the /wp-json/* 404s
(issue `#41`) is written as a definitive root cause; make it hypothesis-neutral by
rephrasing to indicate a possible or likely cause—for example replace "the root
cause" with "a possible cause" or "may contribute to" and remove absolute
language so the comment (the explanatory block mentioning flush_rewrite_rules(),
Plesk nginx mirror and the 404 wave) reads as a hypothesis rather than a
confirmed fact.
- Around line 357-363: The multiline inline Python inside the command
substitution breaks YAML parsing; replace it with a single-line, YAML-safe
invocation so CURRENT_STATUS is assigned reliably from BODY. Change the block to
a one-liner such as: CURRENT_STATUS=$(echo "$BODY" | python3 -c 'import
json,sys; print(json.load(sys.stdin).get("status","unknown"))' 2>/dev/null ||
echo unknown) so the Python code is on a single line and the quoting is safe for
the workflow parser; keep references to CURRENT_STATUS and BODY to locate the
change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ee30fcc2-bfc1-4b71-9b01-37ecd783dfad

📥 Commits

Reviewing files that changed from the base of the PR and between 83b0c42 and deb673d.

📒 Files selected for processing (1)
  • .github/workflows/deploy.yml

Comment thread .github/workflows/deploy.yml Outdated
Comment thread .github/workflows/deploy.yml Outdated
Comment thread .github/workflows/deploy.yml Outdated
- Add --connect-timeout/--max-time to curl GET and POST so the workflow
  fails fast on network hangs; switch -s to -sS so curl errors still
  surface on stderr.
- Soften the explanatory comment from "the root cause" to "the
  suspected cause" since #41 is hypothesis-driven.
- Collapse the inline Python status parser to a single line; the
  try/except was redundant with the outer `|| echo unknown` fallback.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@JohnRDOrazio
Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 1, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant