@cvr/repo 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/repo +0 -0
- package/package.json +1 -1
- package/src/commands/fetch.ts +32 -50
- package/src/commands/path.ts +12 -0
- package/src/services/git.ts +22 -0
- package/src/test-utils/layers/git.ts +2 -0
package/bin/repo
CHANGED
|
Binary file
|
package/package.json
CHANGED
package/src/commands/fetch.ts
CHANGED
|
@@ -19,12 +19,6 @@ const forceOption = Options.boolean("force").pipe(
|
|
|
19
19
|
Options.withDescription("Force re-clone (removes existing and clones fresh)"),
|
|
20
20
|
);
|
|
21
21
|
|
|
22
|
-
const updateOption = Options.boolean("update").pipe(
|
|
23
|
-
Options.withAlias("u"),
|
|
24
|
-
Options.withDefault(false),
|
|
25
|
-
Options.withDescription("Update existing repo (git pull)"),
|
|
26
|
-
);
|
|
27
|
-
|
|
28
22
|
const fullHistoryOption = Options.boolean("full").pipe(
|
|
29
23
|
Options.withDefault(false),
|
|
30
24
|
Options.withDescription("Clone full git history (default: shallow clone with depth 100)"),
|
|
@@ -32,8 +26,8 @@ const fullHistoryOption = Options.boolean("full").pipe(
|
|
|
32
26
|
|
|
33
27
|
export const fetch = Command.make(
|
|
34
28
|
"fetch",
|
|
35
|
-
{ spec: specArg, force: forceOption,
|
|
36
|
-
({ spec, force,
|
|
29
|
+
{ spec: specArg, force: forceOption, full: fullHistoryOption },
|
|
30
|
+
({ spec, force, full }) =>
|
|
37
31
|
Effect.gen(function* () {
|
|
38
32
|
const registry = yield* RegistryService;
|
|
39
33
|
const cache = yield* CacheService;
|
|
@@ -56,52 +50,40 @@ export const fetch = Command.make(
|
|
|
56
50
|
yield* Console.log(`Force re-fetching ${specStr}...`);
|
|
57
51
|
yield* cache.remove(existing.path);
|
|
58
52
|
yield* metadata.remove(parsedSpec);
|
|
59
|
-
} else if (
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
return;
|
|
89
|
-
} else {
|
|
90
|
-
// Not a git repo, can't update
|
|
91
|
-
yield* metadata.updateAccessTime(parsedSpec);
|
|
92
|
-
yield* Console.log(`Already cached at: ${existing.path}`);
|
|
93
|
-
yield* Console.log(`Size: ${formatBytes(existing.sizeBytes)}`);
|
|
94
|
-
yield* Console.log(`(Not a git repo - use --force to re-fetch)`);
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
53
|
+
} else if (isGit) {
|
|
54
|
+
// Git repo: always pull latest
|
|
55
|
+
yield* Console.log(`Updating ${specStr}...`);
|
|
56
|
+
yield* git
|
|
57
|
+
.update(existing.path)
|
|
58
|
+
.pipe(
|
|
59
|
+
Effect.catchAll((e) =>
|
|
60
|
+
Console.log(`Update failed, repo may be up to date: ${e._tag}`),
|
|
61
|
+
),
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
// Recalculate size after update
|
|
65
|
+
const sizeBytes = yield* cache.getSize(existing.path);
|
|
66
|
+
const currentRef = yield* git
|
|
67
|
+
.getCurrentRef(existing.path)
|
|
68
|
+
.pipe(Effect.orElseSucceed(() => "unknown"));
|
|
69
|
+
|
|
70
|
+
yield* metadata.add({
|
|
71
|
+
spec: parsedSpec,
|
|
72
|
+
fetchedAt: existing.fetchedAt,
|
|
73
|
+
lastAccessedAt: new Date().toISOString(),
|
|
74
|
+
sizeBytes,
|
|
75
|
+
path: existing.path,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
yield* Console.log(`Updated: ${existing.path}`);
|
|
79
|
+
yield* Console.log(`Current ref: ${currentRef}`);
|
|
80
|
+
yield* Console.log(`Size: ${formatBytes(sizeBytes)}`);
|
|
81
|
+
return;
|
|
97
82
|
} else {
|
|
98
|
-
//
|
|
83
|
+
// Not a git repo, can't update
|
|
99
84
|
yield* metadata.updateAccessTime(parsedSpec);
|
|
100
85
|
yield* Console.log(`Already cached at: ${existing.path}`);
|
|
101
86
|
yield* Console.log(`Size: ${formatBytes(existing.sizeBytes)}`);
|
|
102
|
-
if (isGit) {
|
|
103
|
-
yield* Console.log(`Use --update to pull latest changes`);
|
|
104
|
-
}
|
|
105
87
|
yield* Console.log(`Use --force to re-fetch from scratch`);
|
|
106
88
|
return;
|
|
107
89
|
}
|
package/src/commands/path.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { Console, Effect } from "effect";
|
|
|
3
3
|
import { NotFoundError, specToString } from "../types.js";
|
|
4
4
|
import { MetadataService } from "../services/metadata.js";
|
|
5
5
|
import { RegistryService } from "../services/registry.js";
|
|
6
|
+
import { GitService } from "../services/git.js";
|
|
6
7
|
|
|
7
8
|
const specArg = Args.text({ name: "spec" }).pipe(
|
|
8
9
|
Args.withDescription("Package spec to get path for"),
|
|
@@ -18,6 +19,7 @@ export const path = Command.make("path", { spec: specArg, quiet: quietOption },
|
|
|
18
19
|
Effect.gen(function* () {
|
|
19
20
|
const registry = yield* RegistryService;
|
|
20
21
|
const metadata = yield* MetadataService;
|
|
22
|
+
const git = yield* GitService;
|
|
21
23
|
|
|
22
24
|
const parsedSpec = yield* registry.parseSpec(spec);
|
|
23
25
|
const existing = yield* metadata.find(parsedSpec);
|
|
@@ -31,5 +33,15 @@ export const path = Command.make("path", { spec: specArg, quiet: quietOption },
|
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
yield* Console.log(existing.path);
|
|
36
|
+
yield* metadata.updateAccessTime(parsedSpec);
|
|
37
|
+
|
|
38
|
+
// Background refresh for git repos — fire-and-forget so we don't block the caller
|
|
39
|
+
const isGit = yield* git.isGitRepo(existing.path);
|
|
40
|
+
if (isGit) {
|
|
41
|
+
yield* git.fetchRefs(existing.path).pipe(
|
|
42
|
+
Effect.catchAll(() => Effect.void),
|
|
43
|
+
Effect.fork,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
34
46
|
}).pipe(Effect.catchAll(() => Effect.void)),
|
|
35
47
|
);
|
package/src/services/git.ts
CHANGED
|
@@ -11,6 +11,7 @@ export class GitService extends Context.Tag("@cvr/repo/services/git/GitService")
|
|
|
11
11
|
options?: { depth?: number; ref?: string },
|
|
12
12
|
) => Effect.Effect<void, GitError>;
|
|
13
13
|
readonly update: (path: string) => Effect.Effect<void, GitError>;
|
|
14
|
+
readonly fetchRefs: (path: string) => Effect.Effect<void, GitError>;
|
|
14
15
|
readonly isGitRepo: (path: string) => Effect.Effect<boolean>;
|
|
15
16
|
readonly getDefaultBranch: (url: string) => Effect.Effect<string, GitError>;
|
|
16
17
|
readonly getCurrentRef: (path: string) => Effect.Effect<string, GitError>;
|
|
@@ -85,6 +86,27 @@ export class GitService extends Context.Tag("@cvr/repo/services/git/GitService")
|
|
|
85
86
|
}
|
|
86
87
|
}),
|
|
87
88
|
|
|
89
|
+
fetchRefs: (path) =>
|
|
90
|
+
Effect.gen(function* () {
|
|
91
|
+
const fetchProc = Bun.spawn(["git", "-C", path, "fetch", "--all", "--prune"], {
|
|
92
|
+
stdout: "pipe",
|
|
93
|
+
stderr: "pipe",
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const fetchExit = yield* Effect.tryPromise({
|
|
97
|
+
try: () => fetchProc.exited,
|
|
98
|
+
catch: (cause) => new GitError({ operation: "fetch", repo: path, cause }),
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
if (fetchExit !== 0) {
|
|
102
|
+
return yield* new GitError({
|
|
103
|
+
operation: "fetch",
|
|
104
|
+
repo: path,
|
|
105
|
+
cause: new Error(`git fetch failed with exit code ${fetchExit}`),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}),
|
|
109
|
+
|
|
88
110
|
update: (path) =>
|
|
89
111
|
Effect.gen(function* () {
|
|
90
112
|
// Fetch all updates
|