fix(core): save external permissions at project root
This commit is contained in:
@@ -5,6 +5,8 @@ import path from "path"
|
|||||||
import { Context, Effect, Layer, Schema } from "effect"
|
import { Context, Effect, Layer, Schema } from "effect"
|
||||||
import { FSUtil } from "./fs-util"
|
import { FSUtil } from "./fs-util"
|
||||||
import { Location } from "./location"
|
import { Location } from "./location"
|
||||||
|
import { Project } from "./project"
|
||||||
|
import { AbsolutePath } from "./schema"
|
||||||
|
|
||||||
export const Kind = Schema.Literals(["file", "directory"])
|
export const Kind = Schema.Literals(["file", "directory"])
|
||||||
export type Kind = typeof Kind.Type
|
export type Kind = typeof Kind.Type
|
||||||
@@ -143,7 +145,12 @@ export const layer = Layer.effect(
|
|||||||
action: "external_directory",
|
action: "external_directory",
|
||||||
directory: externalDirectory,
|
directory: externalDirectory,
|
||||||
resource: externalResource,
|
resource: externalResource,
|
||||||
save: externalResource,
|
save: slash(
|
||||||
|
path.join(
|
||||||
|
(yield* Project.root(fs, AbsolutePath.make(externalDirectory))) ?? externalDirectory,
|
||||||
|
"*",
|
||||||
|
),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
} satisfies Target
|
} satisfies Target
|
||||||
|
|||||||
@@ -40,6 +40,17 @@ export interface Resolved {
|
|||||||
readonly vcs?: Vcs
|
readonly vcs?: Vcs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep this filesystem-only; permission checks use it and should not execute VCS commands.
|
||||||
|
export const root = Effect.fn("Project.root")(function* (
|
||||||
|
fs: FSUtil.Interface,
|
||||||
|
input: AbsolutePath,
|
||||||
|
) {
|
||||||
|
return yield* fs.up({ targets: [".git"], start: input }).pipe(
|
||||||
|
Effect.map((matches) => matches[0] ? AbsolutePath.make(path.dirname(matches[0])) : undefined),
|
||||||
|
Effect.catch(() => Effect.succeed(undefined)),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
export interface Interface {
|
export interface Interface {
|
||||||
readonly directories: (input: DirectoriesInput) => Effect.Effect<Directories>
|
readonly directories: (input: DirectoriesInput) => Effect.Effect<Directories>
|
||||||
readonly resolve: (input: AbsolutePath) => Effect.Effect<Resolved>
|
readonly resolve: (input: AbsolutePath) => Effect.Effect<Resolved>
|
||||||
|
|||||||
@@ -230,6 +230,40 @@ describe("WriteTool", () => {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
it.live("saves external directory approval at the nearest project directory", () =>
|
||||||
|
Effect.acquireUseRelease(
|
||||||
|
Effect.promise(() => Promise.all([tmpdir(), tmpdir()])),
|
||||||
|
([active, outside]) => {
|
||||||
|
reset()
|
||||||
|
const repo = path.join(outside.path, "repo")
|
||||||
|
const nested = path.join(repo, "packages", "app")
|
||||||
|
const target = path.join(nested, "external.txt")
|
||||||
|
return Effect.promise(() =>
|
||||||
|
Promise.all([fs.mkdir(path.join(repo, ".git"), { recursive: true }), fs.mkdir(nested, { recursive: true })]),
|
||||||
|
).pipe(
|
||||||
|
Effect.andThen(
|
||||||
|
withTool(active.path, (registry) => executeTool(registry, call({ path: target, content: "external" }))),
|
||||||
|
),
|
||||||
|
Effect.andThen(
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const canonicalRepo = yield* Effect.promise(() => fs.realpath(repo))
|
||||||
|
const canonicalNested = yield* Effect.promise(() => fs.realpath(nested))
|
||||||
|
expect(assertions[0]).toMatchObject({
|
||||||
|
action: "external_directory",
|
||||||
|
resources: [path.join(canonicalNested, "*").replaceAll("\\", "/")],
|
||||||
|
save: [path.join(canonicalRepo, "*").replaceAll("\\", "/")],
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
([active, outside]) =>
|
||||||
|
Effect.promise(() =>
|
||||||
|
Promise.all([active[Symbol.asyncDispose](), outside[Symbol.asyncDispose]()]).then(() => undefined),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
it.live("does not write when external_directory or edit approval is denied", () =>
|
it.live("does not write when external_directory or edit approval is denied", () =>
|
||||||
Effect.acquireUseRelease(
|
Effect.acquireUseRelease(
|
||||||
Effect.promise(() => Promise.all([tmpdir(), tmpdir()])),
|
Effect.promise(() => Promise.all([tmpdir(), tmpdir()])),
|
||||||
|
|||||||
Reference in New Issue
Block a user