@h-rig/repos-plugin 0.0.6-alpha.156
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/README.md +1 -0
- package/dist/src/index.d.ts +7 -0
- package/dist/src/index.js +491 -0
- package/dist/src/layout.d.ts +10 -0
- package/dist/src/layout.js +83 -0
- package/dist/src/mirror/bootstrap.d.ts +2 -0
- package/dist/src/mirror/bootstrap.js +192 -0
- package/dist/src/mirror/refresh.d.ts +3 -0
- package/dist/src/mirror/refresh.js +322 -0
- package/dist/src/mirror/state.d.ts +3 -0
- package/dist/src/mirror/state.js +93 -0
- package/dist/src/plugin.d.ts +4 -0
- package/dist/src/plugin.js +457 -0
- package/dist/src/registry.d.ts +21 -0
- package/dist/src/registry.js +77 -0
- package/dist/src/service.d.ts +13 -0
- package/dist/src/service.js +391 -0
- package/package.json +36 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/repos-plugin/src/mirror/bootstrap.ts
|
|
3
|
+
import { existsSync as existsSync2, mkdirSync, realpathSync } from "fs";
|
|
4
|
+
import { resolve as resolve2 } from "path";
|
|
5
|
+
|
|
6
|
+
// packages/repos-plugin/src/registry.ts
|
|
7
|
+
var MANAGED_REPOS = new Map;
|
|
8
|
+
function getManagedRepoEntry(repoId) {
|
|
9
|
+
const entry = MANAGED_REPOS.get(repoId);
|
|
10
|
+
if (!entry) {
|
|
11
|
+
throw new Error(`managed repo not registered: ${repoId}. Plugins contribute repos via RigPlugin.contributes.repoSources; ` + `make sure a plugin declares this id and the plugin host has been initialized.`);
|
|
12
|
+
}
|
|
13
|
+
return entry;
|
|
14
|
+
}
|
|
15
|
+
function listManagedRepoEntries() {
|
|
16
|
+
return Array.from(MANAGED_REPOS.values());
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// packages/repos-plugin/src/layout.ts
|
|
20
|
+
import { existsSync } from "fs";
|
|
21
|
+
import { basename, dirname, join, resolve } from "path";
|
|
22
|
+
import { resolveMonorepoRoot } from "@rig/runtime/layout";
|
|
23
|
+
function resolveRepoStateDir(projectRoot) {
|
|
24
|
+
const normalizedProjectRoot = resolve(projectRoot);
|
|
25
|
+
const projectParent = dirname(normalizedProjectRoot);
|
|
26
|
+
if (basename(projectParent) === ".worktrees") {
|
|
27
|
+
const ownerRoot = dirname(projectParent);
|
|
28
|
+
const ownerHasRepoMarkers = existsSync(resolve(ownerRoot, ".git")) || existsSync(resolve(ownerRoot, ".rig", "state"));
|
|
29
|
+
if (ownerHasRepoMarkers) {
|
|
30
|
+
return resolve(ownerRoot, ".rig", "state");
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return resolve(projectRoot, ".rig", "state");
|
|
34
|
+
}
|
|
35
|
+
function resolveManagedRepoLayout(projectRoot, repoId) {
|
|
36
|
+
const normalizedProjectRoot = resolve(projectRoot);
|
|
37
|
+
const entry = getManagedRepoEntry(repoId);
|
|
38
|
+
const stateDir = resolveRepoStateDir(normalizedProjectRoot);
|
|
39
|
+
const metadataRelativePath = join("repos", entry.id);
|
|
40
|
+
const metadataRoot = resolve(stateDir, metadataRelativePath);
|
|
41
|
+
const runtimeWorkspace = process.env.RIG_TASK_WORKSPACE?.trim();
|
|
42
|
+
const runsInsideTaskWorktree = runtimeWorkspace && resolve(runtimeWorkspace) === normalizedProjectRoot || basename(dirname(normalizedProjectRoot)) === ".worktrees";
|
|
43
|
+
const isPrimaryManagedRepo = listManagedRepoEntries()[0]?.id === repoId;
|
|
44
|
+
const checkoutRoot = isPrimaryManagedRepo && runsInsideTaskWorktree ? resolveMonorepoRoot(normalizedProjectRoot) : entry.checkoutEnvVar && process.env[entry.checkoutEnvVar]?.trim() ? resolve(process.env[entry.checkoutEnvVar].trim()) : resolve(normalizedProjectRoot, entry.alias);
|
|
45
|
+
return {
|
|
46
|
+
projectRoot: normalizedProjectRoot,
|
|
47
|
+
repoId: entry.id,
|
|
48
|
+
alias: entry.alias,
|
|
49
|
+
defaultBranch: entry.defaultBranch,
|
|
50
|
+
remoteUrl: entry.remoteEnvVar && process.env[entry.remoteEnvVar]?.trim() ? process.env[entry.remoteEnvVar].trim() : entry.defaultRemoteUrl,
|
|
51
|
+
checkoutRoot,
|
|
52
|
+
worktreesRoot: resolve(checkoutRoot, ".worktrees"),
|
|
53
|
+
stateDir,
|
|
54
|
+
metadataRoot,
|
|
55
|
+
metadataRelativePath,
|
|
56
|
+
mirrorRoot: resolve(metadataRoot, "mirror.git"),
|
|
57
|
+
mirrorStatePath: resolve(metadataRoot, "mirror-state.json"),
|
|
58
|
+
mirrorStateRelativePath: join(metadataRelativePath, "mirror-state.json")
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// packages/repos-plugin/src/mirror/state.ts
|
|
63
|
+
import { readAuthorityProjectStateJson, writeAuthorityProjectStateJson } from "@rig/runtime/control-plane/json-files";
|
|
64
|
+
var STATE_VERSION = 1;
|
|
65
|
+
function defaultMirrorState(projectRoot, repoId) {
|
|
66
|
+
const layout = resolveManagedRepoLayout(projectRoot, repoId);
|
|
67
|
+
return {
|
|
68
|
+
version: STATE_VERSION,
|
|
69
|
+
repoId,
|
|
70
|
+
remoteUrl: layout.remoteUrl,
|
|
71
|
+
defaultBranch: layout.defaultBranch
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function readManagedRepoMirrorState(projectRoot, repoId) {
|
|
75
|
+
const layout = resolveManagedRepoLayout(projectRoot, repoId);
|
|
76
|
+
return readAuthorityProjectStateJson(projectRoot, layout.mirrorStateRelativePath, null);
|
|
77
|
+
}
|
|
78
|
+
function writeManagedRepoMirrorState(projectRoot, repoId, patch) {
|
|
79
|
+
const current = readManagedRepoMirrorState(projectRoot, repoId) || defaultMirrorState(projectRoot, repoId);
|
|
80
|
+
const next = {
|
|
81
|
+
...current,
|
|
82
|
+
...patch,
|
|
83
|
+
version: STATE_VERSION,
|
|
84
|
+
repoId
|
|
85
|
+
};
|
|
86
|
+
const layout = resolveManagedRepoLayout(projectRoot, repoId);
|
|
87
|
+
writeAuthorityProjectStateJson(projectRoot, layout.mirrorStateRelativePath, next);
|
|
88
|
+
return next;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// packages/repos-plugin/src/mirror/bootstrap.ts
|
|
92
|
+
function nowIso() {
|
|
93
|
+
return new Date().toISOString();
|
|
94
|
+
}
|
|
95
|
+
function runGit(command, cwd) {
|
|
96
|
+
const result = Bun.spawnSync(command, {
|
|
97
|
+
cwd,
|
|
98
|
+
stdout: "pipe",
|
|
99
|
+
stderr: "pipe",
|
|
100
|
+
env: process.env
|
|
101
|
+
});
|
|
102
|
+
return {
|
|
103
|
+
exitCode: result.exitCode,
|
|
104
|
+
stdout: result.stdout.toString(),
|
|
105
|
+
stderr: result.stderr.toString()
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function ensureGitSuccess(result, command) {
|
|
109
|
+
if (result.exitCode !== 0) {
|
|
110
|
+
throw new Error(result.stderr || result.stdout || `git command failed: ${command.join(" ")}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function isUsableRemoteUrl(candidate, layout) {
|
|
114
|
+
return candidate.length > 0 && candidate !== layout.mirrorRoot && candidate !== layout.checkoutRoot;
|
|
115
|
+
}
|
|
116
|
+
function sameExistingPath(left, right) {
|
|
117
|
+
try {
|
|
118
|
+
return realpathSync(left) === realpathSync(right);
|
|
119
|
+
} catch {
|
|
120
|
+
return resolve2(left) === resolve2(right);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function repoLooksUsable(repoRoot, projectRoot) {
|
|
124
|
+
const probe = runGit(["git", "-C", repoRoot, "rev-parse", "--show-toplevel"], projectRoot);
|
|
125
|
+
return probe.exitCode === 0 && sameExistingPath(probe.stdout.trim(), repoRoot);
|
|
126
|
+
}
|
|
127
|
+
function checkoutLooksUsable(layout) {
|
|
128
|
+
return repoLooksUsable(layout.checkoutRoot, layout.projectRoot);
|
|
129
|
+
}
|
|
130
|
+
function resolveMirrorRemoteUrl(layout) {
|
|
131
|
+
const entry = getManagedRepoEntry(layout.repoId);
|
|
132
|
+
const explicit = entry.remoteEnvVar ? process.env[entry.remoteEnvVar]?.trim() : "";
|
|
133
|
+
if (explicit) {
|
|
134
|
+
return explicit;
|
|
135
|
+
}
|
|
136
|
+
const persisted = readManagedRepoMirrorState(layout.projectRoot, layout.repoId)?.remoteUrl?.trim();
|
|
137
|
+
if (persisted && isUsableRemoteUrl(persisted, layout)) {
|
|
138
|
+
return persisted;
|
|
139
|
+
}
|
|
140
|
+
const mirrorOrigin = runGit(["git", "--git-dir", layout.mirrorRoot, "remote", "get-url", "origin"], layout.projectRoot);
|
|
141
|
+
if (mirrorOrigin.exitCode === 0) {
|
|
142
|
+
const currentOrigin = mirrorOrigin.stdout.trim();
|
|
143
|
+
if (isUsableRemoteUrl(currentOrigin, layout)) {
|
|
144
|
+
return currentOrigin;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (repoLooksUsable(layout.projectRoot, layout.projectRoot)) {
|
|
148
|
+
const projectOrigin = runGit(["git", "-C", layout.projectRoot, "remote", "get-url", "origin"], layout.projectRoot);
|
|
149
|
+
if (projectOrigin.exitCode === 0) {
|
|
150
|
+
const currentOrigin = projectOrigin.stdout.trim();
|
|
151
|
+
if (isUsableRemoteUrl(currentOrigin, layout)) {
|
|
152
|
+
return currentOrigin;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (existsSync2(resolve2(layout.checkoutRoot, ".git")) && checkoutLooksUsable(layout)) {
|
|
157
|
+
const checkoutOrigin = runGit(["git", "-C", layout.checkoutRoot, "remote", "get-url", "origin"], layout.projectRoot);
|
|
158
|
+
if (checkoutOrigin.exitCode === 0) {
|
|
159
|
+
const currentOrigin = checkoutOrigin.stdout.trim();
|
|
160
|
+
if (isUsableRemoteUrl(currentOrigin, layout)) {
|
|
161
|
+
return currentOrigin;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return layout.remoteUrl;
|
|
166
|
+
}
|
|
167
|
+
function ensureManagedRepoMirror(projectRoot, repoId) {
|
|
168
|
+
const layout = resolveManagedRepoLayout(projectRoot, repoId);
|
|
169
|
+
mkdirSync(layout.metadataRoot, { recursive: true });
|
|
170
|
+
const remoteUrl = resolveMirrorRemoteUrl(layout);
|
|
171
|
+
if (!existsSync2(resolve2(layout.mirrorRoot, "HEAD"))) {
|
|
172
|
+
ensureGitSuccess(runGit(["git", "init", "--bare", layout.mirrorRoot], layout.projectRoot), ["git", "init", "--bare", layout.mirrorRoot]);
|
|
173
|
+
}
|
|
174
|
+
const getOrigin = runGit(["git", "--git-dir", layout.mirrorRoot, "remote", "get-url", "origin"], layout.projectRoot);
|
|
175
|
+
if (getOrigin.exitCode === 0) {
|
|
176
|
+
const currentOrigin = getOrigin.stdout.trim();
|
|
177
|
+
if (currentOrigin !== remoteUrl) {
|
|
178
|
+
ensureGitSuccess(runGit(["git", "--git-dir", layout.mirrorRoot, "remote", "set-url", "origin", remoteUrl], layout.projectRoot), ["git", "--git-dir", layout.mirrorRoot, "remote", "set-url", "origin", remoteUrl]);
|
|
179
|
+
}
|
|
180
|
+
} else {
|
|
181
|
+
ensureGitSuccess(runGit(["git", "--git-dir", layout.mirrorRoot, "remote", "add", "origin", remoteUrl], layout.projectRoot), ["git", "--git-dir", layout.mirrorRoot, "remote", "add", "origin", remoteUrl]);
|
|
182
|
+
}
|
|
183
|
+
writeManagedRepoMirrorState(projectRoot, repoId, {
|
|
184
|
+
remoteUrl,
|
|
185
|
+
defaultBranch: layout.defaultBranch,
|
|
186
|
+
initializedAt: nowIso()
|
|
187
|
+
});
|
|
188
|
+
return layout;
|
|
189
|
+
}
|
|
190
|
+
export {
|
|
191
|
+
ensureManagedRepoMirror
|
|
192
|
+
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ManagedRepoId, ManagedRepoSyncResult } from "@rig/contracts";
|
|
2
|
+
export declare function refreshManagedRepoMirror(projectRoot: string, repoId: ManagedRepoId): ManagedRepoSyncResult;
|
|
3
|
+
export declare function syncManagedRepo(projectRoot: string, repoId: ManagedRepoId): ManagedRepoSyncResult;
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/repos-plugin/src/mirror/refresh.ts
|
|
3
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, realpathSync as realpathSync2, rmSync } from "fs";
|
|
4
|
+
import { resolve as resolve3 } from "path";
|
|
5
|
+
|
|
6
|
+
// packages/repos-plugin/src/mirror/bootstrap.ts
|
|
7
|
+
import { existsSync as existsSync2, mkdirSync, realpathSync } from "fs";
|
|
8
|
+
import { resolve as resolve2 } from "path";
|
|
9
|
+
|
|
10
|
+
// packages/repos-plugin/src/registry.ts
|
|
11
|
+
var MANAGED_REPOS = new Map;
|
|
12
|
+
function getManagedRepoEntry(repoId) {
|
|
13
|
+
const entry = MANAGED_REPOS.get(repoId);
|
|
14
|
+
if (!entry) {
|
|
15
|
+
throw new Error(`managed repo not registered: ${repoId}. Plugins contribute repos via RigPlugin.contributes.repoSources; ` + `make sure a plugin declares this id and the plugin host has been initialized.`);
|
|
16
|
+
}
|
|
17
|
+
return entry;
|
|
18
|
+
}
|
|
19
|
+
function listManagedRepoEntries() {
|
|
20
|
+
return Array.from(MANAGED_REPOS.values());
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// packages/repos-plugin/src/layout.ts
|
|
24
|
+
import { existsSync } from "fs";
|
|
25
|
+
import { basename, dirname, join, resolve } from "path";
|
|
26
|
+
import { resolveMonorepoRoot } from "@rig/runtime/layout";
|
|
27
|
+
function resolveRepoStateDir(projectRoot) {
|
|
28
|
+
const normalizedProjectRoot = resolve(projectRoot);
|
|
29
|
+
const projectParent = dirname(normalizedProjectRoot);
|
|
30
|
+
if (basename(projectParent) === ".worktrees") {
|
|
31
|
+
const ownerRoot = dirname(projectParent);
|
|
32
|
+
const ownerHasRepoMarkers = existsSync(resolve(ownerRoot, ".git")) || existsSync(resolve(ownerRoot, ".rig", "state"));
|
|
33
|
+
if (ownerHasRepoMarkers) {
|
|
34
|
+
return resolve(ownerRoot, ".rig", "state");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return resolve(projectRoot, ".rig", "state");
|
|
38
|
+
}
|
|
39
|
+
function resolveManagedRepoLayout(projectRoot, repoId) {
|
|
40
|
+
const normalizedProjectRoot = resolve(projectRoot);
|
|
41
|
+
const entry = getManagedRepoEntry(repoId);
|
|
42
|
+
const stateDir = resolveRepoStateDir(normalizedProjectRoot);
|
|
43
|
+
const metadataRelativePath = join("repos", entry.id);
|
|
44
|
+
const metadataRoot = resolve(stateDir, metadataRelativePath);
|
|
45
|
+
const runtimeWorkspace = process.env.RIG_TASK_WORKSPACE?.trim();
|
|
46
|
+
const runsInsideTaskWorktree = runtimeWorkspace && resolve(runtimeWorkspace) === normalizedProjectRoot || basename(dirname(normalizedProjectRoot)) === ".worktrees";
|
|
47
|
+
const isPrimaryManagedRepo = listManagedRepoEntries()[0]?.id === repoId;
|
|
48
|
+
const checkoutRoot = isPrimaryManagedRepo && runsInsideTaskWorktree ? resolveMonorepoRoot(normalizedProjectRoot) : entry.checkoutEnvVar && process.env[entry.checkoutEnvVar]?.trim() ? resolve(process.env[entry.checkoutEnvVar].trim()) : resolve(normalizedProjectRoot, entry.alias);
|
|
49
|
+
return {
|
|
50
|
+
projectRoot: normalizedProjectRoot,
|
|
51
|
+
repoId: entry.id,
|
|
52
|
+
alias: entry.alias,
|
|
53
|
+
defaultBranch: entry.defaultBranch,
|
|
54
|
+
remoteUrl: entry.remoteEnvVar && process.env[entry.remoteEnvVar]?.trim() ? process.env[entry.remoteEnvVar].trim() : entry.defaultRemoteUrl,
|
|
55
|
+
checkoutRoot,
|
|
56
|
+
worktreesRoot: resolve(checkoutRoot, ".worktrees"),
|
|
57
|
+
stateDir,
|
|
58
|
+
metadataRoot,
|
|
59
|
+
metadataRelativePath,
|
|
60
|
+
mirrorRoot: resolve(metadataRoot, "mirror.git"),
|
|
61
|
+
mirrorStatePath: resolve(metadataRoot, "mirror-state.json"),
|
|
62
|
+
mirrorStateRelativePath: join(metadataRelativePath, "mirror-state.json")
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// packages/repos-plugin/src/mirror/state.ts
|
|
67
|
+
import { readAuthorityProjectStateJson, writeAuthorityProjectStateJson } from "@rig/runtime/control-plane/json-files";
|
|
68
|
+
var STATE_VERSION = 1;
|
|
69
|
+
function defaultMirrorState(projectRoot, repoId) {
|
|
70
|
+
const layout = resolveManagedRepoLayout(projectRoot, repoId);
|
|
71
|
+
return {
|
|
72
|
+
version: STATE_VERSION,
|
|
73
|
+
repoId,
|
|
74
|
+
remoteUrl: layout.remoteUrl,
|
|
75
|
+
defaultBranch: layout.defaultBranch
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function readManagedRepoMirrorState(projectRoot, repoId) {
|
|
79
|
+
const layout = resolveManagedRepoLayout(projectRoot, repoId);
|
|
80
|
+
return readAuthorityProjectStateJson(projectRoot, layout.mirrorStateRelativePath, null);
|
|
81
|
+
}
|
|
82
|
+
function writeManagedRepoMirrorState(projectRoot, repoId, patch) {
|
|
83
|
+
const current = readManagedRepoMirrorState(projectRoot, repoId) || defaultMirrorState(projectRoot, repoId);
|
|
84
|
+
const next = {
|
|
85
|
+
...current,
|
|
86
|
+
...patch,
|
|
87
|
+
version: STATE_VERSION,
|
|
88
|
+
repoId
|
|
89
|
+
};
|
|
90
|
+
const layout = resolveManagedRepoLayout(projectRoot, repoId);
|
|
91
|
+
writeAuthorityProjectStateJson(projectRoot, layout.mirrorStateRelativePath, next);
|
|
92
|
+
return next;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// packages/repos-plugin/src/mirror/bootstrap.ts
|
|
96
|
+
function nowIso() {
|
|
97
|
+
return new Date().toISOString();
|
|
98
|
+
}
|
|
99
|
+
function runGit(command, cwd) {
|
|
100
|
+
const result = Bun.spawnSync(command, {
|
|
101
|
+
cwd,
|
|
102
|
+
stdout: "pipe",
|
|
103
|
+
stderr: "pipe",
|
|
104
|
+
env: process.env
|
|
105
|
+
});
|
|
106
|
+
return {
|
|
107
|
+
exitCode: result.exitCode,
|
|
108
|
+
stdout: result.stdout.toString(),
|
|
109
|
+
stderr: result.stderr.toString()
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function ensureGitSuccess(result, command) {
|
|
113
|
+
if (result.exitCode !== 0) {
|
|
114
|
+
throw new Error(result.stderr || result.stdout || `git command failed: ${command.join(" ")}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function isUsableRemoteUrl(candidate, layout) {
|
|
118
|
+
return candidate.length > 0 && candidate !== layout.mirrorRoot && candidate !== layout.checkoutRoot;
|
|
119
|
+
}
|
|
120
|
+
function sameExistingPath(left, right) {
|
|
121
|
+
try {
|
|
122
|
+
return realpathSync(left) === realpathSync(right);
|
|
123
|
+
} catch {
|
|
124
|
+
return resolve2(left) === resolve2(right);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function repoLooksUsable(repoRoot, projectRoot) {
|
|
128
|
+
const probe = runGit(["git", "-C", repoRoot, "rev-parse", "--show-toplevel"], projectRoot);
|
|
129
|
+
return probe.exitCode === 0 && sameExistingPath(probe.stdout.trim(), repoRoot);
|
|
130
|
+
}
|
|
131
|
+
function checkoutLooksUsable(layout) {
|
|
132
|
+
return repoLooksUsable(layout.checkoutRoot, layout.projectRoot);
|
|
133
|
+
}
|
|
134
|
+
function resolveMirrorRemoteUrl(layout) {
|
|
135
|
+
const entry = getManagedRepoEntry(layout.repoId);
|
|
136
|
+
const explicit = entry.remoteEnvVar ? process.env[entry.remoteEnvVar]?.trim() : "";
|
|
137
|
+
if (explicit) {
|
|
138
|
+
return explicit;
|
|
139
|
+
}
|
|
140
|
+
const persisted = readManagedRepoMirrorState(layout.projectRoot, layout.repoId)?.remoteUrl?.trim();
|
|
141
|
+
if (persisted && isUsableRemoteUrl(persisted, layout)) {
|
|
142
|
+
return persisted;
|
|
143
|
+
}
|
|
144
|
+
const mirrorOrigin = runGit(["git", "--git-dir", layout.mirrorRoot, "remote", "get-url", "origin"], layout.projectRoot);
|
|
145
|
+
if (mirrorOrigin.exitCode === 0) {
|
|
146
|
+
const currentOrigin = mirrorOrigin.stdout.trim();
|
|
147
|
+
if (isUsableRemoteUrl(currentOrigin, layout)) {
|
|
148
|
+
return currentOrigin;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (repoLooksUsable(layout.projectRoot, layout.projectRoot)) {
|
|
152
|
+
const projectOrigin = runGit(["git", "-C", layout.projectRoot, "remote", "get-url", "origin"], layout.projectRoot);
|
|
153
|
+
if (projectOrigin.exitCode === 0) {
|
|
154
|
+
const currentOrigin = projectOrigin.stdout.trim();
|
|
155
|
+
if (isUsableRemoteUrl(currentOrigin, layout)) {
|
|
156
|
+
return currentOrigin;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (existsSync2(resolve2(layout.checkoutRoot, ".git")) && checkoutLooksUsable(layout)) {
|
|
161
|
+
const checkoutOrigin = runGit(["git", "-C", layout.checkoutRoot, "remote", "get-url", "origin"], layout.projectRoot);
|
|
162
|
+
if (checkoutOrigin.exitCode === 0) {
|
|
163
|
+
const currentOrigin = checkoutOrigin.stdout.trim();
|
|
164
|
+
if (isUsableRemoteUrl(currentOrigin, layout)) {
|
|
165
|
+
return currentOrigin;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return layout.remoteUrl;
|
|
170
|
+
}
|
|
171
|
+
function ensureManagedRepoMirror(projectRoot, repoId) {
|
|
172
|
+
const layout = resolveManagedRepoLayout(projectRoot, repoId);
|
|
173
|
+
mkdirSync(layout.metadataRoot, { recursive: true });
|
|
174
|
+
const remoteUrl = resolveMirrorRemoteUrl(layout);
|
|
175
|
+
if (!existsSync2(resolve2(layout.mirrorRoot, "HEAD"))) {
|
|
176
|
+
ensureGitSuccess(runGit(["git", "init", "--bare", layout.mirrorRoot], layout.projectRoot), ["git", "init", "--bare", layout.mirrorRoot]);
|
|
177
|
+
}
|
|
178
|
+
const getOrigin = runGit(["git", "--git-dir", layout.mirrorRoot, "remote", "get-url", "origin"], layout.projectRoot);
|
|
179
|
+
if (getOrigin.exitCode === 0) {
|
|
180
|
+
const currentOrigin = getOrigin.stdout.trim();
|
|
181
|
+
if (currentOrigin !== remoteUrl) {
|
|
182
|
+
ensureGitSuccess(runGit(["git", "--git-dir", layout.mirrorRoot, "remote", "set-url", "origin", remoteUrl], layout.projectRoot), ["git", "--git-dir", layout.mirrorRoot, "remote", "set-url", "origin", remoteUrl]);
|
|
183
|
+
}
|
|
184
|
+
} else {
|
|
185
|
+
ensureGitSuccess(runGit(["git", "--git-dir", layout.mirrorRoot, "remote", "add", "origin", remoteUrl], layout.projectRoot), ["git", "--git-dir", layout.mirrorRoot, "remote", "add", "origin", remoteUrl]);
|
|
186
|
+
}
|
|
187
|
+
writeManagedRepoMirrorState(projectRoot, repoId, {
|
|
188
|
+
remoteUrl,
|
|
189
|
+
defaultBranch: layout.defaultBranch,
|
|
190
|
+
initializedAt: nowIso()
|
|
191
|
+
});
|
|
192
|
+
return layout;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// packages/repos-plugin/src/mirror/refresh.ts
|
|
196
|
+
function nowIso2() {
|
|
197
|
+
return new Date().toISOString();
|
|
198
|
+
}
|
|
199
|
+
function runGit2(command, cwd) {
|
|
200
|
+
const result = Bun.spawnSync(command, {
|
|
201
|
+
cwd,
|
|
202
|
+
stdout: "pipe",
|
|
203
|
+
stderr: "pipe",
|
|
204
|
+
env: process.env
|
|
205
|
+
});
|
|
206
|
+
return {
|
|
207
|
+
exitCode: result.exitCode,
|
|
208
|
+
stdout: result.stdout.toString(),
|
|
209
|
+
stderr: result.stderr.toString()
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function ensureGitSuccess2(result, command) {
|
|
213
|
+
if (result.exitCode !== 0) {
|
|
214
|
+
throw new Error(result.stderr || result.stdout || `git command failed: ${command.join(" ")}`);
|
|
215
|
+
}
|
|
216
|
+
return result.stdout.trim();
|
|
217
|
+
}
|
|
218
|
+
function sameExistingPath2(left, right) {
|
|
219
|
+
try {
|
|
220
|
+
return realpathSync2(left) === realpathSync2(right);
|
|
221
|
+
} catch {
|
|
222
|
+
return resolve3(left) === resolve3(right);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function ensureMirrorHead(layout) {
|
|
226
|
+
ensureGitSuccess2(runGit2([
|
|
227
|
+
"git",
|
|
228
|
+
"--git-dir",
|
|
229
|
+
layout.mirrorRoot,
|
|
230
|
+
"fetch",
|
|
231
|
+
"--prune",
|
|
232
|
+
"origin",
|
|
233
|
+
"+refs/heads/*:refs/heads/*",
|
|
234
|
+
"+refs/tags/*:refs/tags/*"
|
|
235
|
+
], layout.projectRoot), [
|
|
236
|
+
"git",
|
|
237
|
+
"--git-dir",
|
|
238
|
+
layout.mirrorRoot,
|
|
239
|
+
"fetch",
|
|
240
|
+
"--prune",
|
|
241
|
+
"origin",
|
|
242
|
+
"+refs/heads/*:refs/heads/*",
|
|
243
|
+
"+refs/tags/*:refs/tags/*"
|
|
244
|
+
]);
|
|
245
|
+
const headRef = `refs/heads/${layout.defaultBranch}`;
|
|
246
|
+
const headCommit = ensureGitSuccess2(runGit2(["git", "--git-dir", layout.mirrorRoot, "rev-parse", headRef], layout.projectRoot), ["git", "--git-dir", layout.mirrorRoot, "rev-parse", headRef]);
|
|
247
|
+
const remoteUrl = ensureGitSuccess2(runGit2(["git", "--git-dir", layout.mirrorRoot, "remote", "get-url", "origin"], layout.projectRoot), ["git", "--git-dir", layout.mirrorRoot, "remote", "get-url", "origin"]);
|
|
248
|
+
ensureGitSuccess2(runGit2(["git", "--git-dir", layout.mirrorRoot, "symbolic-ref", "HEAD", headRef], layout.projectRoot), ["git", "--git-dir", layout.mirrorRoot, "symbolic-ref", "HEAD", headRef]);
|
|
249
|
+
writeManagedRepoMirrorState(layout.projectRoot, layout.repoId, {
|
|
250
|
+
remoteUrl,
|
|
251
|
+
defaultBranch: layout.defaultBranch,
|
|
252
|
+
lastSyncedAt: nowIso2(),
|
|
253
|
+
headRef,
|
|
254
|
+
headCommit
|
|
255
|
+
});
|
|
256
|
+
return headCommit;
|
|
257
|
+
}
|
|
258
|
+
function refreshManagedRepoMirror(projectRoot, repoId) {
|
|
259
|
+
const layout = ensureManagedRepoMirror(projectRoot, repoId);
|
|
260
|
+
const headCommit = ensureMirrorHead(layout);
|
|
261
|
+
return { layout, headCommit };
|
|
262
|
+
}
|
|
263
|
+
function checkoutLooksUsable2(layout) {
|
|
264
|
+
const probe = runGit2(["git", "-C", layout.checkoutRoot, "rev-parse", "--show-toplevel"], layout.projectRoot);
|
|
265
|
+
return probe.exitCode === 0 && sameExistingPath2(probe.stdout.trim(), layout.checkoutRoot);
|
|
266
|
+
}
|
|
267
|
+
function ensureCheckoutFromMirror(layout) {
|
|
268
|
+
mkdirSync2(resolve3(layout.checkoutRoot, ".."), { recursive: true });
|
|
269
|
+
const gitPath = resolve3(layout.checkoutRoot, ".git");
|
|
270
|
+
if (existsSync3(layout.checkoutRoot) && (!existsSync3(gitPath) || !checkoutLooksUsable2(layout))) {
|
|
271
|
+
rmSync(layout.checkoutRoot, { recursive: true, force: true });
|
|
272
|
+
}
|
|
273
|
+
if (!existsSync3(gitPath)) {
|
|
274
|
+
ensureGitSuccess2(runGit2(["git", "clone", layout.mirrorRoot, layout.checkoutRoot], layout.projectRoot), ["git", "clone", layout.mirrorRoot, layout.checkoutRoot]);
|
|
275
|
+
}
|
|
276
|
+
const getOrigin = runGit2(["git", "-C", layout.checkoutRoot, "remote", "get-url", "origin"], layout.projectRoot);
|
|
277
|
+
if (getOrigin.exitCode === 0) {
|
|
278
|
+
const currentOrigin = getOrigin.stdout.trim();
|
|
279
|
+
if (currentOrigin !== layout.mirrorRoot) {
|
|
280
|
+
ensureGitSuccess2(runGit2(["git", "-C", layout.checkoutRoot, "remote", "set-url", "origin", layout.mirrorRoot], layout.projectRoot), ["git", "-C", layout.checkoutRoot, "remote", "set-url", "origin", layout.mirrorRoot]);
|
|
281
|
+
}
|
|
282
|
+
} else {
|
|
283
|
+
ensureGitSuccess2(runGit2(["git", "-C", layout.checkoutRoot, "remote", "add", "origin", layout.mirrorRoot], layout.projectRoot), ["git", "-C", layout.checkoutRoot, "remote", "add", "origin", layout.mirrorRoot]);
|
|
284
|
+
}
|
|
285
|
+
ensureGitSuccess2(runGit2(["git", "-C", layout.checkoutRoot, "fetch", "origin", layout.defaultBranch], layout.projectRoot), ["git", "-C", layout.checkoutRoot, "fetch", "origin", layout.defaultBranch]);
|
|
286
|
+
ensureGitSuccess2(runGit2(["git", "-C", layout.checkoutRoot, "reset", "--hard"], layout.projectRoot), ["git", "-C", layout.checkoutRoot, "reset", "--hard"]);
|
|
287
|
+
ensureGitSuccess2(runGit2(["git", "-C", layout.checkoutRoot, "clean", "-fd"], layout.projectRoot), ["git", "-C", layout.checkoutRoot, "clean", "-fd"]);
|
|
288
|
+
ensureGitSuccess2(runGit2([
|
|
289
|
+
"git",
|
|
290
|
+
"-C",
|
|
291
|
+
layout.checkoutRoot,
|
|
292
|
+
"checkout",
|
|
293
|
+
"--force",
|
|
294
|
+
"-B",
|
|
295
|
+
layout.defaultBranch,
|
|
296
|
+
`origin/${layout.defaultBranch}`
|
|
297
|
+
], layout.projectRoot), [
|
|
298
|
+
"git",
|
|
299
|
+
"-C",
|
|
300
|
+
layout.checkoutRoot,
|
|
301
|
+
"checkout",
|
|
302
|
+
"--force",
|
|
303
|
+
"-B",
|
|
304
|
+
layout.defaultBranch,
|
|
305
|
+
`origin/${layout.defaultBranch}`
|
|
306
|
+
]);
|
|
307
|
+
ensureGitSuccess2(runGit2(["git", "-C", layout.checkoutRoot, "reset", "--hard", `origin/${layout.defaultBranch}`], layout.projectRoot), ["git", "-C", layout.checkoutRoot, "reset", "--hard", `origin/${layout.defaultBranch}`]);
|
|
308
|
+
ensureGitSuccess2(runGit2(["git", "-C", layout.checkoutRoot, "clean", "-fd"], layout.projectRoot), ["git", "-C", layout.checkoutRoot, "clean", "-fd"]);
|
|
309
|
+
return ensureGitSuccess2(runGit2(["git", "-C", layout.checkoutRoot, "rev-parse", "HEAD"], layout.projectRoot), ["git", "-C", layout.checkoutRoot, "rev-parse", "HEAD"]);
|
|
310
|
+
}
|
|
311
|
+
function syncManagedRepo(projectRoot, repoId) {
|
|
312
|
+
const { layout, headCommit } = refreshManagedRepoMirror(projectRoot, repoId);
|
|
313
|
+
const checkoutCommit = ensureCheckoutFromMirror(layout);
|
|
314
|
+
if (checkoutCommit !== headCommit) {
|
|
315
|
+
throw new Error(`Managed repo checkout ${layout.alias} is out of sync with mirror: expected ${headCommit}, got ${checkoutCommit}.`);
|
|
316
|
+
}
|
|
317
|
+
return { layout, headCommit };
|
|
318
|
+
}
|
|
319
|
+
export {
|
|
320
|
+
syncManagedRepo,
|
|
321
|
+
refreshManagedRepoMirror
|
|
322
|
+
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ManagedRepoId, ManagedRepoMirrorState } from "@rig/contracts";
|
|
2
|
+
export declare function readManagedRepoMirrorState(projectRoot: string, repoId: ManagedRepoId): ManagedRepoMirrorState | null;
|
|
3
|
+
export declare function writeManagedRepoMirrorState(projectRoot: string, repoId: ManagedRepoId, patch: Partial<ManagedRepoMirrorState>): ManagedRepoMirrorState;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/repos-plugin/src/mirror/state.ts
|
|
3
|
+
import { readAuthorityProjectStateJson, writeAuthorityProjectStateJson } from "@rig/runtime/control-plane/json-files";
|
|
4
|
+
|
|
5
|
+
// packages/repos-plugin/src/layout.ts
|
|
6
|
+
import { existsSync } from "fs";
|
|
7
|
+
import { basename, dirname, join, resolve } from "path";
|
|
8
|
+
import { resolveMonorepoRoot } from "@rig/runtime/layout";
|
|
9
|
+
|
|
10
|
+
// packages/repos-plugin/src/registry.ts
|
|
11
|
+
var MANAGED_REPOS = new Map;
|
|
12
|
+
function getManagedRepoEntry(repoId) {
|
|
13
|
+
const entry = MANAGED_REPOS.get(repoId);
|
|
14
|
+
if (!entry) {
|
|
15
|
+
throw new Error(`managed repo not registered: ${repoId}. Plugins contribute repos via RigPlugin.contributes.repoSources; ` + `make sure a plugin declares this id and the plugin host has been initialized.`);
|
|
16
|
+
}
|
|
17
|
+
return entry;
|
|
18
|
+
}
|
|
19
|
+
function listManagedRepoEntries() {
|
|
20
|
+
return Array.from(MANAGED_REPOS.values());
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// packages/repos-plugin/src/layout.ts
|
|
24
|
+
function resolveRepoStateDir(projectRoot) {
|
|
25
|
+
const normalizedProjectRoot = resolve(projectRoot);
|
|
26
|
+
const projectParent = dirname(normalizedProjectRoot);
|
|
27
|
+
if (basename(projectParent) === ".worktrees") {
|
|
28
|
+
const ownerRoot = dirname(projectParent);
|
|
29
|
+
const ownerHasRepoMarkers = existsSync(resolve(ownerRoot, ".git")) || existsSync(resolve(ownerRoot, ".rig", "state"));
|
|
30
|
+
if (ownerHasRepoMarkers) {
|
|
31
|
+
return resolve(ownerRoot, ".rig", "state");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return resolve(projectRoot, ".rig", "state");
|
|
35
|
+
}
|
|
36
|
+
function resolveManagedRepoLayout(projectRoot, repoId) {
|
|
37
|
+
const normalizedProjectRoot = resolve(projectRoot);
|
|
38
|
+
const entry = getManagedRepoEntry(repoId);
|
|
39
|
+
const stateDir = resolveRepoStateDir(normalizedProjectRoot);
|
|
40
|
+
const metadataRelativePath = join("repos", entry.id);
|
|
41
|
+
const metadataRoot = resolve(stateDir, metadataRelativePath);
|
|
42
|
+
const runtimeWorkspace = process.env.RIG_TASK_WORKSPACE?.trim();
|
|
43
|
+
const runsInsideTaskWorktree = runtimeWorkspace && resolve(runtimeWorkspace) === normalizedProjectRoot || basename(dirname(normalizedProjectRoot)) === ".worktrees";
|
|
44
|
+
const isPrimaryManagedRepo = listManagedRepoEntries()[0]?.id === repoId;
|
|
45
|
+
const checkoutRoot = isPrimaryManagedRepo && runsInsideTaskWorktree ? resolveMonorepoRoot(normalizedProjectRoot) : entry.checkoutEnvVar && process.env[entry.checkoutEnvVar]?.trim() ? resolve(process.env[entry.checkoutEnvVar].trim()) : resolve(normalizedProjectRoot, entry.alias);
|
|
46
|
+
return {
|
|
47
|
+
projectRoot: normalizedProjectRoot,
|
|
48
|
+
repoId: entry.id,
|
|
49
|
+
alias: entry.alias,
|
|
50
|
+
defaultBranch: entry.defaultBranch,
|
|
51
|
+
remoteUrl: entry.remoteEnvVar && process.env[entry.remoteEnvVar]?.trim() ? process.env[entry.remoteEnvVar].trim() : entry.defaultRemoteUrl,
|
|
52
|
+
checkoutRoot,
|
|
53
|
+
worktreesRoot: resolve(checkoutRoot, ".worktrees"),
|
|
54
|
+
stateDir,
|
|
55
|
+
metadataRoot,
|
|
56
|
+
metadataRelativePath,
|
|
57
|
+
mirrorRoot: resolve(metadataRoot, "mirror.git"),
|
|
58
|
+
mirrorStatePath: resolve(metadataRoot, "mirror-state.json"),
|
|
59
|
+
mirrorStateRelativePath: join(metadataRelativePath, "mirror-state.json")
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// packages/repos-plugin/src/mirror/state.ts
|
|
64
|
+
var STATE_VERSION = 1;
|
|
65
|
+
function defaultMirrorState(projectRoot, repoId) {
|
|
66
|
+
const layout = resolveManagedRepoLayout(projectRoot, repoId);
|
|
67
|
+
return {
|
|
68
|
+
version: STATE_VERSION,
|
|
69
|
+
repoId,
|
|
70
|
+
remoteUrl: layout.remoteUrl,
|
|
71
|
+
defaultBranch: layout.defaultBranch
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function readManagedRepoMirrorState(projectRoot, repoId) {
|
|
75
|
+
const layout = resolveManagedRepoLayout(projectRoot, repoId);
|
|
76
|
+
return readAuthorityProjectStateJson(projectRoot, layout.mirrorStateRelativePath, null);
|
|
77
|
+
}
|
|
78
|
+
function writeManagedRepoMirrorState(projectRoot, repoId, patch) {
|
|
79
|
+
const current = readManagedRepoMirrorState(projectRoot, repoId) || defaultMirrorState(projectRoot, repoId);
|
|
80
|
+
const next = {
|
|
81
|
+
...current,
|
|
82
|
+
...patch,
|
|
83
|
+
version: STATE_VERSION,
|
|
84
|
+
repoId
|
|
85
|
+
};
|
|
86
|
+
const layout = resolveManagedRepoLayout(projectRoot, repoId);
|
|
87
|
+
writeAuthorityProjectStateJson(projectRoot, layout.mirrorStateRelativePath, next);
|
|
88
|
+
return next;
|
|
89
|
+
}
|
|
90
|
+
export {
|
|
91
|
+
writeManagedRepoMirrorState,
|
|
92
|
+
readManagedRepoMirrorState
|
|
93
|
+
};
|