Skip to content

fix(ci): redeploy the CDN after the release and gate on it catching up - #43

Merged
elkaix merged 5 commits into
mainfrom
fix/cdn-redeploy-after-release
Aug 8, 2026
Merged

fix(ci): redeploy the CDN after the release and gate on it catching up#43
elkaix merged 5 commits into
mainfrom
fix/cdn-redeploy-after-release

Conversation

@elkaix

@elkaix elkaix commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Related Issue

No issue — the problem is described below.

Problem

The CDN never learns about a new release, so no installed client sees it.

0.13.0 published to npm at 13:52:23Z on 2026-08-08. An hour later https://code.pythinker.com/pythinker-code/latest.json still advertised 0.12.0, and every client's update check logged reason: "not-newer". CI was green throughout.

The cause is an ordering race:

  1. 13:43:42Z — the ci: release packages commit is pushed to main, which is what starts the release.
  2. 13:44:06Z — Dokploy autodeploy, triggered by that same push, builds the site. apps/site/scripts/build-cdn.mjs resolves the advertised version from npm view … dist-tags, which still reads 0.12.0.
  3. 13:52:23Z — the publish finishes and the dist-tag moves.
  4. Nothing rebuilds the site, so the stale manifest is served until the next unrelated push to main.

verify-release-consistency.mjs already noticed this and only logged CDN is behind npm … it catches up on the next push to main. It failed solely when the CDN was ahead, which is why the check stayed green through the whole incident.

What changed

A redeploy is fired after the publish. A new redeploy-cdn job POSTs the existing DOKPLOY_CDN_DEPLOY_WEBHOOK secret. It is ordered after publish-native-assets, because latest.json only gets its per-platform platforms block once the native zips exist on the release.

That job needs always(): publish-native-assets skips on an npm-only release, and a job whose needs includes a skipped job is skipped too. always() then removes the implicit success requirement, so the upstream results are asserted explicitly — it runs on a normal release and on an npm-only release, and not when the release failed or was cancelled.

The webhook step cannot fail the workflow. npm has already published by the time it runs, so dying on a flaky POST buys nothing — an earlier version of this job was deleted precisely because a curl exit-28 timeout failed the 0.5.0 release. It retries, then degrades to a warning. The webhook also matches the branch from the request body, so it sends Content-Type: application/json with {"ref":"refs/heads/main"}; a bare POST answers 301 Branch Not Match and deploys nothing.

The consistency check became a real gate. verify-cdn-release now runs after redeploy-cdn and polls until the manifest matches npm, failing when it never does. The previously warn-and-skip "CDN unreachable" branch folds into the same mechanism: a fetch error, a non-ok status and an unparseable manifest are all treated as lag and retried, since mid-deploy the origin is legitimately unreachable. ahead still fails at once, because more waiting cannot fix a manifest naming a release npm does not have. The 10-minute budget sits under the job's 15-minute timeout so a stale CDN is reported rather than killed by the runner.

The poll logic lives in a new scripts/release/cdn-consistency.mjs with fetch, sleep and clock injected, so it is unit tested with no network and no real waiting.

Unrelated to the above, in the same branch: background task status lines rendered dot and wording in periwinkle while running and both in green on completion, so an ambient background task drew as much attention as the work the user asked for. The wording is now dim on every phase and only the bullet carries status — dim while running, green on completion, red on failure.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

Summary by CodeRabbit

  • UI Improvements

    • Background task status wording is now dimmed for better visual hierarchy.
    • Status dots remain color-coded to indicate started, completed, or failed states.
  • Release Reliability

    • CDN deployments are automatically triggered after package publishing.
    • Release verification now waits for the CDN to reflect the published version.
    • CDN delays, malformed responses, and temporary failures are handled with polling and clear release checks.
  • Tests

    • Added coverage for CDN version synchronization and background task status styling.

elkaix added 3 commits August 8, 2026 11:45
The site autodeploy fires on the push that starts the release, but
build-cdn.mjs reads the version from npm dist-tags, which only moves when
the publish finishes. The first build baked in the previous version and
nothing rebuilt it, so 0.13.0 stayed invisible to every installed client
while CI reported success.

Add a redeploy-cdn job that fires the Dokploy webhook after
publish-native-assets, and turn the consistency check from a warning into
a poll that fails when the CDN never catches up.
The started line rendered dot and wording in periwinkle and the completed
line rendered both in green, so an ambient background task drew as much
attention as the work the user asked for.

Keep the wording dim on every phase and colour the bullet alone: dim while
running, green on completion, red on failure.
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 46 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 10b59f7b-80a3-4732-a871-c0b4aab807d6

📥 Commits

Reviewing files that changed from the base of the PR and between ec62c16 and dc94f1d.

📒 Files selected for processing (1)
  • scripts/release/cdn-consistency.mjs
📝 Walkthrough

Walkthrough

The release workflow redeploys the CDN after publishing and polls until the CDN matches npm. CDN-ahead and timeout states fail verification. Background task status wording is dimmed while status bullets retain phase-specific colors.

Changes

CDN release consistency

Layer / File(s) Summary
CDN consistency classification and polling
scripts/release/cdn-consistency.mjs, apps/pythinker-code/test/scripts/release/cdn-consistency.test.ts
Adds stable version comparison, CDN state classification, manifest validation, bounded polling, and tests for success, retries, ahead states, and timeouts.
Release redeployment and verification wiring
scripts/release/verify-release-consistency.mjs, .github/workflows/release.yml
Adds CDN polling to release verification and triggers Dokploy redeployment before verification.

Background task status styling

Layer / File(s) Summary
Background status rendering and validation
apps/pythinker-code/src/tui/components/messages/background-agent-status.ts, apps/pythinker-code/test/tui/components/messages/background-agent-status.test.ts, .changeset/dim-background-task-status-wording.md
Dims status wording, preserves phase-specific bullet colors, and adds styling tests and a patch changeset.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Release
  participant Dokploy
  participant CDN
  participant Verification
  Release->>Dokploy: trigger CDN redeployment
  Dokploy-->>Release: return webhook result
  Release->>Verification: start CDN consistency check
  Verification->>CDN: poll release manifest
  CDN-->>Verification: return CDN version
  Verification-->>Release: report match, ahead, or timeout
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title uses the valid fix(ci) prefix, imperative wording, stays within 72 characters, and accurately summarizes the main CI and CDN changes.
Description check ✅ Passed The description includes all required sections, explains the problem and solution, and marks the checklist items as complete.
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.

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

@pkg-pr-new

pkg-pr-new Bot commented Aug 8, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@pythoughts/pythinker-code@dc94f1d
npx https://pkg.pr.new/@pythoughts/pythinker-code@dc94f1d

commit: dc94f1d

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (1)
apps/pythinker-code/test/tui/components/messages/background-agent-status.test.ts (1)

67-68: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the conditional fallbacks from the render lookup.

Oxlint reports vitest(no-conditional-in-test) on Lines 67-68. Read the rendered values directly and assert that they are defined before checking their contents. This keeps a missing render line explicit.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/pythinker-code/test/tui/components/messages/background-agent-status.test.ts`
around lines 67 - 68, Update the render lookups in the test around started and
completed to read the indexed rendered lines directly without conditional
fallback operators. Assert both retrieved lines are defined before checking
their contents, preserving explicit failure when the expected render line is
missing.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release.yml:
- Around line 238-250: The webhook handling block must validate WEBHOOK starts
with https:// before invoking curl. Add a guard after the empty-value check that
skips the deployment and exits successfully for any non-HTTPS URL, leaving curl
unreachable in that case.
- Around line 250-254: Update the curl invocation in the CDN redeploy webhook
step to include the --fail option, ensuring HTTP error responses produce a
nonzero status and trigger the existing retry and warning behavior.

In
`@apps/pythinker-code/test/tui/components/messages/background-agent-status.test.ts`:
- Line 59: Update the completed-detail fixture in the background-agent status
test to replace the non-ASCII middle dot with an ASCII separator, keeping
Unicode values out of this non-Unicode test.

In `@scripts/release/cdn-consistency.mjs`:
- Around line 68-78: Update the polling flow around readCdnVersion and the
classification === 'match' return to check now() >= deadline after the fetch
completes, rejecting or continuing according to the existing deadline behavior
before accepting a match. Add a fake-clock test covering a fetch that completes
after the deadline and verifies the release gate does not pass.
- Around line 19-24: Update compareRelease in
scripts/release/cdn-consistency.mjs to avoid Number conversion: compare each
numeric identifier by digit-string length first, then lexicographically when
lengths match, preserving release ordering without precision loss. Add a
regression assertion in
apps/pythinker-code/test/scripts/release/cdn-consistency.test.ts lines 44-51
covering adjacent identifiers beyond safe-integer precision.

---

Nitpick comments:
In
`@apps/pythinker-code/test/tui/components/messages/background-agent-status.test.ts`:
- Around line 67-68: Update the render lookups in the test around started and
completed to read the indexed rendered lines directly without conditional
fallback operators. Assert both retrieved lines are defined before checking
their contents, preserving explicit failure when the expected render line is
missing.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2e2a4f02-7875-40e7-aadb-78cb4b3d6561

📥 Commits

Reviewing files that changed from the base of the PR and between 95db92a and a13fce0.

📒 Files selected for processing (7)
  • .changeset/dim-background-task-status-wording.md
  • .github/workflows/release.yml
  • apps/pythinker-code/src/tui/components/messages/background-agent-status.ts
  • apps/pythinker-code/test/scripts/release/cdn-consistency.test.ts
  • apps/pythinker-code/test/tui/components/messages/background-agent-status.test.ts
  • scripts/release/cdn-consistency.mjs
  • scripts/release/verify-release-consistency.mjs

Comment thread .github/workflows/release.yml Outdated
Comment thread .github/workflows/release.yml Outdated
Comment thread scripts/release/cdn-consistency.mjs
Comment thread scripts/release/cdn-consistency.mjs
elkaix added 2 commits August 8, 2026 12:03
Reject a non-https webhook URL before curl sees it, and add --fail so an
HTTP error status reaches the retries and the warning instead of exiting 0.
Compare release identifiers with BigInt so two versions past 2^53 cannot
round to the same float and read as a match. Drop the conditional fallbacks
from the status colour test.
@elkaix

elkaix commented Aug 8, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai All 5 inline findings triaged and every thread resolved, through dc94f1da29c9172675eae85e821db6a327d0fd8b.

Fixed (3)

  • Non-HTTPS webhook URL is now rejected before curl runs, so the deploy credential cannot go out in cleartext.
  • Added --fail so an HTTP error status reaches the retries and the warning rather than exiting 0.
  • compareRelease now uses BigInt, with a regression test over 9007199254740992 vs 9007199254740993.

Dismissed with rationale on-thread (2) — the ASCII-fixture nit (the middle dot is the separator the component itself emits at background-task-status.ts:89, and predates this PR at lines 28 and 46 of the same file) and the post-fetch deadline check (a late match still proves consistency; the overshoot is bounded by one 15s fetch against a 15-minute job timeout).

Nitpickvitest(no-conditional-in-test) on lines 67-68 was real; oxlint confirmed it locally. The ?? '' fallbacks are gone, replaced by joining the rendered lines, which also removes the index fragility. oxlint --type-aware on that file is now clean.

Docstring coverage — documented the one genuinely undocumented function in the new module (readCdnVersion). The rest of the 60% figure is test helpers, where the repo does not require docstrings; the check is advisory and non-blocking.

Each behaviour change above is covered by a test that was mutation-verified to fail when the fix is reverted.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/release.yml (1)

279-281: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Run verify-cdn-release when redeploy-cdn is skipped.

Add always() and require needs.release.result == 'success' in the job condition. Otherwise, a skipped redeploy-cdn skips the CDN consistency gate.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 279 - 281, Update the
verify-cdn-release job condition to use always() while requiring
needs.release.result == 'success', so it still runs when redeploy-cdn is
skipped. Preserve the existing needs.release and needs.redeploy-cdn
dependencies.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In @.github/workflows/release.yml:
- Around line 279-281: Update the verify-cdn-release job condition to use
always() while requiring needs.release.result == 'success', so it still runs
when redeploy-cdn is skipped. Preserve the existing needs.release and
needs.redeploy-cdn dependencies.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8cff521c-cf36-4743-a05c-7b6c7f20eb8f

📥 Commits

Reviewing files that changed from the base of the PR and between a13fce0 and ec62c16.

📒 Files selected for processing (4)
  • .github/workflows/release.yml
  • apps/pythinker-code/test/scripts/release/cdn-consistency.test.ts
  • apps/pythinker-code/test/tui/components/messages/background-agent-status.test.ts
  • scripts/release/cdn-consistency.mjs
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/pythinker-code/test/tui/components/messages/background-agent-status.test.ts

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

git show --no-ext-diff --stat --oneline dc94f1da29c9172675eae85e821db6a327d0fd8b
git show --no-ext-diff --format=fuller --find-renames \
  dc94f1da29c9172675eae85e821db6a327d0fd8b -- \
  .github/workflows/release.yml \
  scripts/release/cdn-consistency.mjs \
  scripts/release/verify-release-consistency.mjs \
  apps/pythinker-code/test/scripts/release/cdn-consistency.test.ts \
  apps/pythinker-code/test/tui/components/messages/background-agent-status.test.ts \
  apps/pythinker-code/src/tui/components/messages/background-agent-status.ts

printf '\n--- Relevant declarations and assertions ---\n'
rg -n -C 4 \
  'compareRelease|BigInt|readCdnVersion|https:|--fail|DOKPLOY|WEBHOOK|9007199254740992|9007199254740993|rendered.*join|join\(.*render' \
  .github/workflows/release.yml \
  scripts/release/cdn-consistency.mjs \
  apps/pythinker-code/test/scripts/release/cdn-consistency.test.ts \
  apps/pythinker-code/test/tui/components/messages/background-agent-status.test.ts

Length of output: 12971


@elkaix Verified.

The workflow rejects non-HTTPS webhook URLs before curl sends the credential. curl --fail now makes HTTP error responses enter the retry and warning path.

compareRelease now uses BigInt. The regression test covers the IEEE-754 boundary values.

The readCdnVersion docstring is present. The stated rationale for the fixture separator and post-fetch deadline behavior is valid.


✏️ Learnings added
Learnt from: elkaix
URL: https://github.com/Pythoughts-labs/pythinker-code/pull/43

Timestamp: 2026-08-08T16:07:04.222Z
Learning: In `apps/pythinker-code/src/tui/components/messages/background-agent-status.ts`, the middle-dot character is the component's intentional status separator. Tests for this component must preserve that character in expected output.

Learnt from: elkaix
URL: https://github.com/Pythoughts-labs/pythinker-code/pull/43

Timestamp: 2026-08-08T16:07:04.222Z
Learning: In `scripts/release/cdn-consistency.mjs`, a CDN manifest match remains valid when the fetch completes after the nominal polling deadline. The possible overshoot is bounded by one fetch, while the GitHub Actions job timeout provides the outer limit.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

@elkaix
elkaix merged commit 0ea74d4 into main Aug 8, 2026
11 checks passed
@elkaix
elkaix deleted the fix/cdn-redeploy-after-release branch August 8, 2026 16:14
elkaix pushed a commit that referenced this pull request Aug 8, 2026
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @pythoughts/pythinker-code@0.13.1

### Patch Changes

- [#43](#43)
[`0ea74d4`](0ea74d4)
- Dim the wording on background task status lines so only the status dot
is coloured.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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