dsh-plugin-container 0.5.0 → 0.5.1
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 -1
- package/README.zh-CN.md +1 -1
- package/lib/client.js +3 -3
- package/package.json +1 -1
- package/src/client/build-image.jsx +6 -4
- package/src/host/apple.js +1 -1
- package/src/host/images.js +6 -3
- package/src/host/service.js +18 -2
package/README.md
CHANGED
package/README.zh-CN.md
CHANGED
package/lib/client.js
CHANGED
|
@@ -1284,7 +1284,7 @@ function BuildImageDialog({ open, t, api, provider, defaultDir = "", pickDirecto
|
|
|
1284
1284
|
if (id !== requestId.current) return;
|
|
1285
1285
|
setInfo(value);
|
|
1286
1286
|
setFile((current) => value.dockerfiles.includes(current) ? current : value.dockerfiles[0] ?? "");
|
|
1287
|
-
if (value.suggestedTag !== "") setTag(
|
|
1287
|
+
if (value.suggestedTag !== "" && !tagEdited) setTag(value.suggestedTag);
|
|
1288
1288
|
} catch {
|
|
1289
1289
|
if (id === requestId.current) setInfo(null);
|
|
1290
1290
|
}
|
|
@@ -1293,9 +1293,9 @@ function BuildImageDialog({ open, t, api, provider, defaultDir = "", pickDirecto
|
|
|
1293
1293
|
}, [api, dir, open, tagEdited]);
|
|
1294
1294
|
const args = (0, import_react4.useMemo)(() => buildArgs.split(/\n/u).map((line) => line.trim()).filter(Boolean), [buildArgs]);
|
|
1295
1295
|
const tagValid = IMAGE_REF.test(tag.trim());
|
|
1296
|
-
const dirOk = info !== null && info.isDirectory && info.dockerfiles.length > 0;
|
|
1297
1296
|
const trimmedDir = dir.trim();
|
|
1298
1297
|
const dirChecked = info !== null && info.path === trimmedDir;
|
|
1298
|
+
const dirOk = dirChecked && info.isDirectory && info.dockerfiles.length > 0;
|
|
1299
1299
|
let dirLine = null;
|
|
1300
1300
|
if (trimmedDir !== "" && !dirChecked) dirLine = { tone: "hint", text: t("imgBuildDirChecking") };
|
|
1301
1301
|
else if (dirChecked && !info.exists) dirLine = { tone: "err", text: t("imgBuildDirMissing") };
|
|
@@ -1356,7 +1356,7 @@ ${parts.join(" ")}`;
|
|
|
1356
1356
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Button, { size: "sm", variant: "primary", onClick: submit, disabled: busy || !dirOk || !tagValid, children: busy ? t("imgBuilding") : t("imgBuildStart") })
|
|
1357
1357
|
] }),
|
|
1358
1358
|
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("form", { className: "dsh-docker-run-form", onSubmit: submit, children: [
|
|
1359
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("
|
|
1359
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dsh-docker-field", children: [
|
|
1360
1360
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: t("imgBuildDir") }),
|
|
1361
1361
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dsh-docker-image-picker", children: [
|
|
1362
1362
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
package/package.json
CHANGED
|
@@ -61,7 +61,8 @@ export default function BuildImageDialog({ open, t, api, provider, defaultDir =
|
|
|
61
61
|
if (id !== requestId.current) return
|
|
62
62
|
setInfo(value)
|
|
63
63
|
setFile((current) => (value.dockerfiles.includes(current) ? current : (value.dockerfiles[0] ?? '')))
|
|
64
|
-
|
|
64
|
+
// Follow the folder until the user types their own tag.
|
|
65
|
+
if (value.suggestedTag !== '' && !tagEdited) setTag(value.suggestedTag)
|
|
65
66
|
} catch {
|
|
66
67
|
if (id === requestId.current) setInfo(null)
|
|
67
68
|
}
|
|
@@ -71,9 +72,9 @@ export default function BuildImageDialog({ open, t, api, provider, defaultDir =
|
|
|
71
72
|
|
|
72
73
|
const args = useMemo(() => buildArgs.split(/\n/u).map((line) => line.trim()).filter(Boolean), [buildArgs])
|
|
73
74
|
const tagValid = IMAGE_REF.test(tag.trim())
|
|
74
|
-
const dirOk = info !== null && info.isDirectory && info.dockerfiles.length > 0
|
|
75
75
|
const trimmedDir = dir.trim()
|
|
76
76
|
const dirChecked = info !== null && info.path === trimmedDir
|
|
77
|
+
const dirOk = dirChecked && info.isDirectory && info.dockerfiles.length > 0
|
|
77
78
|
|
|
78
79
|
let dirLine = null
|
|
79
80
|
if (trimmedDir !== '' && !dirChecked) dirLine = { tone: 'hint', text: t('imgBuildDirChecking') }
|
|
@@ -141,7 +142,8 @@ export default function BuildImageDialog({ open, t, api, provider, defaultDir =
|
|
|
141
142
|
}
|
|
142
143
|
>
|
|
143
144
|
<form className="dsh-docker-run-form" onSubmit={submit}>
|
|
144
|
-
|
|
145
|
+
{/* A div, not a label: clicking the caption should not open the folder picker. */}
|
|
146
|
+
<div className="dsh-docker-field">
|
|
145
147
|
<span>{t('imgBuildDir')}</span>
|
|
146
148
|
<div className="dsh-docker-image-picker">
|
|
147
149
|
<input
|
|
@@ -160,7 +162,7 @@ export default function BuildImageDialog({ open, t, api, provider, defaultDir =
|
|
|
160
162
|
{dirLine === null || dirLine.tone === 'hint'
|
|
161
163
|
? <span className="dsh-docker-hint">{dirLine?.text ?? t('imgBuildDirHint')}</span>
|
|
162
164
|
: <span className={dirLine.tone === 'ok' ? 'dsh-docker-line-ok' : 'dsh-docker-line-err'}>{dirLine.text}</span>}
|
|
163
|
-
</
|
|
165
|
+
</div>
|
|
164
166
|
{dirOk && info.dockerfiles.length > 1 && (
|
|
165
167
|
<label className="dsh-docker-field">
|
|
166
168
|
<span>{t('imgBuildFile')}</span>
|
package/src/host/apple.js
CHANGED
|
@@ -397,7 +397,7 @@ export class AppleProvider {
|
|
|
397
397
|
if (kind === 'image') {
|
|
398
398
|
if (action === 'pull') {
|
|
399
399
|
const platform = platformOf(payload.platform)
|
|
400
|
-
return this.run({ argv: ['image', 'pull', ...(platform === undefined ? [] : ['--platform', platform]), safeImageRef(payload.id ?? payload.reference)], cwd: payload.cwd, title: payload.title ?? 'container image pull' }, signal)
|
|
400
|
+
return this.run({ argv: ['image', 'pull', ...(platform === undefined ? [] : ['--platform', platform]), safeImageRef(payload.id ?? payload.reference)], cwd: payload.cwd, timeoutMs: LONG_JOB_MS, title: payload.title ?? 'container image pull' }, signal)
|
|
401
401
|
}
|
|
402
402
|
if (action === 'build') {
|
|
403
403
|
const spec = buildSpec(payload)
|
package/src/host/images.js
CHANGED
|
@@ -77,8 +77,10 @@ function statOrNull(value) {
|
|
|
77
77
|
export function buildSpec(payload) {
|
|
78
78
|
const tag = imageRef(payload.tag, 'tag')
|
|
79
79
|
assertLowercaseRepository(tag)
|
|
80
|
-
const
|
|
81
|
-
if (!isAbsolute(
|
|
80
|
+
const rawDir = requireString(payload.contextDir, 'contextDir', { max: 4096 })
|
|
81
|
+
if (!isAbsolute(rawDir) || rawDir.includes('\0')) throw new CommandError('构建目录必须是绝对路径')
|
|
82
|
+
// Normalise so `/a/b/` and `/a/b/./` compare equal to the resolved Dockerfile path below.
|
|
83
|
+
const contextDir = resolve(rawDir)
|
|
82
84
|
const dirStat = statOrNull(contextDir)
|
|
83
85
|
if (dirStat === null) throw new CommandError(`构建目录不存在: ${contextDir}`)
|
|
84
86
|
if (!dirStat.isDirectory()) throw new CommandError(`不是目录: ${contextDir}`)
|
|
@@ -87,7 +89,8 @@ export function buildSpec(payload) {
|
|
|
87
89
|
if (payload.file !== undefined && payload.file !== null && String(payload.file).trim() !== '') {
|
|
88
90
|
const name = requireString(String(payload.file).trim(), 'file', { max: 1024 })
|
|
89
91
|
const resolved = resolve(contextDir, name)
|
|
90
|
-
|
|
92
|
+
const prefix = contextDir.endsWith(sep) ? contextDir : `${contextDir}${sep}`
|
|
93
|
+
if (resolved !== contextDir && !resolved.startsWith(prefix)) throw new CommandError('Dockerfile 必须位于构建目录内')
|
|
91
94
|
const fileStat = statOrNull(resolved)
|
|
92
95
|
if (fileStat === null || !fileStat.isFile()) throw new CommandError(`找不到 Dockerfile: ${name}`)
|
|
93
96
|
file = resolved
|
package/src/host/service.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* records live in this plugin's memory and disappear on unload.
|
|
8
8
|
*/
|
|
9
9
|
import { homedir } from 'node:os'
|
|
10
|
-
import { statSync } from 'node:fs'
|
|
10
|
+
import { readFileSync, statSync } from 'node:fs'
|
|
11
11
|
|
|
12
12
|
import {
|
|
13
13
|
CommandError,
|
|
@@ -42,6 +42,15 @@ import {
|
|
|
42
42
|
import { createRuntimeResolver } from './runtime.js'
|
|
43
43
|
import { codeOf, isRecord, messageOf, optionalInteger, requireString } from './util.js'
|
|
44
44
|
|
|
45
|
+
/** Reported by `ping`; read once so it always matches the published package. */
|
|
46
|
+
const PLUGIN_VERSION = (() => {
|
|
47
|
+
try {
|
|
48
|
+
return String(JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8')).version)
|
|
49
|
+
} catch {
|
|
50
|
+
return 'unknown'
|
|
51
|
+
}
|
|
52
|
+
})()
|
|
53
|
+
|
|
45
54
|
const STATUS_CACHE_MS = 1500
|
|
46
55
|
const CONTEXT_CACHE_MS = 10_000
|
|
47
56
|
const VALUE_CACHE_MS = 1500
|
|
@@ -190,7 +199,7 @@ export class DockerService {
|
|
|
190
199
|
return ok({
|
|
191
200
|
ok: true,
|
|
192
201
|
plugin: 'dsh-plugin-container',
|
|
193
|
-
version:
|
|
202
|
+
version: PLUGIN_VERSION,
|
|
194
203
|
dockerPath: this.cli.dockerPath ?? null,
|
|
195
204
|
containerPath: this.apple.cli.containerPath ?? null,
|
|
196
205
|
})
|
|
@@ -776,6 +785,13 @@ export class DockerService {
|
|
|
776
785
|
if (action === 'pull') {
|
|
777
786
|
const platform = platformOf(payload.platform)
|
|
778
787
|
argv = ['pull', ...(platform === undefined ? [] : ['--platform', platform]), id]
|
|
788
|
+
return this.run({
|
|
789
|
+
argv,
|
|
790
|
+
context: payload.context,
|
|
791
|
+
cwd: payload.cwd,
|
|
792
|
+
timeoutMs: LONG_JOB_MS,
|
|
793
|
+
title: payload.title ?? `拉取镜像 ${id}`,
|
|
794
|
+
}, signal)
|
|
779
795
|
}
|
|
780
796
|
else if (action === 'delete') argv = ['rmi', id]
|
|
781
797
|
} else if (kind === 'volume' && action === 'delete') {
|