chore: generate

This commit is contained in:
opencode-agent[bot]
2026-06-02 13:42:23 +00:00
parent 371ee321e0
commit fcfd47602b
5 changed files with 98 additions and 38 deletions
+31 -5
View File
@@ -34,7 +34,12 @@ export interface Interface {
readonly head: (directory: string) => Effect.Effect<string | undefined> readonly head: (directory: string) => Effect.Effect<string | undefined>
readonly branch: (directory: string) => Effect.Effect<string | undefined> readonly branch: (directory: string) => Effect.Effect<string | undefined>
readonly remoteHead: (directory: string) => Effect.Effect<string | undefined> readonly remoteHead: (directory: string) => Effect.Effect<string | undefined>
readonly clone: (input: { remote: string; target: string; branch?: string; depth?: number }) => Effect.Effect<Result, AppProcess.AppProcessError> readonly clone: (input: {
remote: string
target: string
branch?: string
depth?: number
}) => Effect.Effect<Result, AppProcess.AppProcessError>
readonly fetch: (directory: string) => Effect.Effect<Result, AppProcess.AppProcessError> readonly fetch: (directory: string) => Effect.Effect<Result, AppProcess.AppProcessError>
readonly fetchBranch: (directory: string, branch: string) => Effect.Effect<Result, AppProcess.AppProcessError> readonly fetchBranch: (directory: string, branch: string) => Effect.Effect<Result, AppProcess.AppProcessError>
readonly checkout: (directory: string, branch: string) => Effect.Effect<Result, AppProcess.AppProcessError> readonly checkout: (directory: string, branch: string) => Effect.Effect<Result, AppProcess.AppProcessError>
@@ -109,7 +114,10 @@ export const layer = Layer.effect(
}) })
const clone = Effect.fn("Git.clone")((input: { remote: string; target: string; branch?: string; depth?: number }) => const clone = Effect.fn("Git.clone")((input: { remote: string; target: string; branch?: string; depth?: number }) =>
execute(path.dirname(input.target), proc)([ execute(
path.dirname(input.target),
proc,
)([
"clone", "clone",
"--depth", "--depth",
String(input.depth ?? 100), String(input.depth ?? 100),
@@ -134,7 +142,20 @@ export const layer = Layer.effect(
execute(directory, proc)(["reset", "--hard", target]), execute(directory, proc)(["reset", "--hard", target]),
) )
return Service.of({ find, remote, roots, origin, head, branch, remoteHead, clone, fetch, fetchBranch, checkout, reset }) return Service.of({
find,
remote,
roots,
origin,
head,
branch,
remoteHead,
clone,
fetch,
fetchBranch,
checkout,
reset,
})
}), }),
) )
@@ -150,7 +171,8 @@ export interface Result {
} }
function run(cwd: string, proc: AppProcess.Interface) { function run(cwd: string, proc: AppProcess.Interface) {
return (args: string[]) => execute(cwd, proc)(args).pipe(Effect.catch(() => Effect.succeed({ exitCode: 1, text: "", stderr: "" }))) return (args: string[]) =>
execute(cwd, proc)(args).pipe(Effect.catch(() => Effect.succeed({ exitCode: 1, text: "", stderr: "" })))
} }
function execute(cwd: string, proc: AppProcess.Interface) { function execute(cwd: string, proc: AppProcess.Interface) {
@@ -166,7 +188,11 @@ function execute(cwd: string, proc: AppProcess.Interface) {
.pipe( .pipe(
Effect.map( Effect.map(
(result) => (result) =>
({ exitCode: result.exitCode, text: result.stdout.toString("utf8"), stderr: result.stderr.toString("utf8") }) satisfies Result, ({
exitCode: result.exitCode,
text: result.stdout.toString("utf8"),
stderr: result.stderr.toString("utf8"),
}) satisfies Result,
), ),
) )
} }
+56 -30
View File
@@ -30,10 +30,13 @@ export class InvalidRepositoryError extends Schema.TaggedErrorClass<InvalidRepos
}, },
) {} ) {}
export class InvalidBranchError extends Schema.TaggedErrorClass<InvalidBranchError>()("RepositoryCacheInvalidBranchError", { export class InvalidBranchError extends Schema.TaggedErrorClass<InvalidBranchError>()(
branch: Schema.String, "RepositoryCacheInvalidBranchError",
message: Schema.String, {
}) {} branch: Schema.String,
message: Schema.String,
},
) {}
export class CloneFailedError extends Schema.TaggedErrorClass<CloneFailedError>()("RepositoryCacheCloneFailedError", { export class CloneFailedError extends Schema.TaggedErrorClass<CloneFailedError>()("RepositoryCacheCloneFailedError", {
repository: Schema.String, repository: Schema.String,
@@ -64,11 +67,14 @@ export class LockFailedError extends Schema.TaggedErrorClass<LockFailedError>()(
message: Schema.String, message: Schema.String,
}) {} }) {}
export class CacheOperationError extends Schema.TaggedErrorClass<CacheOperationError>()("RepositoryCacheOperationError", { export class CacheOperationError extends Schema.TaggedErrorClass<CacheOperationError>()(
operation: Schema.String, "RepositoryCacheOperationError",
path: Schema.String, {
message: Schema.String, operation: Schema.String,
}) {} path: Schema.String,
message: Schema.String,
},
) {}
export type Error = export type Error =
| InvalidRepositoryError | InvalidRepositoryError
@@ -155,27 +161,35 @@ export const layer: Layer.Layer<
}) })
if (status === "cloned") { if (status === "cloned") {
const result = yield* git.clone({ remote: input.reference.remote, target: localPath, branch: input.branch }).pipe( const result = yield* git
Effect.mapError((error) => new CloneFailedError({ repository, message: errorMessage(error) })), .clone({ remote: input.reference.remote, target: localPath, branch: input.branch })
) .pipe(Effect.mapError((error) => new CloneFailedError({ repository, message: errorMessage(error) })))
if (result.exitCode !== 0) { if (result.exitCode !== 0) {
return yield* new CloneFailedError({ repository, message: resultMessage(result, `Failed to clone ${repository}`) }) return yield* new CloneFailedError({
repository,
message: resultMessage(result, `Failed to clone ${repository}`),
})
} }
} }
if (status === "refreshed") { if (status === "refreshed") {
const fetch = yield* git.fetch(localPath).pipe( const fetch = yield* git
Effect.mapError((error) => new FetchFailedError({ repository, message: errorMessage(error) })), .fetch(localPath)
) .pipe(Effect.mapError((error) => new FetchFailedError({ repository, message: errorMessage(error) })))
if (fetch.exitCode !== 0) { if (fetch.exitCode !== 0) {
return yield* new FetchFailedError({ repository, message: resultMessage(fetch, `Failed to refresh ${repository}`) }) return yield* new FetchFailedError({
repository,
message: resultMessage(fetch, `Failed to refresh ${repository}`),
})
} }
if (input.branch) { if (input.branch) {
const requestedBranch = input.branch const requestedBranch = input.branch
const fetchBranch = yield* git.fetchBranch(localPath, requestedBranch).pipe( const fetchBranch = yield* git
Effect.mapError((error) => new FetchFailedError({ repository, message: errorMessage(error) })), .fetchBranch(localPath, requestedBranch)
) .pipe(
Effect.mapError((error) => new FetchFailedError({ repository, message: errorMessage(error) })),
)
if (fetchBranch.exitCode !== 0) { if (fetchBranch.exitCode !== 0) {
return yield* new FetchFailedError({ return yield* new FetchFailedError({
repository, repository,
@@ -183,11 +197,18 @@ export const layer: Layer.Layer<
}) })
} }
const checkout = yield* git.checkout(localPath, requestedBranch).pipe( const checkout = yield* git
Effect.mapError((error) => .checkout(localPath, requestedBranch)
new CheckoutFailedError({ repository, branch: requestedBranch, message: errorMessage(error) }), .pipe(
), Effect.mapError(
) (error) =>
new CheckoutFailedError({
repository,
branch: requestedBranch,
message: errorMessage(error),
}),
),
)
if (checkout.exitCode !== 0) { if (checkout.exitCode !== 0) {
return yield* new CheckoutFailedError({ return yield* new CheckoutFailedError({
repository, repository,
@@ -197,11 +218,14 @@ export const layer: Layer.Layer<
} }
} }
const reset = yield* git.reset(localPath, yield* resetTarget(git, localPath, input.branch)).pipe( const reset = yield* git
Effect.mapError((error) => new ResetFailedError({ repository, message: errorMessage(error) })), .reset(localPath, yield* resetTarget(git, localPath, input.branch))
) .pipe(Effect.mapError((error) => new ResetFailedError({ repository, message: errorMessage(error) })))
if (reset.exitCode !== 0) { if (reset.exitCode !== 0) {
return yield* new ResetFailedError({ repository, message: resultMessage(reset, `Failed to reset ${repository}`) }) return yield* new ResetFailedError({
repository,
message: resultMessage(reset, `Failed to reset ${repository}`),
})
} }
} }
@@ -245,7 +269,9 @@ function errorMessage(error: unknown) {
} }
function cacheOperation<A, E, R>(effect: Effect.Effect<A, E, R>, operation: string, target: string) { function cacheOperation<A, E, R>(effect: Effect.Effect<A, E, R>, operation: string, target: string) {
return effect.pipe(Effect.mapError((error) => new CacheOperationError({ operation, path: target, message: errorMessage(error) }))) return effect.pipe(
Effect.mapError((error) => new CacheOperationError({ operation, path: target, message: errorMessage(error) })),
)
} }
const resetTarget = Effect.fnUntraced(function* (git: Git.Interface, cwd: string, requestedBranch?: string) { const resetTarget = Effect.fnUntraced(function* (git: Git.Interface, cwd: string, requestedBranch?: string) {
+2 -1
View File
@@ -182,7 +182,8 @@ function buildRemote(input: { host: string; segments: string[]; remote?: string;
segments, segments,
owner: segments.length === 2 ? segments[0] : undefined, owner: segments.length === 2 ? segments[0] : undefined,
repo: segments[segments.length - 1], repo: segments[segments.length - 1],
remote: input.remote ?? (host === "github.com" ? githubRemote(repositoryPath) : `https://${host}/${repositoryPath}.git`), remote:
input.remote ?? (host === "github.com" ? githubRemote(repositoryPath) : `https://${host}/${repositoryPath}.git`),
label: host === "github.com" && segments.length === 2 ? repositoryPath : `${host}/${repositoryPath}`, label: host === "github.com" && segments.length === 2 ? repositoryPath : `${host}/${repositoryPath}`,
protocol: input.protocol, protocol: input.protocol,
} satisfies RemoteReference } satisfies RemoteReference
+6 -1
View File
@@ -116,5 +116,10 @@ function read(file: string) {
} }
function exists(file: string) { function exists(file: string) {
return Effect.promise(() => fs.stat(file).then(() => true, () => false)) return Effect.promise(() =>
fs.stat(file).then(
() => true,
() => false,
),
)
} }
+3 -1
View File
@@ -42,7 +42,9 @@ describe("Repository", () => {
expect(reference).toMatchObject({ host: "file", protocol: "file:", label: localPath }) expect(reference).toMatchObject({ host: "file", protocol: "file:", label: localPath })
expect(reference && Repository.isFile(reference)).toBe(true) expect(reference && Repository.isFile(reference)).toBe(true)
expect(reference && Repository.isRemote(reference)).toBe(false) expect(reference && Repository.isRemote(reference)).toBe(false)
expect(() => Repository.parseRemote(pathToFileURL(localPath).href)).toThrow(Repository.UnsupportedLocalRepositoryError) expect(() => Repository.parseRemote(pathToFileURL(localPath).href)).toThrow(
Repository.UnsupportedLocalRepositoryError,
)
}) })
test("rejects unsafe remote references and branches with typed errors", () => { test("rejects unsafe remote references and branches with typed errors", () => {