Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
26cfac3
perf(server): answer cheap git metadata from repository files instead…
SkiTee3000 Sep 19, 2026
9b1354a
fix(server): keep file-answered git commands inside the caller's limi…
SkiTee3000 Sep 19, 2026
adbecf8
fix(server): remember a failed global config listing and leave per-wo…
SkiTee3000 Sep 19, 2026
25a26ef
fix(server): cap the git processes the metadata reader starts on its own
SkiTee3000 Sep 19, 2026
35e22bd
fix(server): keep the rev-list memo lookup inside the caller's time b…
SkiTee3000 Oct 1, 2026
0d71f46
fix(server): stop the metadata reader from piling up stuck reads, and…
SkiTee3000 Oct 2, 2026
4b1918c
fix(server): remember git answers after the timeout check, not inside it
SkiTee3000 Oct 2, 2026
911bace
fix(server): leave a file given as the working directory to git
SkiTee3000 Oct 2, 2026
ee570fa
Merge remote-tracking branch 'origin/main' into perf/git-metadata-fro…
SkiTee3000 Oct 7, 2026
780cf61
fix(server): git fast path re-asks git for a replaced repository and …
SkiTee3000 Oct 7, 2026
01f0a84
fix(server): git fast path checks the path for symlinks again on ever…
SkiTee3000 Oct 7, 2026
8c45eef
fix(server): import node:async_hooks as a namespace in the git fast path
SkiTee3000 Oct 7, 2026
24cdbbc
fix(server): git fast path reads only the refs a for-each-ref names
SkiTee3000 Oct 10, 2026
8fce2fc
fix(server): git fast path names the common directory a .git director…
SkiTee3000 Oct 10, 2026
b866579
fix(server): git fast path leaves a HEAD that points at a symbolic re…
SkiTee3000 Oct 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/server/src/observability/Metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export interface WithMetricsOptions {
) => Readonly<Record<string, unknown>>;
}

const recordMetrics = (
export const recordMetrics = (
options: WithMetricsOptions,
startedAt: bigint,
exit: Exit.Exit<unknown, unknown>,
Expand Down
36 changes: 19 additions & 17 deletions apps/server/src/project/RepositoryIdentityResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import * as Duration from "effect/Duration";
import * as Effect from "effect/Effect";
import * as Exit from "effect/Exit";
import * as Layer from "effect/Layer";
import * as Option from "effect/Option";

import * as ProcessRunner from "../processRunner.ts";
import * as GitMetadataFastPath from "../vcs/GitMetadataFastPath.ts";

const DEFAULT_REPOSITORY_IDENTITY_CACHE_CAPACITY = 512;
// Background sweeps resolve every project each minute. A long TTL keeps them
Expand Down Expand Up @@ -115,19 +117,26 @@ function buildRepositoryIdentity(input: {
};
}

const resolveRepositoryIdentityCacheKey = Effect.fn("RepositoryIdentityResolver.resolveCacheKey")(
function* (cwd: string) {
const processRunner = yield* ProcessRunner.ProcessRunner;
/** Reads the answer from the repository files when possible, otherwise spawns git. */
const runGitMetadataCommand = Effect.fn("RepositoryIdentityResolver.runGitMetadataCommand")(
function* (cwd: string, args: ReadonlyArray<string>) {
const answer = yield* Effect.promise(() =>
GitMetadataFastPath.tryAnswerGitCommand({ cwd, args }),
);
if (answer !== null) return Option.some({ code: answer.exitCode, stdout: answer.stdout });

const processRunner = yield* ProcessRunner.ProcessRunner;
// git is a real executable on every platform — no cmd.exe shell mode, which
// would split paths containing spaces during cmd's re-tokenization.
const topLevelResult = yield* processRunner
.run({
command: "git",
args: ["-C", cwd, "rev-parse", "--show-toplevel"],
timeoutBehavior: "timedOutResult",
})
return yield* processRunner
.run({ command: "git", args: ["-C", cwd, ...args], timeoutBehavior: "timedOutResult" })
.pipe(Effect.option);
},
);

const resolveRepositoryIdentityCacheKey = Effect.fn("RepositoryIdentityResolver.resolveCacheKey")(
function* (cwd: string) {
const topLevelResult = yield* runGitMetadataCommand(cwd, ["rev-parse", "--show-toplevel"]);
if (topLevelResult._tag === "None" || topLevelResult.value.code !== 0) {
return null;
}
Expand All @@ -142,14 +151,7 @@ const resolveRepositoryIdentityFromCacheKey = Effect.fn(
)(function* (
cacheKey: string,
): Effect.fn.Return<RepositoryIdentity | null, never, ProcessRunner.ProcessRunner> {
const processRunner = yield* ProcessRunner.ProcessRunner;
const remoteResult = yield* processRunner
.run({
command: "git",
args: ["-C", cacheKey, "remote", "-v"],
timeoutBehavior: "timedOutResult",
})
.pipe(Effect.option);
const remoteResult = yield* runGitMetadataCommand(cacheKey, ["remote", "-v"]);
if (remoteResult._tag === "None" || remoteResult.value.code !== 0) {
return null;
}
Expand Down
Loading
Loading