@prover-coder-ai/docker-git 1.0.19 → 1.0.21
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/.package.json.release.bak +1 -1
- package/CHANGELOG.md +12 -0
- package/dist/main.js +62 -36
- package/dist/main.js.map +1 -1
- package/dist/src/docker-git/cli/parser-apply.js +22 -0
- package/dist/src/docker-git/cli/parser-clone.js +3 -4
- package/dist/src/docker-git/cli/parser-options.js +44 -19
- package/dist/src/docker-git/cli/parser.js +2 -1
- package/dist/src/docker-git/cli/usage.js +6 -1
- package/dist/src/docker-git/program.js +2 -1
- package/package.json +1 -1
- package/src/docker-git/cli/parser-apply.ts +28 -0
- package/src/docker-git/cli/parser-clone.ts +3 -9
- package/src/docker-git/cli/parser-options.ts +65 -22
- package/src/docker-git/cli/parser.ts +2 -0
- package/src/docker-git/cli/usage.ts +6 -1
- package/src/docker-git/program.ts +2 -0
- package/tests/docker-git/entrypoint-auth.test.ts +14 -3
- package/tests/docker-git/parser.test.ts +89 -17
package/CHANGELOG.md
CHANGED
package/dist/main.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NodeContext, NodeRuntime } from "@effect/platform-node";
|
|
2
|
-
import { Match, Effect, pipe,
|
|
2
|
+
import { Data, Match, Effect, pipe, Either, Duration, Console } from "effect";
|
|
3
3
|
import * as CommandExecutor from "@effect/platform/CommandExecutor";
|
|
4
4
|
import { ExitCode } from "@effect/platform/CommandExecutor";
|
|
5
5
|
import * as Path from "@effect/platform/Path";
|
|
@@ -28,6 +28,36 @@ const resolveCloneRequest = (argv, npmLifecycleEvent) => {
|
|
|
28
28
|
}
|
|
29
29
|
return emptyRequest;
|
|
30
30
|
};
|
|
31
|
+
class FileExistsError extends Data.TaggedError("FileExistsError") {
|
|
32
|
+
}
|
|
33
|
+
class ConfigNotFoundError extends Data.TaggedError("ConfigNotFoundError") {
|
|
34
|
+
}
|
|
35
|
+
class ConfigDecodeError extends Data.TaggedError("ConfigDecodeError") {
|
|
36
|
+
}
|
|
37
|
+
class InputCancelledError extends Data.TaggedError("InputCancelledError") {
|
|
38
|
+
}
|
|
39
|
+
class InputReadError extends Data.TaggedError("InputReadError") {
|
|
40
|
+
}
|
|
41
|
+
class DockerCommandError extends Data.TaggedError("DockerCommandError") {
|
|
42
|
+
}
|
|
43
|
+
class DockerAccessError extends Data.TaggedError("DockerAccessError") {
|
|
44
|
+
}
|
|
45
|
+
class CloneFailedError extends Data.TaggedError("CloneFailedError") {
|
|
46
|
+
}
|
|
47
|
+
class PortProbeError extends Data.TaggedError("PortProbeError") {
|
|
48
|
+
}
|
|
49
|
+
class CommandFailedError extends Data.TaggedError("CommandFailedError") {
|
|
50
|
+
}
|
|
51
|
+
class AuthError extends Data.TaggedError("AuthError") {
|
|
52
|
+
}
|
|
53
|
+
class ScrapArchiveNotFoundError extends Data.TaggedError("ScrapArchiveNotFoundError") {
|
|
54
|
+
}
|
|
55
|
+
class ScrapArchiveInvalidError extends Data.TaggedError("ScrapArchiveInvalidError") {
|
|
56
|
+
}
|
|
57
|
+
class ScrapTargetDirUnsupportedError extends Data.TaggedError("ScrapTargetDirUnsupportedError") {
|
|
58
|
+
}
|
|
59
|
+
class ScrapWipeRefusedError extends Data.TaggedError("ScrapWipeRefusedError") {
|
|
60
|
+
}
|
|
31
61
|
const trimLeftChar = (value, char) => {
|
|
32
62
|
let start = 0;
|
|
33
63
|
while (start < value.length && value[start] === char) {
|
|
@@ -160,36 +190,6 @@ const runCommandCapture = (spec, okExitCodes, onFailure) => Effect.scoped(Effect
|
|
|
160
190
|
yield* _(ensureExitCode(Number(exitCode), okExitCodes, onFailure));
|
|
161
191
|
return new TextDecoder("utf-8").decode(bytes);
|
|
162
192
|
}));
|
|
163
|
-
class FileExistsError extends Data.TaggedError("FileExistsError") {
|
|
164
|
-
}
|
|
165
|
-
class ConfigNotFoundError extends Data.TaggedError("ConfigNotFoundError") {
|
|
166
|
-
}
|
|
167
|
-
class ConfigDecodeError extends Data.TaggedError("ConfigDecodeError") {
|
|
168
|
-
}
|
|
169
|
-
class InputCancelledError extends Data.TaggedError("InputCancelledError") {
|
|
170
|
-
}
|
|
171
|
-
class InputReadError extends Data.TaggedError("InputReadError") {
|
|
172
|
-
}
|
|
173
|
-
class DockerCommandError extends Data.TaggedError("DockerCommandError") {
|
|
174
|
-
}
|
|
175
|
-
class DockerAccessError extends Data.TaggedError("DockerAccessError") {
|
|
176
|
-
}
|
|
177
|
-
class CloneFailedError extends Data.TaggedError("CloneFailedError") {
|
|
178
|
-
}
|
|
179
|
-
class PortProbeError extends Data.TaggedError("PortProbeError") {
|
|
180
|
-
}
|
|
181
|
-
class CommandFailedError extends Data.TaggedError("CommandFailedError") {
|
|
182
|
-
}
|
|
183
|
-
class AuthError extends Data.TaggedError("AuthError") {
|
|
184
|
-
}
|
|
185
|
-
class ScrapArchiveNotFoundError extends Data.TaggedError("ScrapArchiveNotFoundError") {
|
|
186
|
-
}
|
|
187
|
-
class ScrapArchiveInvalidError extends Data.TaggedError("ScrapArchiveInvalidError") {
|
|
188
|
-
}
|
|
189
|
-
class ScrapTargetDirUnsupportedError extends Data.TaggedError("ScrapTargetDirUnsupportedError") {
|
|
190
|
-
}
|
|
191
|
-
class ScrapWipeRefusedError extends Data.TaggedError("ScrapWipeRefusedError") {
|
|
192
|
-
}
|
|
193
193
|
const successExitCode$1 = Number(ExitCode(0));
|
|
194
194
|
const readCloneRequest = Effect.sync(() => resolveCloneRequest(process.argv.slice(2), process.env["npm_lifecycle_event"]));
|
|
195
195
|
const runDockerGitClone = (args) => Effect.gen(function* (_) {
|
|
@@ -215,6 +215,9 @@ const TemplateConfigSchema = Schema.Struct({
|
|
|
215
215
|
sshPort: Schema.Number.pipe(Schema.int()),
|
|
216
216
|
repoUrl: Schema.String,
|
|
217
217
|
repoRef: Schema.String,
|
|
218
|
+
gitTokenLabel: Schema.optional(Schema.String),
|
|
219
|
+
codexAuthLabel: Schema.optional(Schema.String),
|
|
220
|
+
claudeAuthLabel: Schema.optional(Schema.String),
|
|
218
221
|
targetDir: Schema.String,
|
|
219
222
|
volumeName: Schema.String,
|
|
220
223
|
dockerGitPath: Schema.optionalWith(Schema.String, {
|
|
@@ -275,19 +278,38 @@ const runDockerPsNames = (cwd) => pipe(runCommandCapture({
|
|
|
275
278
|
}, [Number(ExitCode(0))], (exitCode) => new CommandFailedError({ command: "docker ps", exitCode })), Effect.map((output) => output.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0)));
|
|
276
279
|
const isParseError = (error) => error._tag === "UnknownCommand" || error._tag === "UnknownOption" || error._tag === "MissingOptionValue" || error._tag === "MissingRequiredOption" || error._tag === "InvalidOption" || error._tag === "UnexpectedArgument";
|
|
277
280
|
const renderDockerAccessHeadline = (issue) => issue === "PermissionDenied" ? "Cannot access Docker daemon socket: permission denied." : "Cannot connect to Docker daemon.";
|
|
281
|
+
const renderDockerAccessActionPlan = (issue) => {
|
|
282
|
+
const permissionDeniedPlan = [
|
|
283
|
+
"Action plan:",
|
|
284
|
+
"1) In the same shell, run: `groups $USER` and make sure group `docker` is present.",
|
|
285
|
+
"2) Re-login to refresh group memberships and run command again.",
|
|
286
|
+
"3) If DOCKER_HOST is set to rootless socket, keep running: `export DOCKER_HOST=unix:///run/user/$UID/docker.sock`.",
|
|
287
|
+
"4) If using a dedicated socket not in /run/user, set DOCKER_HOST explicitly and re-run.",
|
|
288
|
+
"Tip: this app now auto-tries a rootless socket fallback on first permission error."
|
|
289
|
+
];
|
|
290
|
+
const daemonUnavailablePlan = [
|
|
291
|
+
"Action plan:",
|
|
292
|
+
"1) Check daemon status: `systemctl --user status docker` or `systemctl status docker`.",
|
|
293
|
+
"2) Start daemon: `systemctl --user start docker` (or `systemctl start docker` for system Docker).",
|
|
294
|
+
"3) Retry command in a new shell."
|
|
295
|
+
];
|
|
296
|
+
return issue === "PermissionDenied" ? permissionDeniedPlan.join("\n") : daemonUnavailablePlan.join("\n");
|
|
297
|
+
};
|
|
278
298
|
const renderPrimaryError = (error) => Match.value(error).pipe(Match.when({ _tag: "FileExistsError" }, ({ path }) => `File already exists: ${path} (use --force to overwrite)`), Match.when({ _tag: "DockerCommandError" }, ({ exitCode }) => [
|
|
279
299
|
`docker compose failed with exit code ${exitCode}`,
|
|
280
|
-
"Hint: ensure Docker daemon is running and current user can access /var/run/docker.sock (for example via the docker group)."
|
|
300
|
+
"Hint: ensure Docker daemon is running and current user can access /var/run/docker.sock (for example via the docker group).",
|
|
301
|
+
"Hint: if output above contains 'port is already allocated', retry with a free SSH port via --ssh-port <port> (for example --ssh-port 2235), or stop the conflicting project/container."
|
|
281
302
|
].join("\n")), Match.when({ _tag: "DockerAccessError" }, ({ details, issue }) => [
|
|
282
303
|
renderDockerAccessHeadline(issue),
|
|
283
304
|
"Hint: ensure Docker daemon is running and current user can access the docker socket.",
|
|
284
305
|
"Hint: if you use rootless Docker, set DOCKER_HOST to your user socket (for example unix:///run/user/$UID/docker.sock).",
|
|
306
|
+
renderDockerAccessActionPlan(issue),
|
|
285
307
|
`Details: ${details}`
|
|
286
308
|
].join("\n")), Match.when({ _tag: "CloneFailedError" }, ({ repoRef, repoUrl, targetDir }) => `Clone failed for ${repoUrl} (${repoRef}) into ${targetDir}`), Match.when({ _tag: "PortProbeError" }, ({ message, port }) => `SSH port check failed for ${port}: ${message}`), Match.when({ _tag: "CommandFailedError" }, ({ command, exitCode }) => `${command} failed with exit code ${exitCode}`), Match.when({ _tag: "ScrapArchiveNotFoundError" }, ({ path }) => `Scrap archive not found: ${path} (run docker-git scrap export first)`), Match.when({ _tag: "ScrapArchiveInvalidError" }, ({ message, path }) => `Invalid scrap archive: ${path}
|
|
287
|
-
Details: ${message}`), Match.when({ _tag: "ScrapTargetDirUnsupportedError" }, ({ reason,
|
|
309
|
+
Details: ${message}`), Match.when({ _tag: "ScrapTargetDirUnsupportedError" }, ({ reason, targetDir }) => [
|
|
288
310
|
`Cannot use scrap with targetDir ${targetDir}.`,
|
|
289
311
|
`Reason: ${reason}`,
|
|
290
|
-
`Hint: scrap currently supports workspaces under
|
|
312
|
+
`Hint: scrap currently supports workspaces under the ssh home directory only (for example: ~/repo).`
|
|
291
313
|
].join("\n")), Match.when({ _tag: "ScrapWipeRefusedError" }, ({ reason, targetDir }) => [
|
|
292
314
|
`Refusing to wipe workspace for scrap import (targetDir ${targetDir}).`,
|
|
293
315
|
`Reason: ${reason}`,
|
|
@@ -502,13 +524,17 @@ Env global: ${config.template.envGlobalPath}
|
|
|
502
524
|
Env project: ${config.template.envProjectPath}
|
|
503
525
|
Codex auth: ${config.template.codexAuthPath} -> ${config.template.codexHome}`;
|
|
504
526
|
const isDockerGitConfig = (entry) => entry.endsWith("docker-git.json");
|
|
505
|
-
const shouldSkipDir = (entry) => entry === ".git" || entry === ".orch" || entry === ".docker-git";
|
|
527
|
+
const shouldSkipDir = (entry) => entry === ".git" || entry === ".orch" || entry === ".docker-git" || entry === ".cache";
|
|
528
|
+
const isNotFoundStatError = (error) => error._tag === "SystemError" && error.reason === "NotFound";
|
|
506
529
|
const processDockerGitEntry = (fs, path, dir, entry, state) => Effect.gen(function* (_) {
|
|
507
530
|
if (shouldSkipDir(entry)) {
|
|
508
531
|
return;
|
|
509
532
|
}
|
|
510
533
|
const resolved = path.join(dir, entry);
|
|
511
|
-
const info = yield* _(fs.stat(resolved));
|
|
534
|
+
const info = yield* _(fs.stat(resolved).pipe(Effect.catchTag("SystemError", (error) => isNotFoundStatError(error) ? Effect.succeed(null) : Effect.fail(error))));
|
|
535
|
+
if (info === null) {
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
512
538
|
if (info.type === "Directory") {
|
|
513
539
|
state.stack.push(resolved);
|
|
514
540
|
return;
|