Skip to content

chore: upgrade Astro 6.4.8 → 7.0.2#838

Open
riderx wants to merge 1 commit into
mainfrom
chore/astro-7-upgrade
Open

chore: upgrade Astro 6.4.8 → 7.0.2#838
riderx wants to merge 1 commit into
mainfrom
chore/astro-7-upgrade

Conversation

@riderx

@riderx riderx commented Jun 23, 2026

Copy link
Copy Markdown
Member

Summary

  • Upgrades astro to 7.0.2 (Rust compiler, Vite 8/Rolldown) and bumps @astrojs/cloudflare to 14.0.0 / @astrojs/mdx to 7.0.0 for docs.
  • Fixes native-build.astro for Astro 7’s stricter HTML parser by removing a multi-line HTML comment that broke the Rust compiler after a large <script> block.
  • Adds scripts/visual-diff.mjs (Playwright + ImageMagick) to capture/compare representative web + docs pages during framework upgrades.

Build impact

App Astro 6.4.8 Astro 7.0.2
docs (481 pages) ~3m 34s ~50s
web (582 pages) ~1m+ ~17s
full bun run build ~5m+ ~1m

Visual diff (before → after)

Captured 18 screenshots (7 web + 3 docs routes × desktop + mobile) from production builds served statically.

Result Pages
Pixel-identical (0 diff) /solutions/live-updates/, /docs/plugins/updater/
Minor diff (<6k px) /about/, /plugins/, /pricing/, /docs/getting-started/quickstart/ (mobile)
Small diff /, /docs/, docs quickstart desktop
Larger diff /blog/ (dynamic post listing), home mobile (full-page capture variance)

No layout-breaking regressions observed; largest diffs align with dynamic blog content and subpixel/font rendering rather than broken pages.

Re-run locally:

bun run build
bun run scripts/visual-diff.mjs capture before   # before upgrade baseline
# upgrade, rebuild
bun run scripts/visual-diff.mjs capture after
bun run scripts/visual-diff.mjs compare

Test plan

  • bun run build (docs + web)
  • bun run ci:verify:web (check + build + SEO checker)
  • bun run ci:verify:docs (check + build)
  • Visual diff on 18 representative routes (see above)
  • Spot-check /native-build/ in preview (page touched for compiler fix)

Made with Cursor

Summary by CodeRabbit

Release Notes

  • New Features

    • Added scroll-triggered animations to enhance visual experience and engagement across key sections of the site.
  • Chores

    • Updated framework and build dependencies to latest versions for improved performance and compatibility.
    • Enhanced development infrastructure with visual regression testing capabilities.

Moves to Astro 7's Rust compiler and Vite 8/Rolldown, updates Cloudflare and MDX integrations, fixes native-build.astro for stricter HTML parsing, and adds a Playwright visual-diff script for upgrade regression checks.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Bumps astro to 7.0.2, @astrojs/cloudflare to 14.0.0, and @astrojs/mdx to 7.0.0 in root and apps/docs package.json. Removes a GSAP/ScrollTrigger block from native-build.astro. Adds playwright as a dev dependency and a new scripts/visual-diff.mjs script for automated screenshot capture and pixel-diff comparison. Updates .gitignore with .wrangler and .visual-diff/.

Changes

Astro v7 Upgrade and Visual Regression Tooling

Layer / File(s) Summary
Astro v7 dependency bumps and native-build cleanup
package.json, apps/docs/package.json, apps/web/src/pages/native-build.astro
Bumps astro to 7.0.2, @astrojs/cloudflare to 14.0.0, and @astrojs/mdx to 7.0.0 in both package files; removes a GSAP/ScrollTrigger inline script block from native-build.astro.
Visual regression script, Playwright dep, and .gitignore
package.json, .gitignore, scripts/visual-diff.mjs
Adds playwright ^1.61.0 to devDependencies, ignores .wrangler and .visual-diff/, and introduces scripts/visual-diff.mjs with a static HTTP server, Playwright screenshot capture across configurable viewports/routes, ImageMagick pixel-diff comparison, report.json output, and a capture <before|after> / compare CLI interface.

Sequence Diagram(s)

sequenceDiagram
  participant Dev as Developer
  participant CLI as visual-diff.mjs CLI
  participant Server as Static HTTP Server
  participant Browser as Playwright Chromium
  participant IM as ImageMagick compare

  Dev->>CLI: node visual-diff.mjs capture before
  CLI->>Server: startStaticServer(web dist, 4321)
  CLI->>Server: startStaticServer(docs dist, 4322)
  CLI->>Browser: launch, iterate viewports + routes
  Browser->>Server: GET /route
  Server-->>Browser: HTML/assets
  Browser-->>CLI: full-page PNG saved to .visual-diff/before/

  Dev->>CLI: node visual-diff.mjs capture after
  Note over CLI,Browser: same capture flow → .visual-diff/after/

  Dev->>CLI: node visual-diff.mjs compare
  CLI->>IM: compare before.png after.png -metric AE
  IM-->>CLI: pixel diff count
  CLI-->>Dev: report.json + exit code (0=identical, 1=changes)
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • Cap-go/website#813: Also bumps astro and @astrojs/cloudflare in package.json and apps/docs/package.json, making it a direct predecessor to this Astro v7 upgrade.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly matches the primary objective of this pull request: upgrading Astro from 6.4.8 to 7.0.2, which is clearly reflected in the main changeset across multiple dependency updates.
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 docstrings
  • Create stacked PR
  • Commit on current branch

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

@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedplaywright@​1.61.01001001009980
Added@​astrojs/​mdx@​7.0.0991008397100
Updated@​astrojs/​cloudflare@​13.7.0 ⏵ 14.0.0100 +110083 +197 +1100
Updatedastro@​6.4.8 ⏵ 7.0.2881008898100

View full report

@socket-security

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
Obfuscated code: npm astro is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: apps/docs/package.jsonnpm/astro@7.0.2

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/astro@7.0.2. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@cursor cursor 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.

Risk: medium. Cursor Bugbot did not run on this PR; skipped that signal. Docs Build, Web Build + SEO, and other CI checks passed. Approved.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/visual-diff.mjs`:
- Around line 112-115: The capture function accepts a user-controlled label
parameter that is directly concatenated into a file path and used in a recursive
delete operation without validation. This allows path traversal attacks using
sequences like ".." to delete directories outside the intended OUT_DIR location.
Add validation at the beginning of the capture function to restrict the label
parameter and reject any values containing path separators (forward slashes,
backslashes) or parent directory references (".."). Apply the same validation to
the other instance of this vulnerability mentioned at lines 183-190.
- Around line 124-139: The compareImages function returns null when it cannot
parse a valid diff pixel value from stderr, which silently treats errors as
success. Instead of returning null on parsing failure, the function should
validate that the stderr output contains a valid parseable number and throw an
error if it does not. Modify the logic after Number.parseFloat(stderr) to check
if the result is finite, and if not (when isFinite returns false), throw an
error with a descriptive message that includes the actual stderr output. This
ensures that failures in the image comparison process (unreadable diffs, missing
files, or mismatched images) properly fail the regression test rather than being
treated as successful comparisons.
- Around line 63-67: The path traversal vulnerability exists because the
filePath constructed from joining distDir with the decoded pathname is not
validated to ensure it stays within the distDir boundary, allowing attackers to
use .. sequences to escape the directory. After constructing filePath with
path.join(distDir, pathname), use path.resolve() to get the absolute paths of
both filePath and distDir, then use path.relative() to verify that filePath is
actually within distDir by checking that the relative path does not start with
.., and reject the request if it does escape the boundary.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 76493ab0-c53d-4f1b-83d4-e7b90ce818f9

📥 Commits

Reviewing files that changed from the base of the PR and between 84f3ef9 and e5f78e4.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • .gitignore
  • apps/docs/package.json
  • apps/web/src/pages/native-build.astro
  • package.json
  • scripts/visual-diff.mjs
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • Cap-go/capacitor-updater (manual)
💤 Files with no reviewable changes (1)
  • apps/web/src/pages/native-build.astro

Comment thread scripts/visual-diff.mjs
Comment on lines +63 to +67
const url = new URL(req.url || '/', `http://127.0.0.1:${port}`)
let pathname = decodeURIComponent(url.pathname)
if (pathname.endsWith('/')) pathname += 'index.html'
const filePath = path.join(distDir, pathname)
const data = await readFile(filePath)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Prevent path traversal in the local static server.

The request path is decoded and joined directly into distDir without a root-boundary check, so .. traversal can escape the dist folder.

Proposed fix
       const url = new URL(req.url || '/', `http://127.0.0.1:${port}`)
       let pathname = decodeURIComponent(url.pathname)
       if (pathname.endsWith('/')) pathname += 'index.html'
-      const filePath = path.join(distDir, pathname)
+      const filePath = path.resolve(distDir, `.${pathname}`)
+      const distRoot = path.resolve(distDir) + path.sep
+      if (!filePath.startsWith(distRoot)) {
+        res.writeHead(403).end('Forbidden')
+        return
+      }
       const data = await readFile(filePath)
🤖 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 `@scripts/visual-diff.mjs` around lines 63 - 67, The path traversal
vulnerability exists because the filePath constructed from joining distDir with
the decoded pathname is not validated to ensure it stays within the distDir
boundary, allowing attackers to use .. sequences to escape the directory. After
constructing filePath with path.join(distDir, pathname), use path.resolve() to
get the absolute paths of both filePath and distDir, then use path.relative() to
verify that filePath is actually within distDir by checking that the relative
path does not start with .., and reject the request if it does escape the
boundary.

Comment thread scripts/visual-diff.mjs
Comment on lines +112 to +115
async function capture(label) {
const outputDir = path.join(OUT_DIR, label)
await rm(outputDir, { recursive: true, force: true })
await ensureDir(outputDir)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Restrict capture labels before recursive deletion.

label is user-controlled and is fed into rm(..., { recursive: true, force: true }) without validation. A crafted value can resolve outside .visual-diff and delete unintended directories.

Proposed fix
+const CAPTURE_LABELS = new Set(['before', 'after'])
+
 async function capture(label) {
-  const outputDir = path.join(OUT_DIR, label)
+  const outputDir = path.resolve(OUT_DIR, label)
+  if (!outputDir.startsWith(`${OUT_DIR}${path.sep}`)) {
+    throw new Error(`Invalid capture label: ${label}`)
+  }
   await rm(outputDir, { recursive: true, force: true })
   await ensureDir(outputDir)
@@
 if (command === 'capture') {
-  if (!label) {
+  if (!label || !CAPTURE_LABELS.has(label)) {
     console.error('Usage: bun run scripts/visual-diff.mjs capture <before|after>')
     process.exit(1)
   }
   await capture(label)

Also applies to: 183-190

🤖 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 `@scripts/visual-diff.mjs` around lines 112 - 115, The capture function accepts
a user-controlled label parameter that is directly concatenated into a file path
and used in a recursive delete operation without validation. This allows path
traversal attacks using sequences like ".." to delete directories outside the
intended OUT_DIR location. Add validation at the beginning of the capture
function to restrict the label parameter and reject any values containing path
separators (forward slashes, backslashes) or parent directory references ("..").
Apply the same validation to the other instance of this vulnerability mentioned
at lines 183-190.

Comment thread scripts/visual-diff.mjs
Comment on lines +124 to +139
async function compareImages(beforePath, afterPath) {
const proc = spawn('compare', ['-metric', 'AE', beforePath, afterPath, 'null:'], {
stdio: ['ignore', 'pipe', 'pipe'],
})

const stderr = await new Promise((resolve) => {
let data = ''
proc.stderr.on('data', (chunk) => {
data += chunk.toString()
})
proc.on('close', () => resolve(data.trim()))
})

const diffPixels = Number.parseFloat(stderr)
return Number.isFinite(diffPixels) ? diffPixels : null
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Fail compare mode on invalid diffs and mismatched file sets.

Current logic can return success when diffs are unreadable or before/after screenshots don’t match, which weakens regression gating.

Proposed fix
 async function compareImages(beforePath, afterPath) {
@@
-  const diffPixels = Number.parseFloat(stderr)
-  return Number.isFinite(diffPixels) ? diffPixels : null
+  const diffPixels = Number.parseFloat(stderr)
+  if (!Number.isFinite(diffPixels)) {
+    throw new Error(`Image comparison failed: ${stderr || 'no metric output'}`)
+  }
+  return diffPixels
 }
@@
-  const files = (await readdir(beforeDir)).filter((f) => f.endsWith('.png')).sort()
+  const beforeFiles = (await readdir(beforeDir)).filter((f) => f.endsWith('.png')).sort()
+  const afterFiles = (await readdir(afterDir)).filter((f) => f.endsWith('.png')).sort()
+  if (beforeFiles.length !== afterFiles.length || beforeFiles.some((f, i) => f !== afterFiles[i])) {
+    throw new Error('Before/after screenshot sets do not match. Re-run capture for both labels.')
+  }
+  const files = beforeFiles
@@
-      const status = diffPixels === 0 ? 'identical' : diffPixels === null ? 'unknown' : 'changed'
+      const status = diffPixels === 0 ? 'identical' : 'changed'
@@
-  const changed = results.filter((r) => r.status === 'changed').length
+  const changed = results.filter((r) => r.status === 'changed').length
+  const errored = results.filter((r) => r.status === 'error').length
@@
-  return changed === 0
+  return changed === 0 && errored === 0

Also applies to: 147-180

🤖 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 `@scripts/visual-diff.mjs` around lines 124 - 139, The compareImages function
returns null when it cannot parse a valid diff pixel value from stderr, which
silently treats errors as success. Instead of returning null on parsing failure,
the function should validate that the stderr output contains a valid parseable
number and throw an error if it does not. Modify the logic after
Number.parseFloat(stderr) to check if the result is finite, and if not (when
isFinite returns false), throw an error with a descriptive message that includes
the actual stderr output. This ensures that failures in the image comparison
process (unreadable diffs, missing files, or mismatched images) properly fail
the regression test rather than being treated as successful comparisons.

@cursor cursor Bot requested a review from rishi-raj-jain June 23, 2026 18:24

@cursor cursor 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.

Risk: medium. Did not approve — this Astro 6→7 monorepo upgrade exceeds the low-risk approval threshold. Cursor Bugbot did not run on this PR (signal skipped). Assigned rishi-raj-jain and WcaleNieWolny for human review.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Approver External

@cursor cursor Bot requested a review from WcaleNieWolny June 23, 2026 18:24
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
B Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

Comment thread scripts/visual-diff.mjs
}

async function compareImages(beforePath, afterPath) {
const proc = spawn('compare', ['-metric', 'AE', beforePath, afterPath, 'null:'], {
Comment thread scripts/visual-diff.mjs

if (status === 'changed') {
const diffPath = path.join(diffDir, file.replace('.png', '-diff.png'))
spawn('compare', [beforePath, afterPath, diffPath], { stdio: 'ignore' })
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.

2 participants