@bleedingdev/modern-js-create 3.2.0-ultramodern.20 → 3.2.0-ultramodern.22
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 +20 -14
- package/dist/index.js +1460 -321
- package/dist/types/locale/en.d.ts +2 -0
- package/dist/types/locale/zh.d.ts +2 -0
- package/dist/types/ultramodern-workspace.d.ts +13 -0
- package/package.json +6 -6
- package/template/.mise.toml +2 -0
- package/template/README.md +7 -6
- package/template/package.json.handlebars +16 -16
- package/template/pnpm-workspace.yaml +2 -0
- package/template/scripts/validate-ultramodern.mjs.handlebars +138 -26
- package/template/tests/{ultramodern.contract.test.ts → ultramodern.contract.test.ts.handlebars} +28 -0
- package/template-workspace/.agents/agent-reference-repos.json +1 -0
- package/template-workspace/.agents/skills-lock.json +19 -0
- package/template-workspace/.github/workflows/ultramodern-workspace-gates.yml.handlebars +1 -1
- package/template-workspace/.gitignore.handlebars +0 -1
- package/template-workspace/.mise.toml +2 -0
- package/template-workspace/AGENTS.md +8 -8
- package/template-workspace/README.md.handlebars +15 -6
- package/template-workspace/pnpm-workspace.yaml +15 -0
- package/template-workspace/scripts/assert-mf-types.mjs +44 -0
- package/template-workspace/scripts/bootstrap-agent-skills.mjs +73 -13
- package/template-workspace/scripts/setup-agent-reference-repos.mjs +229 -82
- package/template-workspace/scripts/validate-ultramodern-workspace.mjs.handlebars +457 -121
- package/template-workspace/scripts/check-i18n-strings.mjs +0 -83
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
const root = process.cwd();
|
|
5
|
+
const defaultAppDirs = [
|
|
6
|
+
'apps/remotes/remote-commerce',
|
|
7
|
+
'apps/remotes/remote-identity',
|
|
8
|
+
'apps/remotes/remote-design-system',
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
const candidateDirs = process.argv.slice(2);
|
|
12
|
+
const appDirs = candidateDirs.length
|
|
13
|
+
? candidateDirs
|
|
14
|
+
: fs.existsSync(path.join(root, 'module-federation.config.ts'))
|
|
15
|
+
? ['.']
|
|
16
|
+
: defaultAppDirs;
|
|
17
|
+
|
|
18
|
+
for (const appDir of appDirs) {
|
|
19
|
+
const configPath = path.join(root, appDir, 'module-federation.config.ts');
|
|
20
|
+
if (!fs.existsSync(configPath)) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
`Missing Module Federation config: ${path.relative(root, configPath)}`,
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const config = fs.readFileSync(configPath, 'utf-8');
|
|
27
|
+
if (!config.includes('exposes:') || config.includes('dts: false')) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const typesArchivePath = path.join(root, appDir, 'dist/@mf-types.zip');
|
|
32
|
+
if (!fs.existsSync(typesArchivePath)) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`Missing Module Federation DTS archive: ${path.relative(root, typesArchivePath)}`,
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const stats = fs.statSync(typesArchivePath);
|
|
39
|
+
if (stats.size === 0) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
`Empty Module Federation DTS archive: ${path.relative(root, typesArchivePath)}`,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -8,7 +8,7 @@ const lockPath = path.join(root, '.agents/skills-lock.json');
|
|
|
8
8
|
const checkOnly = process.argv.includes('--check');
|
|
9
9
|
const force = process.argv.includes('--force');
|
|
10
10
|
|
|
11
|
-
const readJson =
|
|
11
|
+
const readJson = filePath => JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
12
12
|
|
|
13
13
|
const run = (command, args, options = {}) =>
|
|
14
14
|
execFileSync(command, args, {
|
|
@@ -28,6 +28,23 @@ const cloneSource = (source, targetDir) => {
|
|
|
28
28
|
stdio: 'inherit',
|
|
29
29
|
});
|
|
30
30
|
}
|
|
31
|
+
if (source.commit) {
|
|
32
|
+
try {
|
|
33
|
+
run('git', ['checkout', source.commit], {
|
|
34
|
+
cwd: targetDir,
|
|
35
|
+
stdio: 'inherit',
|
|
36
|
+
});
|
|
37
|
+
} catch {
|
|
38
|
+
run('git', ['fetch', '--depth', '1', 'origin', source.commit], {
|
|
39
|
+
cwd: targetDir,
|
|
40
|
+
stdio: 'inherit',
|
|
41
|
+
});
|
|
42
|
+
run('git', ['checkout', source.commit], {
|
|
43
|
+
cwd: targetDir,
|
|
44
|
+
stdio: 'inherit',
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
31
48
|
};
|
|
32
49
|
|
|
33
50
|
const resolveSkillDir = (sourceRoot, skillName) => {
|
|
@@ -37,7 +54,9 @@ const resolveSkillDir = (sourceRoot, skillName) => {
|
|
|
37
54
|
path.join(sourceRoot, 'skills', 'engineering', skillName),
|
|
38
55
|
path.join(sourceRoot, 'skills', 'productivity', skillName),
|
|
39
56
|
];
|
|
40
|
-
return candidates.find(
|
|
57
|
+
return candidates.find(candidate =>
|
|
58
|
+
fs.existsSync(path.join(candidate, 'SKILL.md')),
|
|
59
|
+
);
|
|
41
60
|
};
|
|
42
61
|
|
|
43
62
|
if (!fs.existsSync(lockPath)) {
|
|
@@ -47,36 +66,77 @@ if (!fs.existsSync(lockPath)) {
|
|
|
47
66
|
|
|
48
67
|
const lock = readJson(lockPath);
|
|
49
68
|
const installDir = path.join(root, lock.installDir ?? '.agents/skills');
|
|
50
|
-
const
|
|
51
|
-
|
|
69
|
+
const sources = lock.sources ?? [];
|
|
70
|
+
const requiredCloneSources = sources.filter(
|
|
71
|
+
source => source.install === 'clone',
|
|
72
|
+
);
|
|
73
|
+
const optionalCloneSources = sources.filter(
|
|
74
|
+
source => source.install === 'clone-if-authorized',
|
|
75
|
+
);
|
|
76
|
+
const requiredSkills = [
|
|
77
|
+
...(lock.baseline ?? []),
|
|
78
|
+
...requiredCloneSources.flatMap(source => source.baseline ?? []),
|
|
79
|
+
].filter(
|
|
80
|
+
(skill, index, skills) =>
|
|
81
|
+
skills.findIndex(candidate => candidate.name === skill.name) === index,
|
|
52
82
|
);
|
|
53
83
|
|
|
54
84
|
if (checkOnly) {
|
|
55
|
-
const
|
|
85
|
+
const missingRequired = requiredSkills
|
|
86
|
+
.map(skill => skill.name)
|
|
87
|
+
.filter(
|
|
88
|
+
skillName => !fs.existsSync(path.join(installDir, skillName, 'SKILL.md')),
|
|
89
|
+
);
|
|
90
|
+
const missingOptional = optionalCloneSources.flatMap(source =>
|
|
56
91
|
(source.baseline ?? [])
|
|
57
|
-
.map(
|
|
58
|
-
.filter(
|
|
92
|
+
.map(skill => skill.name)
|
|
93
|
+
.filter(
|
|
94
|
+
skillName =>
|
|
95
|
+
!fs.existsSync(path.join(installDir, skillName, 'SKILL.md')),
|
|
96
|
+
),
|
|
59
97
|
);
|
|
60
|
-
|
|
98
|
+
|
|
99
|
+
if (missingRequired.length > 0) {
|
|
100
|
+
console.error(
|
|
101
|
+
`Required agent skills not installed: ${missingRequired.join(', ')}. Run pnpm skills:install.`,
|
|
102
|
+
);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (missingOptional.length > 0) {
|
|
61
107
|
console.warn(
|
|
62
|
-
`Private skills not installed: ${
|
|
108
|
+
`Private skills not installed: ${missingOptional.join(', ')}. Run pnpm skills:install if you have access.`,
|
|
63
109
|
);
|
|
64
110
|
} else {
|
|
65
|
-
console.log('
|
|
111
|
+
console.log('Required and private agent skills are installed.');
|
|
112
|
+
process.exit(0);
|
|
66
113
|
}
|
|
114
|
+
console.log('Required agent skills are installed.');
|
|
67
115
|
process.exit(0);
|
|
68
116
|
}
|
|
69
117
|
|
|
70
118
|
fs.mkdirSync(installDir, { recursive: true });
|
|
71
119
|
|
|
72
|
-
for (const source of
|
|
120
|
+
for (const source of [...requiredCloneSources, ...optionalCloneSources]) {
|
|
73
121
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ultramodern-skills-'));
|
|
74
122
|
try {
|
|
75
|
-
|
|
123
|
+
try {
|
|
124
|
+
cloneSource(source, tempDir);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
if (source.install === 'clone-if-authorized') {
|
|
127
|
+
console.warn(
|
|
128
|
+
`Skipping ${source.repository}; current developer may not have access.`,
|
|
129
|
+
);
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
76
134
|
for (const skill of source.baseline ?? []) {
|
|
77
135
|
const sourceSkillDir = resolveSkillDir(tempDir, skill.name);
|
|
78
136
|
if (!sourceSkillDir) {
|
|
79
|
-
throw new Error(
|
|
137
|
+
throw new Error(
|
|
138
|
+
`Skill ${skill.name} not found in ${source.repository}`,
|
|
139
|
+
);
|
|
80
140
|
}
|
|
81
141
|
const targetSkillDir = path.join(installDir, skill.name);
|
|
82
142
|
if (fs.existsSync(targetSkillDir)) {
|
|
@@ -7,7 +7,6 @@ const args = new Set(process.argv.slice(2));
|
|
|
7
7
|
const checkOnly = args.has('--check');
|
|
8
8
|
const configPath = path.join(root, '.agents', 'agent-reference-repos.json');
|
|
9
9
|
const manifestPath = path.join(root, '.modernjs', 'agent-reference-repos.json');
|
|
10
|
-
const tempRoot = path.join(root, '.modernjs', 'agent-reference-repos-tmp');
|
|
11
10
|
|
|
12
11
|
const truthy = value => /^(1|true|yes|on)$/i.test(String(value ?? ''));
|
|
13
12
|
const falsy = value => /^(0|false|no|off)$/i.test(String(value ?? ''));
|
|
@@ -18,6 +17,17 @@ const skipRequested =
|
|
|
18
17
|
const required = truthy(process.env.ULTRAMODERN_AGENT_REPOS_REQUIRED);
|
|
19
18
|
const refresh = truthy(process.env.ULTRAMODERN_AGENT_REPOS_REFRESH);
|
|
20
19
|
|
|
20
|
+
const gitIdentityEnv = {
|
|
21
|
+
GIT_AUTHOR_NAME:
|
|
22
|
+
process.env.GIT_AUTHOR_NAME || 'UltraModern Agent Reference Setup',
|
|
23
|
+
GIT_AUTHOR_EMAIL:
|
|
24
|
+
process.env.GIT_AUTHOR_EMAIL || 'ultramodern-agent-refs@local',
|
|
25
|
+
GIT_COMMITTER_NAME:
|
|
26
|
+
process.env.GIT_COMMITTER_NAME || 'UltraModern Agent Reference Setup',
|
|
27
|
+
GIT_COMMITTER_EMAIL:
|
|
28
|
+
process.env.GIT_COMMITTER_EMAIL || 'ultramodern-agent-refs@local',
|
|
29
|
+
};
|
|
30
|
+
|
|
21
31
|
const log = message => console.log(`[agent-reference-repos] ${message}`);
|
|
22
32
|
const warn = message => console.warn(`[agent-reference-repos] ${message}`);
|
|
23
33
|
|
|
@@ -36,6 +46,11 @@ function run(command, commandArgs, options = {}) {
|
|
|
36
46
|
const result = spawnSync(command, commandArgs, {
|
|
37
47
|
cwd: options.cwd ?? root,
|
|
38
48
|
encoding: 'utf-8',
|
|
49
|
+
env: {
|
|
50
|
+
...process.env,
|
|
51
|
+
...gitIdentityEnv,
|
|
52
|
+
...(options.env ?? {}),
|
|
53
|
+
},
|
|
39
54
|
stdio: options.stdio ?? ['ignore', 'pipe', 'pipe'],
|
|
40
55
|
timeout: options.timeout ?? 120000,
|
|
41
56
|
});
|
|
@@ -74,38 +89,160 @@ function hasGit() {
|
|
|
74
89
|
return result.status === 0;
|
|
75
90
|
}
|
|
76
91
|
|
|
77
|
-
function
|
|
78
|
-
const
|
|
79
|
-
|
|
92
|
+
function hasGitSubtree() {
|
|
93
|
+
const result = spawnSync('git', ['subtree', '-h'], {
|
|
94
|
+
encoding: 'utf-8',
|
|
95
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
96
|
+
});
|
|
97
|
+
return (
|
|
98
|
+
(result.status === 0 || result.status === 129) &&
|
|
99
|
+
result.stdout.includes('usage: git subtree')
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function isGitWorkTree() {
|
|
104
|
+
const result = spawnSync('git', ['rev-parse', '--is-inside-work-tree'], {
|
|
105
|
+
cwd: root,
|
|
106
|
+
encoding: 'utf-8',
|
|
107
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
108
|
+
});
|
|
109
|
+
return result.status === 0 && result.stdout.trim() === 'true';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function hasCommits() {
|
|
113
|
+
const result = spawnSync('git', ['rev-parse', '--verify', 'HEAD'], {
|
|
114
|
+
cwd: root,
|
|
115
|
+
encoding: 'utf-8',
|
|
116
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
117
|
+
});
|
|
118
|
+
return result.status === 0;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function porcelainStatus() {
|
|
122
|
+
return run('git', ['status', '--porcelain'], { timeout: 30000 });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function ensureGitRepository() {
|
|
126
|
+
if (!isGitWorkTree()) {
|
|
127
|
+
if (checkOnly) {
|
|
128
|
+
fail('workspace is not a git repository');
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
log('initializing git repository for agent reference subtrees');
|
|
132
|
+
run('git', ['init'], { timeout: 30000 });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (!hasCommits()) {
|
|
136
|
+
if (checkOnly) {
|
|
137
|
+
fail('workspace has no initial git commit');
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
log('creating initial workspace commit before adding reference subtrees');
|
|
141
|
+
run('git', ['add', '-A'], { timeout: 30000 });
|
|
142
|
+
run('git', ['commit', '-m', 'Initialize UltraModern workspace'], {
|
|
143
|
+
timeout: 120000,
|
|
144
|
+
});
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const status = porcelainStatus();
|
|
149
|
+
if (status) {
|
|
150
|
+
fail(
|
|
151
|
+
'workspace has uncommitted changes; commit or stash them before installing reference subtrees',
|
|
152
|
+
);
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function remoteCommit(repo) {
|
|
160
|
+
let output = run('git', ['ls-remote', repo.url, `refs/heads/${repo.ref}`], {
|
|
161
|
+
timeout: 120000,
|
|
162
|
+
});
|
|
163
|
+
if (!output) {
|
|
164
|
+
output = run('git', ['ls-remote', repo.url, repo.ref], {
|
|
165
|
+
timeout: 120000,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
const [commit] = output.split(/\s+/);
|
|
169
|
+
if (!/^[a-f0-9]{40}$/i.test(commit ?? '')) {
|
|
170
|
+
throw new Error(`Could not resolve ${repo.url}#${repo.ref}`);
|
|
171
|
+
}
|
|
172
|
+
return commit;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function subtreeCommitExists(repo) {
|
|
176
|
+
const result = spawnSync(
|
|
177
|
+
'git',
|
|
178
|
+
[
|
|
179
|
+
'log',
|
|
180
|
+
'--grep',
|
|
181
|
+
`git-subtree-dir: ${repo.path}`,
|
|
182
|
+
'--format=%H',
|
|
183
|
+
'-n',
|
|
184
|
+
'1',
|
|
185
|
+
],
|
|
186
|
+
{
|
|
187
|
+
cwd: root,
|
|
188
|
+
encoding: 'utf-8',
|
|
189
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
190
|
+
},
|
|
191
|
+
);
|
|
192
|
+
return result.status === 0 && result.stdout.trim().length > 0;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function installedManifestEntry(repo) {
|
|
196
|
+
if (!fs.existsSync(manifestPath)) {
|
|
80
197
|
return undefined;
|
|
81
198
|
}
|
|
82
199
|
try {
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
return marker;
|
|
86
|
-
}
|
|
200
|
+
const manifest = readJson(manifestPath);
|
|
201
|
+
return manifest.repositories?.find(entry => entry.id === repo.id);
|
|
87
202
|
} catch {
|
|
88
203
|
return undefined;
|
|
89
204
|
}
|
|
90
|
-
return undefined;
|
|
91
205
|
}
|
|
92
206
|
|
|
93
|
-
function
|
|
207
|
+
function assertSubtreePresent(repo) {
|
|
94
208
|
assertSafeRepoPath(repo.path);
|
|
95
209
|
const targetPath = path.join(root, repo.path);
|
|
96
|
-
|
|
210
|
+
if (!fs.existsSync(targetPath)) {
|
|
211
|
+
fail(`${repo.path} is missing`);
|
|
212
|
+
return undefined;
|
|
213
|
+
}
|
|
214
|
+
if (!subtreeCommitExists(repo)) {
|
|
215
|
+
fail(`${repo.path} is present but has no git-subtree commit evidence`);
|
|
216
|
+
return undefined;
|
|
217
|
+
}
|
|
218
|
+
return (
|
|
219
|
+
installedManifestEntry(repo) ?? {
|
|
220
|
+
id: repo.id,
|
|
221
|
+
name: repo.name,
|
|
222
|
+
url: repo.url,
|
|
223
|
+
ref: repo.ref,
|
|
224
|
+
path: repo.path,
|
|
225
|
+
readOnly: repo.readOnly !== false,
|
|
226
|
+
status: 'present',
|
|
227
|
+
strategy: 'git-subtree-squash',
|
|
228
|
+
}
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function addSubtree(repo) {
|
|
233
|
+
assertSafeRepoPath(repo.path);
|
|
234
|
+
const targetPath = path.join(root, repo.path);
|
|
235
|
+
const existing = fs.existsSync(targetPath);
|
|
97
236
|
|
|
98
237
|
if (existing && !refresh) {
|
|
99
|
-
|
|
100
|
-
return { ...existing, status: 'present' };
|
|
238
|
+
return assertSubtreePresent(repo);
|
|
101
239
|
}
|
|
102
240
|
|
|
103
|
-
if (
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
fs.rmSync(targetPath, { recursive: true, force: true });
|
|
241
|
+
if (existing && refresh) {
|
|
242
|
+
fail(
|
|
243
|
+
`${repo.path} already exists; refresh for subtree references is intentionally manual`,
|
|
244
|
+
);
|
|
245
|
+
return undefined;
|
|
109
246
|
}
|
|
110
247
|
|
|
111
248
|
if (checkOnly) {
|
|
@@ -113,53 +250,70 @@ function materializeRepository(repo) {
|
|
|
113
250
|
return undefined;
|
|
114
251
|
}
|
|
115
252
|
|
|
116
|
-
|
|
117
|
-
|
|
253
|
+
const commit = remoteCommit(repo);
|
|
254
|
+
log(`adding ${repo.name} as git subtree at ${repo.path} (${commit})`);
|
|
255
|
+
run('git', ['fetch', '--depth', '1', repo.url, repo.ref], {
|
|
256
|
+
timeout: 300000,
|
|
257
|
+
});
|
|
258
|
+
run(
|
|
259
|
+
'git',
|
|
260
|
+
[
|
|
261
|
+
'subtree',
|
|
262
|
+
'add',
|
|
263
|
+
'--prefix',
|
|
264
|
+
repo.path,
|
|
265
|
+
'FETCH_HEAD',
|
|
266
|
+
'--squash',
|
|
267
|
+
'-m',
|
|
268
|
+
`Add ${repo.name} agent reference repo`,
|
|
269
|
+
],
|
|
270
|
+
{ timeout: 600000 },
|
|
271
|
+
);
|
|
118
272
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
],
|
|
134
|
-
{ timeout: 300000 },
|
|
135
|
-
);
|
|
136
|
-
const commit = run('git', ['-C', tempPath, 'rev-parse', 'HEAD']);
|
|
137
|
-
fs.rmSync(path.join(tempPath, '.git'), { recursive: true, force: true });
|
|
138
|
-
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
139
|
-
fs.renameSync(tempPath, targetPath);
|
|
273
|
+
return {
|
|
274
|
+
schemaVersion: 1,
|
|
275
|
+
id: repo.id,
|
|
276
|
+
name: repo.name,
|
|
277
|
+
url: repo.url,
|
|
278
|
+
ref: repo.ref,
|
|
279
|
+
commit,
|
|
280
|
+
path: repo.path,
|
|
281
|
+
readOnly: repo.readOnly !== false,
|
|
282
|
+
strategy: 'git-subtree-squash',
|
|
283
|
+
status: 'installed',
|
|
284
|
+
installedAt: new Date().toISOString(),
|
|
285
|
+
};
|
|
286
|
+
}
|
|
140
287
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
)
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
288
|
+
function writeManifest(entries) {
|
|
289
|
+
fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
|
|
290
|
+
fs.writeFileSync(
|
|
291
|
+
manifestPath,
|
|
292
|
+
`${JSON.stringify(
|
|
293
|
+
{
|
|
294
|
+
schemaVersion: 1,
|
|
295
|
+
generatedAt: new Date().toISOString(),
|
|
296
|
+
strategy: 'git-subtree-squash',
|
|
297
|
+
installDir: 'repos',
|
|
298
|
+
repositories: entries,
|
|
299
|
+
},
|
|
300
|
+
null,
|
|
301
|
+
2,
|
|
302
|
+
)}\n`,
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function commitManifestIfChanged() {
|
|
307
|
+
const status = run('git', ['status', '--porcelain', '--', manifestPath], {
|
|
308
|
+
timeout: 30000,
|
|
309
|
+
});
|
|
310
|
+
if (!status) {
|
|
311
|
+
return;
|
|
162
312
|
}
|
|
313
|
+
run('git', ['add', manifestPath], { timeout: 30000 });
|
|
314
|
+
run('git', ['commit', '-m', 'Record agent reference repo manifest'], {
|
|
315
|
+
timeout: 120000,
|
|
316
|
+
});
|
|
163
317
|
}
|
|
164
318
|
|
|
165
319
|
function main() {
|
|
@@ -180,30 +334,25 @@ function main() {
|
|
|
180
334
|
fail('git is required to install agent reference repositories');
|
|
181
335
|
return;
|
|
182
336
|
}
|
|
337
|
+
if (!hasGitSubtree()) {
|
|
338
|
+
fail('git subtree is required to install agent reference repositories');
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
if (!ensureGitRepository()) {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
183
344
|
|
|
184
|
-
const
|
|
345
|
+
const entries = [];
|
|
185
346
|
for (const repo of config.repositories ?? []) {
|
|
186
|
-
const result =
|
|
347
|
+
const result = checkOnly ? assertSubtreePresent(repo) : addSubtree(repo);
|
|
187
348
|
if (result) {
|
|
188
|
-
|
|
349
|
+
entries.push(result);
|
|
189
350
|
}
|
|
190
351
|
}
|
|
191
352
|
|
|
192
353
|
if (!checkOnly) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
manifestPath,
|
|
196
|
-
`${JSON.stringify(
|
|
197
|
-
{
|
|
198
|
-
schemaVersion: 1,
|
|
199
|
-
generatedAt: new Date().toISOString(),
|
|
200
|
-
installDir: config.installDir ?? 'repos',
|
|
201
|
-
repositories: installed,
|
|
202
|
-
},
|
|
203
|
-
null,
|
|
204
|
-
2,
|
|
205
|
-
)}\n`,
|
|
206
|
-
);
|
|
354
|
+
writeManifest(entries);
|
|
355
|
+
commitManifestIfChanged();
|
|
207
356
|
}
|
|
208
357
|
}
|
|
209
358
|
|
|
@@ -212,6 +361,4 @@ try {
|
|
|
212
361
|
} catch (error) {
|
|
213
362
|
console.error(`[agent-reference-repos] ${error.message}`);
|
|
214
363
|
process.exitCode = 1;
|
|
215
|
-
} finally {
|
|
216
|
-
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
217
364
|
}
|