diff --git a/.agents/skills/merge/fix-tests.md b/.agents/skills/merge/fix-tests.md index 150183227818..52f0d5813adc 100644 --- a/.agents/skills/merge/fix-tests.md +++ b/.agents/skills/merge/fix-tests.md @@ -17,7 +17,17 @@ This skill follows a "fix and push" approach. After pushing, CI will re-run auto ## Steps -### Step 1: Get CI failure logs +### Step 1: Build all packages + +Before analyzing test failures, ensure all packages are built: + +```bash +pnpm build +``` + +Fix any build errors first — these may be the cause of test failures downstream. + +### Step 2: Get CI failure logs Use the GitHub CLI to find the failed CI run and download its logs: @@ -35,7 +45,7 @@ Parse the output to identify: - The specific test names that failed - The error messages and assertion diffs -### Step 2: Analyze failures +### Step 3: Analyze failures For each failure, determine the cause: @@ -47,7 +57,7 @@ For each failure, determine the cause: 4. **Configuration mismatches** — Test fixtures using config options that changed on `next`. Fix by updating the fixture config. -### Step 3: Fix the failures +### Step 4: Fix the failures For each failure: @@ -68,7 +78,7 @@ For each failure: - Smoke tests — these are allowed to fail - `astro check` failures — these are permitted to fail -### Step 4: Verify fixes locally (targeted only) +### Step 5: Verify fixes locally (targeted only) Only run the specific tests you fixed, not the full suite: @@ -78,7 +88,7 @@ pnpm -C exec astro-scripts test "test/.test.j This is a quick sanity check, not a replacement for CI. CI will do the full validation after you push. -### Step 5: Rebuild if source files were modified +### Step 6: Rebuild if source files were modified If you modified any source files in `packages/` (not just test files), rebuild the affected package: @@ -88,7 +98,7 @@ pnpm -C packages/ build Then re-run the specific affected tests to confirm. -### Step 6: Ensure dependencies are up to date +### Step 7: Ensure dependencies are up to date If the merge introduced new dependencies or changed versions, run: diff --git a/.agents/skills/merge/resolve-conflicts.md b/.agents/skills/merge/resolve-conflicts.md index f891a609eb94..ac3f1f06b866 100644 --- a/.agents/skills/merge/resolve-conflicts.md +++ b/.agents/skills/merge/resolve-conflicts.md @@ -1,6 +1,6 @@ -# Resolve Merge Conflicts +# Verify Merge Conflict Resolutions -Resolve merge conflicts from merging `main` into the `next` branch. +Verify that the automated conflict resolution (which kept the "ours"/next side for JSON and YAML files) was correct, and fix any issues. **SCOPE: Do not spawn tasks/sub-agents.** @@ -8,50 +8,66 @@ Resolve merge conflicts from merging `main` into the `next` branch. - **`prNumber`** — The PR number for the merge PR. - **`branch`** — The branch name (e.g., `ci/merge-main-to-next`). -- The working directory is the repo root, checked out on the merge branch with conflict markers present. +- The working directory is the repo root, checked out on the merge branch. +- Dependencies are installed and packages are built. +- Conflict markers in JSON/YAML files have already been stripped by the GitHub Action, keeping the "ours" (next) side. Source code files (`.ts`, `.js`, `.md`, `.astro`) may still have conflict markers. -## Strategy +## Background -When resolving conflicts, follow these rules based on file type: +The GitHub Action strips conflict markers from JSON/YAML files before `pnpm install` can run, always keeping the `next` branch side. This is usually correct (next has pre-release versions that must be preserved), but it may discard important changes from `main` — like new dependencies, updated dependency versions, or config changes from bug fixes. -### Changeset files (`.changeset/*.md`) - -- If a changeset file exists on `main` but not on `next`, it was likely already released. **Delete it.** The clean-changesets skill will handle this more thoroughly, but removing obvious ones here unblocks the merge. -- If both branches modified the same changeset, prefer the `next` version since it's the one targeting the upcoming major. - -### `package.json` and version files +## Steps -- **Prefer `next` branch versions.** The `next` branch has the pre-release versions (alpha/beta) which should not be overwritten by `main`'s stable versions. -- For dependency version updates from `main`, accept those — they are typically patches/minors that should be forward-ported. +### Step 1: Resolve any remaining conflict markers -### `pnpm-lock.yaml` +Check for conflict markers in source code files: -- Do not try to manually resolve lockfile conflicts. Instead: - 1. Accept the `next` version: `git checkout --theirs pnpm-lock.yaml` - 2. The lockfile will be regenerated after `pnpm install` later. +```bash +grep -r "<<<<<<< " --include="*.ts" --include="*.js" --include="*.mjs" --include="*.cjs" --include="*.md" --include="*.astro" . 2>/dev/null | grep -v node_modules +``` -### Source code (`packages/**/*.ts`, `packages/**/*.js`) +If any exist, resolve them following these rules: - **Prefer `main` for bug fixes.** If `main` fixed a bug, that fix should carry over to `next`. -- **Prefer `next` for API changes.** If `next` changed an API (new major version features), keep the `next` version and adapt the `main` fix to work with it if needed. +- **Prefer `next` for API changes.** If `next` changed an API, keep the `next` version and adapt the `main` fix if needed. - When in doubt, prefer `next` — it's the forward-looking branch. -### Test files +After resolving, `git add` each file. -- If tests conflict, prefer `next` and adapt. Tests on `next` may test new major-version behavior. +### Step 2: Review what was lost from main -### Configuration files (`.changeset/config.json`, `tsconfig.json`, etc.) +Compare what `main` had in the conflicted files against what we kept: -- Prefer `next` unless `main` has a clear fix that should be forward-ported. +```bash +# List files that were modified on both branches (potential conflict sites) +git diff --name-only origin/next...origin/main -- '*.json' '*.yaml' '*.yml' +``` -## Steps +For each file, compare the `main` version to the current version: + +```bash +git diff origin/main -- +``` + +Look for: + +- **New dependencies** added on `main` that are missing from `next` — these should be added +- **Dependency version bumps** on `main` that are higher than what's on `next` — these should typically be accepted (they're patches/minors) +- **New scripts or config entries** added on `main` — these should be forward-ported +- **Version fields** (`"version":`) — keep `next`'s pre-release versions, do NOT use `main`'s stable versions + +### Step 3: Apply corrections + +If you find changes from `main` that should have been kept: + +1. Apply those specific changes to the current files +2. Run `pnpm install --no-frozen-lockfile` if you modified any `package.json` files +3. `git add` the modified files + +### Step 4: Do NOT commit -1. Run `git diff --name-only --diff-filter=U` to list all conflicted files. -2. For each conflicted file, read the file and resolve the conflict following the strategy above. -3. After resolving each file, run `git add ` to mark it as resolved. -4. After all files are resolved, verify no conflict markers remain: `grep -r "<<<<<<< " --include="*.ts" --include="*.js" --include="*.json" --include="*.yaml" --include="*.yml" --include="*.md" .` -5. Do NOT commit — the orchestrator will handle committing. +The orchestrator will handle committing. ## Output -Return the list of files that were resolved and whether all conflicts were successfully handled. +Return whether the conflict resolutions were correct and what files (if any) needed corrections. diff --git a/.flue/workflows/merge-fix/WORKFLOW.ts b/.flue/workflows/merge-fix/WORKFLOW.ts index eb77a3637974..754b49dea01b 100644 --- a/.flue/workflows/merge-fix/WORKFLOW.ts +++ b/.flue/workflows/merge-fix/WORKFLOW.ts @@ -26,30 +26,25 @@ export const args = v.object({ export default async function mergeFix(flue: FlueClient, { prNumber }: v.InferOutput) { const branch = 'ci/merge-main-to-next'; - // Step 1: Check for merge conflicts (unresolved conflict markers in files) - const conflictCheck = await flue.shell( - 'git diff --check HEAD 2>&1 || grep -r "<<<<<<< " --include="*.ts" --include="*.js" --include="*.json" --include="*.yaml" --include="*.yml" --include="*.md" --include="*.mjs" --include="*.cjs" . 2>/dev/null | head -20', - ); - const hasConflicts = conflictCheck.stdout.includes('<<<<<<<'); - - // Step 2: Resolve conflicts if any - if (hasConflicts) { - await flue.skill('merge/resolve-conflicts.md', { - args: { prNumber, branch }, - result: v.object({ - resolved: v.pipe( - v.boolean(), - v.description('true if all merge conflicts were resolved successfully'), - ), - filesResolved: v.pipe( - v.array(v.string()), - v.description('List of files where conflicts were resolved'), - ), - }), - }); - } + // Step 1: Verify conflict resolutions and resolve any remaining source code conflicts. + // JSON/YAML conflicts were pre-stripped by the GitHub Action (keeping next side). + // This skill checks that nothing important from main was lost, and resolves + // any remaining conflict markers in .ts/.js/.md/.astro files. + const verifyResult = await flue.skill('merge/resolve-conflicts.md', { + args: { prNumber, branch }, + result: v.object({ + correct: v.pipe( + v.boolean(), + v.description('true if all conflict resolutions were correct or have been fixed'), + ), + correctedFiles: v.pipe( + v.array(v.string()), + v.description('List of files that needed corrections after verification'), + ), + }), + }); - // Step 3: Remove stale changesets that were already released on main + // Step 2: Remove stale changesets that were already released on main await flue.skill('merge/clean-changesets.md', { args: { prNumber }, result: v.object({ @@ -60,7 +55,7 @@ export default async function mergeFix(flue: FlueClient, { prNumber }: v.InferOu }), }); - // Step 4: Run tests and fix failures + // Step 3: Run tests and fix failures const fixResult = await flue.skill('merge/fix-tests.md', { args: { prNumber }, result: v.object({ @@ -78,13 +73,13 @@ export default async function mergeFix(flue: FlueClient, { prNumber }: v.InferOu }), }); - // Step 5: Commit and push all changes + // Step 4: Commit and push all changes const status = await flue.shell('git status --porcelain'); if (status.stdout.trim()) { await flue.shell('git add -A'); const commitParts = []; - if (hasConflicts) commitParts.push('resolve merge conflicts'); + if (verifyResult.correctedFiles.length > 0) commitParts.push('fix merge conflict resolutions'); if (fixResult.fixedFiles.length > 0) commitParts.push('fix test failures'); const commitMsg = commitParts.length > 0 @@ -100,9 +95,11 @@ export default async function mergeFix(flue: FlueClient, { prNumber }: v.InferOu } } - // Step 6: Post a summary comment on the PR + // Step 5: Post a summary comment on the PR const summaryParts = []; - if (hasConflicts) summaryParts.push('- Resolved merge conflicts'); + if (verifyResult.correctedFiles.length > 0) { + summaryParts.push(`- Fixed conflict resolutions in: ${verifyResult.correctedFiles.join(', ')}`); + } if (fixResult.fixedFiles.length > 0) { summaryParts.push(`- Fixed test failures in: ${fixResult.fixedFiles.join(', ')}`); } @@ -134,7 +131,7 @@ ${fixResult.testsPass ? 'All tests pass — this PR should be ready for review.' return { pushed: true, testsPass: fixResult.testsPass, - hasConflicts, + correctedFiles: verifyResult.correctedFiles, remainingFailures: fixResult.remainingFailures, }; } diff --git a/.github/workflows/merge-fix.yml b/.github/workflows/merge-fix.yml index 239235776aa7..e5840acc434c 100644 --- a/.github/workflows/merge-fix.yml +++ b/.github/workflows/merge-fix.yml @@ -16,7 +16,6 @@ on: env: IMAGE: ghcr.io/${{ github.repository }}/flue-sandbox - PNPM_STORE_DIR: .pnpm-store concurrency: group: merge-fix @@ -91,6 +90,37 @@ jobs: echo "skip=false" >> "$GITHUB_OUTPUT" fi + - name: Strip conflict markers from JSON/YAML files + if: steps.pr.outputs.number != '' && steps.attempts.outputs.skip != 'true' + # Merge conflicts in package.json files make pnpm install impossible. + # Strip conflict markers (keeping "ours"/next side) so pnpm can parse + # the workspace. The Flue verify skill will check via git diff whether + # anything important from main was lost and patch it back in. + run: | + node -e ' + const fs = require("fs"); + const path = require("path"); + let count = 0; + function fix(dir) { + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + const full = path.join(dir, entry.name); + if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== ".git") { + fix(full); + } else if (/\.(json|yaml|yml)$/.test(entry.name)) { + let c = fs.readFileSync(full, "utf8"); + if (c.includes("<<<<<<<")) { + c = c.replace(/^<{7}.*\n([\s\S]*?)^={7}\n[\s\S]*?^>{7}.*\n/gm, "$1"); + fs.writeFileSync(full, c); + console.log("Stripped conflicts:", full); + count++; + } + } + } + } + fix("."); + console.log(count + " file(s) fixed"); + ' + - name: Setup PNPM if: steps.pr.outputs.number != '' && steps.attempts.outputs.skip != 'true' uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0 @@ -104,8 +134,6 @@ jobs: - name: Install deps if: steps.pr.outputs.number != '' && steps.attempts.outputs.skip != 'true' - # Use --no-frozen-lockfile because the merge may have introduced - # lockfile conflicts or new dependencies that need resolving run: pnpm install --no-frozen-lockfile - name: Build diff --git a/packages/astro/CHANGELOG.md b/packages/astro/CHANGELOG.md index 7c2f4882bcf3..0d3cff73e004 100644 --- a/packages/astro/CHANGELOG.md +++ b/packages/astro/CHANGELOG.md @@ -20,6 +20,42 @@ - [#16434](https://github.com/withastro/astro/pull/16434) [`ee079d4`](https://github.com/withastro/astro/commit/ee079d4c7f143076b84d663c832911009a077c7f) Thanks [@ematipico](https://github.com/ematipico)! - Fixes an issue where i18n domains would return 404 when `trailingSlash` is set to `never`. +## 6.2.2 + +### Patch Changes + +- [#16292](https://github.com/withastro/astro/pull/16292) [`00f48ee`](https://github.com/withastro/astro/commit/00f48ee25fdc072df93210fa2d6d24ea649d4ab1) Thanks [@p-linnane](https://github.com/p-linnane)! - Fixes head metadata propagation in dev for adapters that load modules in the `prerender` Vite environment, such as `@astrojs/cloudflare`. The `astro:head-metadata` plugin previously only tracked the `ssr` environment, so `maybeRenderHead()` could fire inside an unrelated component's `