@xcanwin/manyoyo 3.2.0 → 3.4.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/README.md +15 -6
- package/bin/manyoyo.js +184 -3
- package/docker/manyoyo.Dockerfile +78 -17
- package/docs/README_EN.md +15 -6
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
[English](docs/README_EN.md) |
|
|
1
|
+
[English](docs/README_EN.md) | << [中文](README.md) >>
|
|
2
2
|
|
|
3
3
|
---
|
|
4
4
|
|
|
@@ -49,15 +49,23 @@ npm install -g .
|
|
|
49
49
|
# 拉取基础镜像
|
|
50
50
|
podman pull ubuntu:24.04
|
|
51
51
|
|
|
52
|
-
# 使用 manyoyo
|
|
53
|
-
manyoyo --ib all # 构建 all
|
|
54
|
-
manyoyo --ib common # 构建 common
|
|
55
|
-
manyoyo --ib go,codex,java,gemini #
|
|
52
|
+
# 使用 manyoyo 构建镜像(推荐,自动使用缓存加速)
|
|
53
|
+
manyoyo --ib all # 构建 all 版本(自动使用缓存,首次下载后 2 天内极速构建)
|
|
54
|
+
manyoyo --ib common # 构建 common 版本
|
|
55
|
+
manyoyo --ib go,codex,java,gemini # 构建自定义组合版本
|
|
56
|
+
|
|
57
|
+
# 工作原理:
|
|
58
|
+
# - 首次构建:自动下载 Node.js、JDT LSP、gopls 等到 docker/cache/
|
|
59
|
+
# - 2天内再次构建:直接使用本地缓存,速度提升约 5 倍
|
|
60
|
+
# - 缓存过期后:自动重新下载最新版本
|
|
56
61
|
|
|
57
62
|
# 自定义镜像名称和版本
|
|
58
63
|
manyoyo --ib all --in myimage --iv 2.0.0
|
|
59
64
|
# 构建:myimage:2.0.0-all
|
|
60
65
|
|
|
66
|
+
# 清理镜像
|
|
67
|
+
manyoyo --ip # 清理悬空镜像和 <none> 镜像
|
|
68
|
+
|
|
61
69
|
# 或手动构建(不推荐)
|
|
62
70
|
iv=1.4.0 && podman build -t localhost/xcanwin/manyoyo:$iv-all -f docker/manyoyo.Dockerfile . --build-arg EXT=all --no-cache
|
|
63
71
|
podman image prune -f
|
|
@@ -207,7 +215,8 @@ docker ps -a
|
|
|
207
215
|
| `-x CMD` | `--sf`, `--shell-full` | 完整命令(替代 --sp, -s 和 --) |
|
|
208
216
|
| `-y CLI` | `--yolo` | 无需确认运行 AI 智能体 |
|
|
209
217
|
| `-m MODE` | `--cm`, `--cont-mode` | 设置容器模式(common, dind, mdsock) |
|
|
210
|
-
| `--ib EXT` | `--image-build` | 构建镜像,EXT
|
|
218
|
+
| `--ib EXT` | `--image-build` | 构建镜像,EXT 为镜像变体,自动使用缓存加速 |
|
|
219
|
+
| `--ip` | `--image-prune` | 清理悬空镜像和 `<none>` 镜像 |
|
|
211
220
|
| `--install NAME` | | 安装 manyoyo 命令 |
|
|
212
221
|
| `-V` | `--version` | 显示版本 |
|
|
213
222
|
| `-h` | `--help` | 显示帮助 |
|
package/bin/manyoyo.js
CHANGED
|
@@ -87,6 +87,8 @@ function showHelp() {
|
|
|
87
87
|
console.log(" 例如 common, dind, mdsock");
|
|
88
88
|
console.log(" --ib|--image-build EXT 构建镜像,EXT 为镜像变体,逗号分割");
|
|
89
89
|
console.log(" 例如 \"common\" (默认值), \"all\", \"go,codex,java,gemini\" ...");
|
|
90
|
+
console.log(" (自动使用缓存加速,缓存过期后自动重新下载)");
|
|
91
|
+
console.log(" --ip|--image-prune 清理悬空镜像和 <none> 镜像");
|
|
90
92
|
console.log(" --install NAME 安装manyoyo命令");
|
|
91
93
|
console.log(" 例如 docker-cli-plugin");
|
|
92
94
|
console.log(" -V|--version 显示版本");
|
|
@@ -300,6 +302,179 @@ function getContList() {
|
|
|
300
302
|
}
|
|
301
303
|
}
|
|
302
304
|
|
|
305
|
+
function pruneDanglingImages() {
|
|
306
|
+
console.log(`\n${YELLOW}清理悬空镜像...${NC}`);
|
|
307
|
+
execSync(`${DOCKER_CMD} image prune -f`, { stdio: 'inherit' });
|
|
308
|
+
|
|
309
|
+
// Remove remaining <none> images
|
|
310
|
+
try {
|
|
311
|
+
const imagesOutput = execSync(`${DOCKER_CMD} images -a --format "{{.ID}} {{.Repository}}"`, { encoding: 'utf-8' });
|
|
312
|
+
const noneImages = imagesOutput
|
|
313
|
+
.split('\n')
|
|
314
|
+
.filter(line => line.includes('<none>'))
|
|
315
|
+
.map(line => line.split(' ')[0])
|
|
316
|
+
.filter(id => id);
|
|
317
|
+
|
|
318
|
+
if (noneImages.length > 0) {
|
|
319
|
+
console.log(`${YELLOW}清理剩余的 <none> 镜像 (${noneImages.length} 个)...${NC}`);
|
|
320
|
+
execSync(`${DOCKER_CMD} rmi -f ${noneImages.join(' ')}`, { stdio: 'inherit' });
|
|
321
|
+
}
|
|
322
|
+
} catch (e) {
|
|
323
|
+
// Ignore errors if no <none> images found
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
console.log(`${GREEN}✅ 清理完成${NC}`);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
async function prepareBuildCache(ext) {
|
|
330
|
+
const cacheDir = path.join(__dirname, '../docker/cache');
|
|
331
|
+
const timestampFile = path.join(cacheDir, '.timestamps.json');
|
|
332
|
+
const cacheTTLDays = 2;
|
|
333
|
+
|
|
334
|
+
console.log(`\n${CYAN}准备构建缓存...${NC}`);
|
|
335
|
+
|
|
336
|
+
// Create cache directory
|
|
337
|
+
if (!fs.existsSync(cacheDir)) {
|
|
338
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Load timestamps
|
|
342
|
+
let timestamps = {};
|
|
343
|
+
if (fs.existsSync(timestampFile)) {
|
|
344
|
+
try {
|
|
345
|
+
timestamps = JSON.parse(fs.readFileSync(timestampFile, 'utf-8'));
|
|
346
|
+
} catch (e) {
|
|
347
|
+
timestamps = {};
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const now = new Date();
|
|
352
|
+
const isExpired = (key) => {
|
|
353
|
+
if (!timestamps[key]) return true;
|
|
354
|
+
const cachedTime = new Date(timestamps[key]);
|
|
355
|
+
const diffDays = (now - cachedTime) / (1000 * 60 * 60 * 24);
|
|
356
|
+
return diffDays > cacheTTLDays;
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
// Determine architecture
|
|
360
|
+
const arch = process.arch === 'x64' ? 'amd64' : process.arch === 'arm64' ? 'arm64' : process.arch;
|
|
361
|
+
const archNode = arch === 'amd64' ? 'x64' : 'arm64';
|
|
362
|
+
|
|
363
|
+
// Prepare Node.js cache
|
|
364
|
+
const nodeCacheDir = path.join(cacheDir, 'node');
|
|
365
|
+
const nodeVersion = 24;
|
|
366
|
+
const nodeKey = 'node/'; // 使用目录级别的相对路径
|
|
367
|
+
|
|
368
|
+
if (!fs.existsSync(nodeCacheDir)) {
|
|
369
|
+
fs.mkdirSync(nodeCacheDir, { recursive: true });
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const hasNodeCache = fs.existsSync(nodeCacheDir) && fs.readdirSync(nodeCacheDir).some(f => f.startsWith('node-') && f.includes(`linux-${archNode}`));
|
|
373
|
+
if (!hasNodeCache || isExpired(nodeKey)) {
|
|
374
|
+
console.log(`${YELLOW}下载 Node.js ${nodeVersion} (${archNode})...${NC}`);
|
|
375
|
+
const mirror = 'https://mirrors.tencent.com/nodejs-release';
|
|
376
|
+
try {
|
|
377
|
+
const shasum = execSync(`curl -sL ${mirror}/latest-v${nodeVersion}.x/SHASUMS256.txt | grep linux-${archNode}.tar.gz | awk '{print $2}'`, { encoding: 'utf-8' }).trim();
|
|
378
|
+
const nodeUrl = `${mirror}/latest-v${nodeVersion}.x/${shasum}`;
|
|
379
|
+
const nodeTargetPath = path.join(nodeCacheDir, shasum);
|
|
380
|
+
execSync(`curl -fsSL "${nodeUrl}" -o "${nodeTargetPath}"`, { stdio: 'inherit' });
|
|
381
|
+
timestamps[nodeKey] = now.toISOString();
|
|
382
|
+
fs.writeFileSync(timestampFile, JSON.stringify(timestamps, null, 2));
|
|
383
|
+
console.log(`${GREEN}✓ Node.js 下载完成${NC}`);
|
|
384
|
+
} catch (e) {
|
|
385
|
+
console.error(`${RED}错误: Node.js 下载失败${NC}`);
|
|
386
|
+
throw e;
|
|
387
|
+
}
|
|
388
|
+
} else {
|
|
389
|
+
console.log(`${GREEN}✓ Node.js 缓存已存在${NC}`);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Prepare JDT LSP cache (for java variant)
|
|
393
|
+
if (ext === 'all' || ext.includes('java')) {
|
|
394
|
+
const jdtlsCacheDir = path.join(cacheDir, 'jdtls');
|
|
395
|
+
const jdtlsKey = 'jdtls/jdt-language-server-latest.tar.gz'; // 使用相对路径
|
|
396
|
+
const jdtlsPath = path.join(cacheDir, jdtlsKey);
|
|
397
|
+
|
|
398
|
+
if (!fs.existsSync(jdtlsCacheDir)) {
|
|
399
|
+
fs.mkdirSync(jdtlsCacheDir, { recursive: true });
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (!fs.existsSync(jdtlsPath) || isExpired(jdtlsKey)) {
|
|
403
|
+
console.log(`${YELLOW}下载 JDT Language Server...${NC}`);
|
|
404
|
+
const jdtUrl = 'https://download.eclipse.org/jdtls/snapshots/jdt-language-server-latest.tar.gz';
|
|
405
|
+
try {
|
|
406
|
+
execSync(`curl -fsSL "${jdtUrl}" -o "${jdtlsPath}"`, { stdio: 'inherit' });
|
|
407
|
+
timestamps[jdtlsKey] = now.toISOString();
|
|
408
|
+
fs.writeFileSync(timestampFile, JSON.stringify(timestamps, null, 2));
|
|
409
|
+
console.log(`${GREEN}✓ JDT LSP 下载完成${NC}`);
|
|
410
|
+
} catch (e) {
|
|
411
|
+
console.error(`${RED}错误: JDT LSP 下载失败${NC}`);
|
|
412
|
+
throw e;
|
|
413
|
+
}
|
|
414
|
+
} else {
|
|
415
|
+
console.log(`${GREEN}✓ JDT LSP 缓存已存在${NC}`);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Prepare gopls cache (for go variant)
|
|
420
|
+
if (ext === 'all' || ext.includes('go')) {
|
|
421
|
+
const goplsCacheDir = path.join(cacheDir, 'gopls');
|
|
422
|
+
const goplsKey = `gopls/gopls-linux-${arch}`; // 使用相对路径
|
|
423
|
+
const goplsPath = path.join(cacheDir, goplsKey);
|
|
424
|
+
|
|
425
|
+
if (!fs.existsSync(goplsCacheDir)) {
|
|
426
|
+
fs.mkdirSync(goplsCacheDir, { recursive: true });
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (!fs.existsSync(goplsPath) || isExpired(goplsKey)) {
|
|
430
|
+
console.log(`${YELLOW}下载 gopls (${arch})...${NC}`);
|
|
431
|
+
try {
|
|
432
|
+
// Download using go install in temporary environment
|
|
433
|
+
const tmpGoPath = path.join(cacheDir, '.tmp-go');
|
|
434
|
+
|
|
435
|
+
// Clean up existing temp directory (with go clean for mod cache)
|
|
436
|
+
if (fs.existsSync(tmpGoPath)) {
|
|
437
|
+
try {
|
|
438
|
+
execSync(`GOPATH="${tmpGoPath}" go clean -modcache 2>/dev/null || true`, { stdio: 'inherit' });
|
|
439
|
+
execSync(`chmod -R u+w "${tmpGoPath}" 2>/dev/null || true`, { stdio: 'inherit' });
|
|
440
|
+
execSync(`rm -rf "${tmpGoPath}"`, { stdio: 'inherit' });
|
|
441
|
+
} catch (e) {
|
|
442
|
+
// Ignore cleanup errors
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
fs.mkdirSync(tmpGoPath, { recursive: true });
|
|
446
|
+
|
|
447
|
+
execSync(`GOPATH="${tmpGoPath}" GOOS=linux GOARCH=${arch} go install golang.org/x/tools/gopls@latest`, { stdio: 'inherit' });
|
|
448
|
+
execSync(`cp "${tmpGoPath}/bin/linux_${arch}/gopls" "${goplsPath}" || cp "${tmpGoPath}/bin/gopls" "${goplsPath}"`, { stdio: 'inherit' });
|
|
449
|
+
execSync(`chmod +x "${goplsPath}"`, { stdio: 'inherit' });
|
|
450
|
+
|
|
451
|
+
// Save timestamp immediately after successful download
|
|
452
|
+
timestamps[goplsKey] = now.toISOString();
|
|
453
|
+
fs.writeFileSync(timestampFile, JSON.stringify(timestamps, null, 2));
|
|
454
|
+
console.log(`${GREEN}✓ gopls 下载完成${NC}`);
|
|
455
|
+
|
|
456
|
+
// Clean up temp directory (with go clean for mod cache)
|
|
457
|
+
try {
|
|
458
|
+
execSync(`GOPATH="${tmpGoPath}" go clean -modcache 2>/dev/null || true`, { stdio: 'inherit' });
|
|
459
|
+
execSync(`chmod -R u+w "${tmpGoPath}" 2>/dev/null || true`, { stdio: 'inherit' });
|
|
460
|
+
execSync(`rm -rf "${tmpGoPath}"`, { stdio: 'inherit' });
|
|
461
|
+
} catch (e) {
|
|
462
|
+
console.log(`${YELLOW}提示: 临时目录清理失败,可手动删除 ${tmpGoPath}${NC}`);
|
|
463
|
+
}
|
|
464
|
+
} catch (e) {
|
|
465
|
+
console.error(`${RED}错误: gopls 下载失败${NC}`);
|
|
466
|
+
throw e;
|
|
467
|
+
}
|
|
468
|
+
} else {
|
|
469
|
+
console.log(`${GREEN}✓ gopls 缓存已存在${NC}`);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Save timestamps
|
|
474
|
+
fs.writeFileSync(timestampFile, JSON.stringify(timestamps, null, 2));
|
|
475
|
+
console.log(`${GREEN}✅ 构建缓存准备完成${NC}\n`);
|
|
476
|
+
}
|
|
477
|
+
|
|
303
478
|
async function buildImage(ext, imageName, imageVersion) {
|
|
304
479
|
// Use package.json imageVersion if not specified
|
|
305
480
|
const version = imageVersion || IMAGE_VERSION_BASE;
|
|
@@ -308,6 +483,9 @@ async function buildImage(ext, imageName, imageVersion) {
|
|
|
308
483
|
console.log(`${CYAN}🔨 正在构建镜像: ${YELLOW}${fullImageTag}${NC}`);
|
|
309
484
|
console.log(`${BLUE}构建参数: EXT=${ext}${NC}\n`);
|
|
310
485
|
|
|
486
|
+
// Prepare cache (自动检测并下载缺失的文件)
|
|
487
|
+
await prepareBuildCache(ext);
|
|
488
|
+
|
|
311
489
|
// Find Dockerfile path
|
|
312
490
|
const dockerfilePath = path.join(__dirname, '../docker/manyoyo.Dockerfile');
|
|
313
491
|
if (!fs.existsSync(dockerfilePath)) {
|
|
@@ -331,9 +509,7 @@ async function buildImage(ext, imageName, imageVersion) {
|
|
|
331
509
|
console.log(` manyoyo -n test --in ${imageName} --iv ${version}-${ext} -y c`);
|
|
332
510
|
|
|
333
511
|
// Prune dangling images
|
|
334
|
-
|
|
335
|
-
execSync(`${DOCKER_CMD} image prune -f`, { stdio: 'inherit' });
|
|
336
|
-
console.log(`${GREEN}✅ 清理完成${NC}`);
|
|
512
|
+
pruneDanglingImages();
|
|
337
513
|
} catch (e) {
|
|
338
514
|
console.error(`${RED}错误: 镜像构建失败${NC}`);
|
|
339
515
|
process.exit(1);
|
|
@@ -491,6 +667,11 @@ function parseArguments(argv) {
|
|
|
491
667
|
i += 2;
|
|
492
668
|
break;
|
|
493
669
|
|
|
670
|
+
case '--ip':
|
|
671
|
+
case '--image-prune':
|
|
672
|
+
pruneDanglingImages();
|
|
673
|
+
process.exit(0);
|
|
674
|
+
|
|
494
675
|
case '--install':
|
|
495
676
|
installManyoyo(args[i + 1]);
|
|
496
677
|
process.exit(0);
|
|
@@ -1,3 +1,60 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# Stage 1: 缓存准备阶段 - 智能检测缓存或下载
|
|
3
|
+
# ==============================================================================
|
|
4
|
+
FROM ubuntu:24.04 AS cache-stage
|
|
5
|
+
|
|
6
|
+
ARG TARGETARCH
|
|
7
|
+
|
|
8
|
+
# 复制缓存目录(可能为空)
|
|
9
|
+
COPY ./docker/cache/ /cache/
|
|
10
|
+
|
|
11
|
+
RUN <<EOX
|
|
12
|
+
# 确定架构
|
|
13
|
+
case "$TARGETARCH" in
|
|
14
|
+
amd64) ARCH_NODE="x64"; ARCH_GO="amd64" ;;
|
|
15
|
+
arm64) ARCH_NODE="arm64"; ARCH_GO="arm64" ;;
|
|
16
|
+
*) ARCH_NODE="$TARGETARCH"; ARCH_GO="$TARGETARCH" ;;
|
|
17
|
+
esac
|
|
18
|
+
|
|
19
|
+
# Node.js: 检测缓存,不存在则下载
|
|
20
|
+
mkdir -p /opt/node
|
|
21
|
+
if ls /cache/node/node-*-linux-${ARCH_NODE}.tar.gz 1> /dev/null 2>&1; then
|
|
22
|
+
echo "使用 Node.js 缓存"
|
|
23
|
+
NODE_TAR=$(ls /cache/node/node-*-linux-${ARCH_NODE}.tar.gz | head -1)
|
|
24
|
+
tar -xzf ${NODE_TAR} -C /opt/node --strip-components=1 --exclude='*.md' --exclude='LICENSE'
|
|
25
|
+
else
|
|
26
|
+
echo "下载 Node.js"
|
|
27
|
+
NVM_NODEJS_ORG_MIRROR=https://mirrors.tencent.com/nodejs-release/
|
|
28
|
+
NODE_TAR=$(curl -sL ${NVM_NODEJS_ORG_MIRROR}/latest-v24.x/SHASUMS256.txt | grep linux-${ARCH_NODE}.tar.gz | awk '{print $2}')
|
|
29
|
+
curl -fsSL ${NVM_NODEJS_ORG_MIRROR}/latest-v24.x/${NODE_TAR} | tar -xz -C /opt/node --strip-components=1 --exclude='*.md' --exclude='LICENSE'
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# JDT LSP: 检测缓存,不存在则下载
|
|
33
|
+
mkdir -p /opt/jdtls
|
|
34
|
+
if [ -f /cache/jdtls/jdt-language-server-latest.tar.gz ]; then
|
|
35
|
+
echo "使用 JDT LSP 缓存"
|
|
36
|
+
tar -xzf /cache/jdtls/jdt-language-server-latest.tar.gz -C /opt/jdtls
|
|
37
|
+
else
|
|
38
|
+
echo "下载 JDT LSP"
|
|
39
|
+
curl -fsSL https://download.eclipse.org/jdtls/snapshots/jdt-language-server-latest.tar.gz | tar -xz -C /opt/jdtls
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# gopls: 检测缓存,不存在则下载
|
|
43
|
+
mkdir -p /opt/gopls
|
|
44
|
+
if [ -f /cache/gopls/gopls-linux-${ARCH_GO} ]; then
|
|
45
|
+
echo "使用 gopls 缓存"
|
|
46
|
+
cp /cache/gopls/gopls-linux-${ARCH_GO} /opt/gopls/gopls
|
|
47
|
+
chmod +x /opt/gopls/gopls
|
|
48
|
+
else
|
|
49
|
+
echo "下载 gopls (需要 go 环境)"
|
|
50
|
+
# gopls 需要编译,这里跳过,在最终阶段处理
|
|
51
|
+
touch /opt/gopls/.no-cache
|
|
52
|
+
fi
|
|
53
|
+
EOX
|
|
54
|
+
|
|
55
|
+
# ==============================================================================
|
|
56
|
+
# Stage 2: 最终镜像
|
|
57
|
+
# ==============================================================================
|
|
1
58
|
FROM ubuntu:24.04
|
|
2
59
|
|
|
3
60
|
ARG TARGETARCH
|
|
@@ -45,16 +102,11 @@ RUN <<EOX
|
|
|
45
102
|
rm -rf /tmp/* /var/tmp/* /var/log/apt /var/log/*.log /var/lib/apt/lists/* ~/.cache ~/.npm ~/go/pkg/mod/cache
|
|
46
103
|
EOX
|
|
47
104
|
|
|
105
|
+
# 从 cache-stage 复制 Node.js(缓存或下载)
|
|
106
|
+
COPY --from=cache-stage /opt/node /usr/local
|
|
107
|
+
|
|
48
108
|
RUN <<EOX
|
|
49
|
-
#
|
|
50
|
-
case "$TARGETARCH" in
|
|
51
|
-
amd64) ARCH_NODE="x64" ;;
|
|
52
|
-
arm64) ARCH_NODE="arm64" ;;
|
|
53
|
-
*) ARCH_NODE="$TARGETARCH" ;;
|
|
54
|
-
esac
|
|
55
|
-
NVM_NODEJS_ORG_MIRROR=https://mirrors.tencent.com/nodejs-release/
|
|
56
|
-
NODE_TAR=$(curl -sL ${NVM_NODEJS_ORG_MIRROR}/latest-v${NODE_VERSION}.x/SHASUMS256.txt | grep linux-${ARCH_NODE}.tar.gz | awk '{print $2}')
|
|
57
|
-
curl -fsSL ${NVM_NODEJS_ORG_MIRROR}/latest-v${NODE_VERSION}.x/${NODE_TAR} | tar -xz -C /usr/local --strip-components=1 --exclude='*.md' --exclude='LICENSE'
|
|
109
|
+
# 配置 node.js
|
|
58
110
|
npm config set registry=https://mirrors.tencent.com/npm/
|
|
59
111
|
npm install -g npm
|
|
60
112
|
|
|
@@ -164,18 +216,16 @@ EOF
|
|
|
164
216
|
rm -rf /tmp/* /var/tmp/* /var/log/apt /var/log/*.log /var/lib/apt/lists/* ~/.cache ~/.npm ~/go/pkg/mod/cache
|
|
165
217
|
EOX
|
|
166
218
|
|
|
167
|
-
|
|
219
|
+
# 从 cache-stage 复制 JDT LSP(缓存或下载)
|
|
220
|
+
COPY --from=cache-stage /opt/jdtls /root/.local/share/jdtls
|
|
221
|
+
|
|
168
222
|
RUN <<EOX
|
|
169
223
|
# 安装 java
|
|
170
224
|
case ",$EXT," in *,all,*|*,java,*)
|
|
171
225
|
apt-get update -y
|
|
172
226
|
apt-get install -y --no-install-recommends openjdk-21-jdk maven
|
|
173
227
|
|
|
174
|
-
#
|
|
175
|
-
mkdir -p ~/.local/share/jdtls
|
|
176
|
-
# wget -O /tmp/jdt-language-server-latest.tar.gz https://download.eclipse.org/jdtls/snapshots/jdt-language-server-latest.tar.gz
|
|
177
|
-
tar -xzf /tmp/jdt-language-server-latest.tar.gz -C ~/.local/share/jdtls
|
|
178
|
-
rm -f /tmp/jdt-language-server-latest.tar.gz
|
|
228
|
+
# 配置 LSP服务(java)
|
|
179
229
|
ln -sf ~/.local/share/jdtls/bin/jdtls /usr/local/bin/jdtls
|
|
180
230
|
|
|
181
231
|
# 清理
|
|
@@ -184,6 +234,9 @@ RUN <<EOX
|
|
|
184
234
|
;; esac
|
|
185
235
|
EOX
|
|
186
236
|
|
|
237
|
+
# 从 cache-stage 复制 gopls(缓存或下载)
|
|
238
|
+
COPY --from=cache-stage /opt/gopls /tmp/gopls-cache
|
|
239
|
+
|
|
187
240
|
RUN <<EOX
|
|
188
241
|
# 安装 go
|
|
189
242
|
case ",$EXT," in *,all,*|*,go,*)
|
|
@@ -192,8 +245,16 @@ RUN <<EOX
|
|
|
192
245
|
go env -w GOPROXY=https://mirrors.tencent.com/go
|
|
193
246
|
|
|
194
247
|
# 安装 LSP服务(go)
|
|
195
|
-
|
|
196
|
-
|
|
248
|
+
if [ -f /tmp/gopls-cache/gopls ] && [ ! -f /tmp/gopls-cache/.no-cache ]; then
|
|
249
|
+
# 使用缓存
|
|
250
|
+
cp /tmp/gopls-cache/gopls /usr/local/bin/gopls
|
|
251
|
+
chmod +x /usr/local/bin/gopls
|
|
252
|
+
else
|
|
253
|
+
# 下载编译
|
|
254
|
+
go install golang.org/x/tools/gopls@latest
|
|
255
|
+
ln -sf ~/go/bin/gopls /usr/local/bin/gopls
|
|
256
|
+
fi
|
|
257
|
+
rm -rf /tmp/gopls-cache
|
|
197
258
|
|
|
198
259
|
# 清理
|
|
199
260
|
apt-get clean
|
package/docs/README_EN.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
<< [English](README_EN.md) >> | [中文](../README.md)
|
|
2
2
|
|
|
3
3
|
---
|
|
4
4
|
|
|
@@ -49,15 +49,23 @@ After installing manyoyo, use the built-in command to build images:
|
|
|
49
49
|
# Pull base image
|
|
50
50
|
podman pull ubuntu:24.04
|
|
51
51
|
|
|
52
|
-
# Build using manyoyo (Recommended)
|
|
53
|
-
manyoyo --ib all # Build all version (
|
|
54
|
-
manyoyo --ib common # Build common version
|
|
55
|
-
manyoyo --ib go,codex,java,gemini # Build
|
|
52
|
+
# Build using manyoyo (Recommended, auto-cache enabled)
|
|
53
|
+
manyoyo --ib all # Build all version (auto-cache, blazing fast after first build within 2 days)
|
|
54
|
+
manyoyo --ib common # Build common version
|
|
55
|
+
manyoyo --ib go,codex,java,gemini # Build custom combination
|
|
56
|
+
|
|
57
|
+
# How it works:
|
|
58
|
+
# - First build: Auto-downloads Node.js, JDT LSP, gopls etc. to docker/cache/
|
|
59
|
+
# - Rebuild within 2 days: Uses local cache, ~5x faster
|
|
60
|
+
# - After cache expires: Auto-downloads latest versions
|
|
56
61
|
|
|
57
62
|
# Custom image name and version
|
|
58
63
|
manyoyo --ib all --in myimage --iv 2.0.0
|
|
59
64
|
# Builds: myimage:2.0.0-all
|
|
60
65
|
|
|
66
|
+
# Clean images
|
|
67
|
+
manyoyo --ip # Clean dangling images and <none> images
|
|
68
|
+
|
|
61
69
|
# Or build manually (Not recommended)
|
|
62
70
|
iv=1.4.0 && podman build -t localhost/xcanwin/manyoyo:$iv-all -f docker/manyoyo.Dockerfile . --build-arg EXT=all --no-cache
|
|
63
71
|
podman image prune -f
|
|
@@ -207,7 +215,8 @@ docker ps -a
|
|
|
207
215
|
| `-x CMD` | `--sf`, `--shell-full` | Full command (replaces --sp, -s, and --) |
|
|
208
216
|
| `-y CLI` | `--yolo` | Run AI agent without confirmation |
|
|
209
217
|
| `-m MODE` | `--cm`, `--cont-mode` | Set container mode (common, dind, mdsock) |
|
|
210
|
-
| `--ib EXT` | `--image-build` | Build image, EXT is image variant
|
|
218
|
+
| `--ib EXT` | `--image-build` | Build image with auto-cache, EXT is image variant |
|
|
219
|
+
| `--ip` | `--image-prune` | Clean dangling images and `<none>` images |
|
|
211
220
|
| `--install NAME` | | Install manyoyo command |
|
|
212
221
|
| `-V` | `--version` | Show version |
|
|
213
222
|
| `-h` | `--help` | Show help |
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xcanwin/manyoyo",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"imageVersion": "1.
|
|
3
|
+
"version": "3.4.0",
|
|
4
|
+
"imageVersion": "1.6.0",
|
|
5
5
|
"description": "AI Agent CLI Security Sandbox",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"ai", "agent", "sandbox", "docker", "cli", "container", "development"
|