fix(watcher): resolve symlinked .git path before subscribing (#27016)
Co-authored-by: Simon Klee <hello@simonklee.dk>
This commit is contained in:
@@ -2,7 +2,7 @@ import { Cause, Effect, Layer, Context, Schema } from "effect"
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { createWrapper } from "@parcel/watcher/wrapper"
|
import { createWrapper } from "@parcel/watcher/wrapper"
|
||||||
import type ParcelWatcher from "@parcel/watcher"
|
import type ParcelWatcher from "@parcel/watcher"
|
||||||
import { readdir } from "fs/promises"
|
import { readdir, realpath } from "fs/promises"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { Bus } from "@/bus"
|
import { Bus } from "@/bus"
|
||||||
import { BusEvent } from "@/bus/bus-event"
|
import { BusEvent } from "@/bus/bus-event"
|
||||||
@@ -131,8 +131,11 @@ export const layer = Layer.effect(
|
|||||||
const result = yield* git.run(["rev-parse", "--git-dir"], {
|
const result = yield* git.run(["rev-parse", "--git-dir"], {
|
||||||
cwd: ctx.worktree,
|
cwd: ctx.worktree,
|
||||||
})
|
})
|
||||||
const vcsDir = result.exitCode === 0 ? path.resolve(ctx.worktree, result.text().trim()) : undefined
|
const resolved = result.exitCode === 0 ? path.resolve(ctx.worktree, result.text().trim()) : undefined
|
||||||
if (vcsDir && !cfgIgnores.includes(".git") && !cfgIgnores.includes(vcsDir)) {
|
const vcsDir = resolved
|
||||||
|
? yield* Effect.promise(() => realpath(resolved).catch(() => resolved))
|
||||||
|
: undefined
|
||||||
|
if (vcsDir && !cfgIgnores.includes(".git") && !cfgIgnores.includes(vcsDir) && (!resolved || !cfgIgnores.includes(resolved))) {
|
||||||
const ignore = (yield* Effect.promise(() => readdir(vcsDir).catch(() => []))).filter(
|
const ignore = (yield* Effect.promise(() => readdir(vcsDir).catch(() => []))).filter(
|
||||||
(entry) => entry !== "HEAD",
|
(entry) => entry !== "HEAD",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -260,4 +260,57 @@ describeWatcher("FileWatcher", () => {
|
|||||||
}),
|
}),
|
||||||
{ git: true },
|
{ git: true },
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Symlink support varies by platform; skip where unavailable
|
||||||
|
const describeSymlink = process.platform !== "win32" ? describe : describe.skip
|
||||||
|
|
||||||
|
describeSymlink("symlinked .git", () => {
|
||||||
|
it.instance(
|
||||||
|
"publishes .git/HEAD events through a symlinked .git directory",
|
||||||
|
() =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const test = yield* TestInstance
|
||||||
|
const fs = yield* AppFileSystem.Service
|
||||||
|
const git = yield* Git.Service
|
||||||
|
const dir = test.directory
|
||||||
|
const actualGit = path.join(dir, "..", "tmp_actual_git_" + Math.random().toString(36).slice(2))
|
||||||
|
|
||||||
|
// Move .git to a sibling directory and replace with a symlink
|
||||||
|
yield* Effect.promise(() => import("fs")).pipe(
|
||||||
|
Effect.flatMap((nodeFs) =>
|
||||||
|
Effect.all([
|
||||||
|
Effect.promise(() => nodeFs.promises.rename(path.join(dir, ".git"), actualGit)),
|
||||||
|
Effect.promise(() => nodeFs.promises.symlink(actualGit, path.join(dir, ".git"))),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
yield* Effect.acquireRelease(
|
||||||
|
Effect.succeed(actualGit),
|
||||||
|
(p) => Effect.promise(() => import("fs").then((f) => f.promises.rm(p, { recursive: true, force: true }).catch(() => undefined))),
|
||||||
|
)
|
||||||
|
|
||||||
|
const head = path.join(dir, ".git", "HEAD")
|
||||||
|
const branch = `watch-${Math.random().toString(36).slice(2)}`
|
||||||
|
yield* git.run(["branch", branch], { cwd: dir })
|
||||||
|
|
||||||
|
yield* withWatcher(
|
||||||
|
dir,
|
||||||
|
nextUpdate(
|
||||||
|
dir,
|
||||||
|
(evt) => evt.file === path.join(actualGit, "HEAD") && evt.event !== "unlink",
|
||||||
|
fs.writeFileString(head, `ref: refs/heads/${branch}\n`),
|
||||||
|
).pipe(
|
||||||
|
Effect.tap((evt) =>
|
||||||
|
Effect.sync(() => {
|
||||||
|
expect(evt.file).toBe(path.join(actualGit, "HEAD"))
|
||||||
|
expect(["add", "change"]).toContain(evt.event)
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
{ git: true },
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user