@prover-coder-ai/docker-git 1.0.20 → 1.0.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/.package.json.release.bak +4 -3
- package/CHANGELOG.md +12 -0
- package/README.md +28 -1
- package/dist/src/docker-git/main.js +10256 -12
- package/dist/src/docker-git/main.js.map +1 -0
- package/package.json +3 -4
- 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 +71 -22
- package/src/docker-git/cli/parser.ts +2 -0
- package/src/docker-git/cli/usage.ts +11 -3
- package/src/docker-git/menu-actions.ts +5 -2
- package/src/docker-git/menu-create.ts +9 -13
- package/src/docker-git/menu-render.ts +1 -1
- package/src/docker-git/program.ts +2 -0
- package/tests/docker-git/entrypoint-auth.test.ts +14 -3
- package/tests/docker-git/parser-network-options.test.ts +47 -0
- package/tests/docker-git/parser.test.ts +105 -18
- package/vite.docker-git.config.ts +34 -0
- package/dist/main.js +0 -905
- package/dist/main.js.map +0 -1
- package/dist/src/app/main.js +0 -15
- package/dist/src/app/program.js +0 -61
- package/dist/src/docker-git/cli/input.js +0 -21
- package/dist/src/docker-git/cli/parser-attach.js +0 -19
- package/dist/src/docker-git/cli/parser-auth.js +0 -90
- package/dist/src/docker-git/cli/parser-clone.js +0 -41
- package/dist/src/docker-git/cli/parser-create.js +0 -1
- package/dist/src/docker-git/cli/parser-mcp-playwright.js +0 -18
- package/dist/src/docker-git/cli/parser-options.js +0 -109
- package/dist/src/docker-git/cli/parser-panes.js +0 -19
- package/dist/src/docker-git/cli/parser-scrap.js +0 -74
- package/dist/src/docker-git/cli/parser-sessions.js +0 -69
- package/dist/src/docker-git/cli/parser-shared.js +0 -26
- package/dist/src/docker-git/cli/parser-state.js +0 -62
- package/dist/src/docker-git/cli/parser.js +0 -46
- package/dist/src/docker-git/cli/read-command.js +0 -17
- package/dist/src/docker-git/cli/usage.js +0 -108
- package/dist/src/docker-git/menu-actions.js +0 -135
- package/dist/src/docker-git/menu-auth-data.js +0 -90
- package/dist/src/docker-git/menu-auth-helpers.js +0 -20
- package/dist/src/docker-git/menu-auth.js +0 -159
- package/dist/src/docker-git/menu-buffer-input.js +0 -9
- package/dist/src/docker-git/menu-create.js +0 -199
- package/dist/src/docker-git/menu-input-handler.js +0 -109
- package/dist/src/docker-git/menu-input-utils.js +0 -47
- package/dist/src/docker-git/menu-input.js +0 -2
- package/dist/src/docker-git/menu-labeled-env.js +0 -33
- package/dist/src/docker-git/menu-menu.js +0 -46
- package/dist/src/docker-git/menu-project-auth-claude.js +0 -43
- package/dist/src/docker-git/menu-project-auth-data.js +0 -165
- package/dist/src/docker-git/menu-project-auth.js +0 -124
- package/dist/src/docker-git/menu-render-auth.js +0 -45
- package/dist/src/docker-git/menu-render-common.js +0 -26
- package/dist/src/docker-git/menu-render-layout.js +0 -14
- package/dist/src/docker-git/menu-render-project-auth.js +0 -37
- package/dist/src/docker-git/menu-render-select.js +0 -129
- package/dist/src/docker-git/menu-render.js +0 -137
- package/dist/src/docker-git/menu-select-actions.js +0 -66
- package/dist/src/docker-git/menu-select-connect.js +0 -6
- package/dist/src/docker-git/menu-select-load.js +0 -12
- package/dist/src/docker-git/menu-select-order.js +0 -21
- package/dist/src/docker-git/menu-select-runtime.js +0 -82
- package/dist/src/docker-git/menu-select-view.js +0 -15
- package/dist/src/docker-git/menu-select.js +0 -98
- package/dist/src/docker-git/menu-shared.js +0 -180
- package/dist/src/docker-git/menu-startup.js +0 -57
- package/dist/src/docker-git/menu-types.js +0 -21
- package/dist/src/docker-git/menu.js +0 -226
- package/dist/src/docker-git/program.js +0 -42
- package/dist/src/docker-git/tmux.js +0 -176
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prover-coder-ai/docker-git",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.22",
|
|
4
4
|
"description": "Minimal Vite-powered TypeScript console starter using Effect",
|
|
5
5
|
"main": "dist/src/docker-git/main.js",
|
|
6
6
|
"bin": {
|
|
@@ -35,8 +35,7 @@
|
|
|
35
35
|
"@effect/schema": "^0.75.5",
|
|
36
36
|
"effect": "^3.19.14",
|
|
37
37
|
"ink": "^5.0.1",
|
|
38
|
-
"react": "^18.3.1"
|
|
39
|
-
"@effect-template/lib": "1.0.0"
|
|
38
|
+
"react": "^18.3.1"
|
|
40
39
|
},
|
|
41
40
|
"scripts": {
|
|
42
41
|
"prebuild": "pnpm -C ../lib build",
|
|
@@ -48,7 +47,7 @@
|
|
|
48
47
|
"lint:tests": "PATH=../../scripts:$PATH vibecode-linter tests/",
|
|
49
48
|
"lint:effect": "PATH=../../scripts:$PATH eslint --config eslint.effect-ts-check.config.mjs .",
|
|
50
49
|
"prebuild:docker-git": "pnpm -C ../lib build",
|
|
51
|
-
"build:docker-git": "
|
|
50
|
+
"build:docker-git": "vite build --config vite.docker-git.config.ts",
|
|
52
51
|
"check": "pnpm run typecheck",
|
|
53
52
|
"clone": "pnpm -C ../.. run clone",
|
|
54
53
|
"docker-git": "node dist/src/docker-git/main.js",
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Either } from "effect"
|
|
2
|
+
|
|
3
|
+
import { type ApplyCommand, type ParseError } from "@effect-template/lib/core/domain"
|
|
4
|
+
|
|
5
|
+
import { parseProjectDirWithOptions } from "./parser-shared.js"
|
|
6
|
+
|
|
7
|
+
// CHANGE: parse "apply" command for existing docker-git projects
|
|
8
|
+
// WHY: update managed docker-git config on the current project/container without creating a new project
|
|
9
|
+
// QUOTE(ТЗ): "Не создавать новый... а прямо в текущем обновить её на актуальную"
|
|
10
|
+
// REF: issue-72-followup-apply-current-config
|
|
11
|
+
// SOURCE: n/a
|
|
12
|
+
// FORMAT THEOREM: forall argv: parseApply(argv) = cmd -> deterministic(cmd)
|
|
13
|
+
// PURITY: CORE
|
|
14
|
+
// EFFECT: Effect<ApplyCommand, ParseError, never>
|
|
15
|
+
// INVARIANT: projectDir is never empty
|
|
16
|
+
// COMPLEXITY: O(n) where n = |argv|
|
|
17
|
+
export const parseApply = (
|
|
18
|
+
args: ReadonlyArray<string>
|
|
19
|
+
): Either.Either<ApplyCommand, ParseError> =>
|
|
20
|
+
Either.map(parseProjectDirWithOptions(args), ({ projectDir, raw }) => ({
|
|
21
|
+
_tag: "Apply",
|
|
22
|
+
projectDir,
|
|
23
|
+
runUp: raw.up ?? true,
|
|
24
|
+
gitTokenLabel: raw.gitTokenLabel,
|
|
25
|
+
codexTokenLabel: raw.codexTokenLabel,
|
|
26
|
+
claudeTokenLabel: raw.claudeTokenLabel,
|
|
27
|
+
enableMcpPlaywright: raw.enableMcpPlaywright
|
|
28
|
+
}))
|
|
@@ -2,12 +2,7 @@ import { Either } from "effect"
|
|
|
2
2
|
|
|
3
3
|
import { buildCreateCommand, nonEmpty } from "@effect-template/lib/core/command-builders"
|
|
4
4
|
import type { RawOptions } from "@effect-template/lib/core/command-options"
|
|
5
|
-
import {
|
|
6
|
-
type Command,
|
|
7
|
-
defaultTemplateConfig,
|
|
8
|
-
type ParseError,
|
|
9
|
-
resolveRepoInput
|
|
10
|
-
} from "@effect-template/lib/core/domain"
|
|
5
|
+
import { type Command, type ParseError, resolveRepoInput } from "@effect-template/lib/core/domain"
|
|
11
6
|
|
|
12
7
|
import { parseRawOptions } from "./parser-options.js"
|
|
13
8
|
import { resolveWorkspaceRepoPath, splitPositionalRepo } from "./parser-shared.js"
|
|
@@ -18,13 +13,12 @@ const applyCloneDefaults = (
|
|
|
18
13
|
resolvedRepo: ReturnType<typeof resolveRepoInput>
|
|
19
14
|
): RawOptions => {
|
|
20
15
|
const repoPath = resolveWorkspaceRepoPath(resolvedRepo)
|
|
21
|
-
const
|
|
22
|
-
const homeDir = `/home/${sshUser}`
|
|
16
|
+
const targetHome = "~"
|
|
23
17
|
return {
|
|
24
18
|
...raw,
|
|
25
19
|
repoUrl: rawRepoUrl,
|
|
26
20
|
outDir: raw.outDir ?? `.docker-git/${repoPath}`,
|
|
27
|
-
targetDir: raw.targetDir ?? `${
|
|
21
|
+
targetDir: raw.targetDir ?? `${targetHome}/workspaces/${repoPath}`
|
|
28
22
|
}
|
|
29
23
|
}
|
|
30
24
|
|
|
@@ -20,9 +20,14 @@ interface ValueOptionSpec {
|
|
|
20
20
|
| "envProjectPath"
|
|
21
21
|
| "codexAuthPath"
|
|
22
22
|
| "codexHome"
|
|
23
|
+
| "dockerNetworkMode"
|
|
24
|
+
| "dockerSharedNetworkName"
|
|
23
25
|
| "archivePath"
|
|
24
26
|
| "scrapMode"
|
|
25
27
|
| "label"
|
|
28
|
+
| "gitTokenLabel"
|
|
29
|
+
| "codexTokenLabel"
|
|
30
|
+
| "claudeTokenLabel"
|
|
26
31
|
| "token"
|
|
27
32
|
| "scopes"
|
|
28
33
|
| "message"
|
|
@@ -48,9 +53,14 @@ const valueOptionSpecs: ReadonlyArray<ValueOptionSpec> = [
|
|
|
48
53
|
{ flag: "--env-project", key: "envProjectPath" },
|
|
49
54
|
{ flag: "--codex-auth", key: "codexAuthPath" },
|
|
50
55
|
{ flag: "--codex-home", key: "codexHome" },
|
|
56
|
+
{ flag: "--network-mode", key: "dockerNetworkMode" },
|
|
57
|
+
{ flag: "--shared-network", key: "dockerSharedNetworkName" },
|
|
51
58
|
{ flag: "--archive", key: "archivePath" },
|
|
52
59
|
{ flag: "--mode", key: "scrapMode" },
|
|
53
60
|
{ flag: "--label", key: "label" },
|
|
61
|
+
{ flag: "--git-token", key: "gitTokenLabel" },
|
|
62
|
+
{ flag: "--codex-token", key: "codexTokenLabel" },
|
|
63
|
+
{ flag: "--claude-token", key: "claudeTokenLabel" },
|
|
54
64
|
{ flag: "--token", key: "token" },
|
|
55
65
|
{ flag: "--scopes", key: "scopes" },
|
|
56
66
|
{ flag: "--message", key: "message" },
|
|
@@ -96,9 +106,14 @@ const valueFlagUpdaters: { readonly [K in ValueKey]: (raw: RawOptions, value: st
|
|
|
96
106
|
envProjectPath: (raw, value) => ({ ...raw, envProjectPath: value }),
|
|
97
107
|
codexAuthPath: (raw, value) => ({ ...raw, codexAuthPath: value }),
|
|
98
108
|
codexHome: (raw, value) => ({ ...raw, codexHome: value }),
|
|
109
|
+
dockerNetworkMode: (raw, value) => ({ ...raw, dockerNetworkMode: value }),
|
|
110
|
+
dockerSharedNetworkName: (raw, value) => ({ ...raw, dockerSharedNetworkName: value }),
|
|
99
111
|
archivePath: (raw, value) => ({ ...raw, archivePath: value }),
|
|
100
112
|
scrapMode: (raw, value) => ({ ...raw, scrapMode: value }),
|
|
101
113
|
label: (raw, value) => ({ ...raw, label: value }),
|
|
114
|
+
gitTokenLabel: (raw, value) => ({ ...raw, gitTokenLabel: value }),
|
|
115
|
+
codexTokenLabel: (raw, value) => ({ ...raw, codexTokenLabel: value }),
|
|
116
|
+
claudeTokenLabel: (raw, value) => ({ ...raw, claudeTokenLabel: value }),
|
|
102
117
|
token: (raw, value) => ({ ...raw, token: value }),
|
|
103
118
|
scopes: (raw, value) => ({ ...raw, scopes: value }),
|
|
104
119
|
message: (raw, value) => ({ ...raw, message: value }),
|
|
@@ -126,34 +141,68 @@ export const applyCommandValueFlag = (
|
|
|
126
141
|
return Either.right(update(raw, value))
|
|
127
142
|
}
|
|
128
143
|
|
|
144
|
+
type ParseRawOptionsStep =
|
|
145
|
+
| { readonly _tag: "ok"; readonly raw: RawOptions; readonly nextIndex: number }
|
|
146
|
+
| { readonly _tag: "error"; readonly error: ParseError }
|
|
147
|
+
|
|
148
|
+
const parseInlineValueToken = (
|
|
149
|
+
raw: RawOptions,
|
|
150
|
+
token: string
|
|
151
|
+
): Either.Either<RawOptions, ParseError> | null => {
|
|
152
|
+
const equalIndex = token.indexOf("=")
|
|
153
|
+
if (equalIndex <= 0 || !token.startsWith("-")) {
|
|
154
|
+
return null
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const flag = token.slice(0, equalIndex)
|
|
158
|
+
const inlineValue = token.slice(equalIndex + 1)
|
|
159
|
+
return applyCommandValueFlag(raw, flag, inlineValue)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const parseRawOptionsStep = (
|
|
163
|
+
args: ReadonlyArray<string>,
|
|
164
|
+
index: number,
|
|
165
|
+
raw: RawOptions
|
|
166
|
+
): ParseRawOptionsStep => {
|
|
167
|
+
const token = args[index] ?? ""
|
|
168
|
+
const inlineApplied = parseInlineValueToken(raw, token)
|
|
169
|
+
if (inlineApplied !== null) {
|
|
170
|
+
return Either.isLeft(inlineApplied)
|
|
171
|
+
? { _tag: "error", error: inlineApplied.left }
|
|
172
|
+
: { _tag: "ok", raw: inlineApplied.right, nextIndex: index + 1 }
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const booleanApplied = applyCommandBooleanFlag(raw, token)
|
|
176
|
+
if (booleanApplied !== null) {
|
|
177
|
+
return { _tag: "ok", raw: booleanApplied, nextIndex: index + 1 }
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (!token.startsWith("-")) {
|
|
181
|
+
return { _tag: "error", error: { _tag: "UnexpectedArgument", value: token } }
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const value = args[index + 1]
|
|
185
|
+
if (value === undefined) {
|
|
186
|
+
return { _tag: "error", error: { _tag: "MissingOptionValue", option: token } }
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const nextRaw = applyCommandValueFlag(raw, token, value)
|
|
190
|
+
return Either.isLeft(nextRaw)
|
|
191
|
+
? { _tag: "error", error: nextRaw.left }
|
|
192
|
+
: { _tag: "ok", raw: nextRaw.right, nextIndex: index + 2 }
|
|
193
|
+
}
|
|
194
|
+
|
|
129
195
|
export const parseRawOptions = (args: ReadonlyArray<string>): Either.Either<RawOptions, ParseError> => {
|
|
130
196
|
let index = 0
|
|
131
197
|
let raw: RawOptions = {}
|
|
132
198
|
|
|
133
199
|
while (index < args.length) {
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
raw = booleanApplied
|
|
138
|
-
index += 1
|
|
139
|
-
continue
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (!token.startsWith("-")) {
|
|
143
|
-
return Either.left({ _tag: "UnexpectedArgument", value: token })
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const value = args[index + 1]
|
|
147
|
-
if (value === undefined) {
|
|
148
|
-
return Either.left({ _tag: "MissingOptionValue", option: token })
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const nextRaw = applyCommandValueFlag(raw, token, value)
|
|
152
|
-
if (Either.isLeft(nextRaw)) {
|
|
153
|
-
return Either.left(nextRaw.left)
|
|
200
|
+
const step = parseRawOptionsStep(args, index, raw)
|
|
201
|
+
if (step._tag === "error") {
|
|
202
|
+
return Either.left(step.error)
|
|
154
203
|
}
|
|
155
|
-
raw =
|
|
156
|
-
index
|
|
204
|
+
raw = step.raw
|
|
205
|
+
index = step.nextIndex
|
|
157
206
|
}
|
|
158
207
|
|
|
159
208
|
return Either.right(raw)
|
|
@@ -2,6 +2,7 @@ import { Either, Match } from "effect"
|
|
|
2
2
|
|
|
3
3
|
import { type Command, type ParseError } from "@effect-template/lib/core/domain"
|
|
4
4
|
|
|
5
|
+
import { parseApply } from "./parser-apply.js"
|
|
5
6
|
import { parseAttach } from "./parser-attach.js"
|
|
6
7
|
import { parseAuth } from "./parser-auth.js"
|
|
7
8
|
import { parseClone } from "./parser-clone.js"
|
|
@@ -74,6 +75,7 @@ export const parseArgs = (args: ReadonlyArray<string>): Either.Either<Command, P
|
|
|
74
75
|
Match.when("auth", () => parseAuth(rest))
|
|
75
76
|
)
|
|
76
77
|
.pipe(
|
|
78
|
+
Match.when("apply", () => parseApply(rest)),
|
|
77
79
|
Match.when("state", () => parseState(rest)),
|
|
78
80
|
Match.orElse(() => Either.left(unknownCommandError))
|
|
79
81
|
)
|
|
@@ -3,8 +3,9 @@ import { Match } from "effect"
|
|
|
3
3
|
import type { ParseError } from "@effect-template/lib/core/domain"
|
|
4
4
|
|
|
5
5
|
export const usageText = `docker-git menu
|
|
6
|
-
docker-git create --repo-url <url> [options]
|
|
6
|
+
docker-git create [--repo-url <url>] [options]
|
|
7
7
|
docker-git clone <url> [options]
|
|
8
|
+
docker-git apply [<url>] [options]
|
|
8
9
|
docker-git mcp-playwright [<url>] [options]
|
|
9
10
|
docker-git attach [<url>] [options]
|
|
10
11
|
docker-git panes [<url>] [options]
|
|
@@ -19,8 +20,9 @@ docker-git state <action> [options]
|
|
|
19
20
|
|
|
20
21
|
Commands:
|
|
21
22
|
menu Interactive menu (default when no args)
|
|
22
|
-
create, init Generate docker development environment
|
|
23
|
+
create, init Generate docker development environment (repo URL optional)
|
|
23
24
|
clone Create + run container and clone repo
|
|
25
|
+
apply Apply docker-git config to an existing project/container (current dir by default)
|
|
24
26
|
mcp-playwright Enable Playwright MCP + Chromium sidecar for an existing project dir
|
|
25
27
|
attach, tmux Open tmux workspace for a docker-git project
|
|
26
28
|
panes, terms List tmux panes for a docker-git project
|
|
@@ -32,9 +34,10 @@ Commands:
|
|
|
32
34
|
state Manage docker-git state directory via git (sync across machines)
|
|
33
35
|
|
|
34
36
|
Options:
|
|
37
|
+
--repo-url <url> Repository URL (create: optional; clone: required via positional arg or flag)
|
|
35
38
|
--repo-ref <ref> Git ref/branch (default: main)
|
|
36
39
|
--branch, -b <ref> Alias for --repo-ref
|
|
37
|
-
--target-dir <path> Target dir inside container (create default: /home/dev/app, clone default:
|
|
40
|
+
--target-dir <path> Target dir inside container (create default: /home/dev/app, clone default: ~/workspaces/<org>/<repo>[/issue-<id>|/pr-<id>])
|
|
38
41
|
--ssh-port <port> Local SSH port (default: 2222)
|
|
39
42
|
--ssh-user <user> SSH user inside container (default: dev)
|
|
40
43
|
--container-name <name> Docker container name (default: dg-<repo>)
|
|
@@ -45,10 +48,15 @@ Options:
|
|
|
45
48
|
--env-project <path> Host path to project env file (default: ./.orch/env/project.env)
|
|
46
49
|
--codex-auth <path> Host path for Codex auth cache (default: <projectsRoot>/.orch/auth/codex)
|
|
47
50
|
--codex-home <path> Container path for Codex auth (default: /home/dev/.codex)
|
|
51
|
+
--network-mode <mode> Compose network mode: shared|project (default: shared)
|
|
52
|
+
--shared-network <name> Shared Docker network name when network-mode=shared (default: docker-git-shared)
|
|
48
53
|
--out-dir <path> Output directory (default: <projectsRoot>/<org>/<repo>[/issue-<id>|/pr-<id>])
|
|
49
54
|
--project-dir <path> Project directory for attach (default: .)
|
|
50
55
|
--archive <path> Scrap snapshot directory (default: .orch/scrap/session)
|
|
51
56
|
--mode <session> Scrap mode (default: session)
|
|
57
|
+
--git-token <label> Token label for clone/create (maps to GITHUB_TOKEN__<LABEL>, example: agiens)
|
|
58
|
+
--codex-token <label> Codex auth label for clone/create (maps to CODEX_AUTH_LABEL, example: agien)
|
|
59
|
+
--claude-token <label> Claude auth label for clone/create (maps to CLAUDE_AUTH_LABEL, example: agien)
|
|
52
60
|
--wipe | --no-wipe Wipe workspace before scrap import (default: --wipe)
|
|
53
61
|
--lines <n> Tail last N lines for sessions logs (default: 200)
|
|
54
62
|
--include-default Show default/system processes in sessions list
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
listProjectStatus,
|
|
10
10
|
listRunningProjectItems
|
|
11
11
|
} from "@effect-template/lib/usecases/projects"
|
|
12
|
+
import { gcProjectNetworkByTemplate } from "@effect-template/lib/usecases/docker-network-gc"
|
|
12
13
|
import { runDockerComposeUpWithPortCheck } from "@effect-template/lib/usecases/projects-up"
|
|
13
14
|
import { Effect, Match, pipe } from "effect"
|
|
14
15
|
|
|
@@ -149,8 +150,10 @@ const handleMenuAction = (
|
|
|
149
150
|
withProjectConfig(state, setMessage, () =>
|
|
150
151
|
runDockerComposeLogs(state.activeDir ?? state.cwd))),
|
|
151
152
|
Match.when({ _tag: "Down" }, () =>
|
|
152
|
-
withProjectConfig(state, setMessage, () =>
|
|
153
|
-
runDockerComposeDown(state.activeDir ?? state.cwd)
|
|
153
|
+
withProjectConfig(state, setMessage, (config) =>
|
|
154
|
+
runDockerComposeDown(state.activeDir ?? state.cwd).pipe(
|
|
155
|
+
Effect.zipRight(gcProjectNetworkByTemplate(state.activeDir ?? state.cwd, config.template))
|
|
156
|
+
))),
|
|
154
157
|
Match.when({ _tag: "DownAll" }, () =>
|
|
155
158
|
pipe(
|
|
156
159
|
downAllDockerGitProjects,
|
|
@@ -46,11 +46,16 @@ type CreateReturnContext = CreateContext & {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
export const buildCreateArgs = (input: CreateInputs): ReadonlyArray<string> => {
|
|
49
|
-
const args: Array<string> = ["create"
|
|
49
|
+
const args: Array<string> = ["create"]
|
|
50
|
+
if (input.repoUrl.length > 0) {
|
|
51
|
+
args.push("--repo-url", input.repoUrl)
|
|
52
|
+
}
|
|
50
53
|
if (input.repoRef.length > 0) {
|
|
51
54
|
args.push("--repo-ref", input.repoRef)
|
|
52
55
|
}
|
|
53
|
-
|
|
56
|
+
if (input.outDir.length > 0) {
|
|
57
|
+
args.push("--out-dir", input.outDir)
|
|
58
|
+
}
|
|
54
59
|
if (!input.runUp) {
|
|
55
60
|
args.push("--no-up")
|
|
56
61
|
}
|
|
@@ -106,8 +111,8 @@ export const resolveCreateInputs = (
|
|
|
106
111
|
values: Partial<CreateInputs>
|
|
107
112
|
): CreateInputs => {
|
|
108
113
|
const repoUrl = values.repoUrl ?? ""
|
|
109
|
-
const resolvedRepoRef =
|
|
110
|
-
const outDir = values.outDir ??
|
|
114
|
+
const resolvedRepoRef = resolveRepoInput(repoUrl).repoRef
|
|
115
|
+
const outDir = values.outDir ?? resolveDefaultOutDir(cwd, repoUrl)
|
|
111
116
|
|
|
112
117
|
return {
|
|
113
118
|
repoUrl,
|
|
@@ -179,10 +184,6 @@ const applyCreateStep = (input: {
|
|
|
179
184
|
}): boolean =>
|
|
180
185
|
Match.value(input.step).pipe(
|
|
181
186
|
Match.when("repoUrl", () => {
|
|
182
|
-
if (input.buffer.length === 0) {
|
|
183
|
-
input.setMessage("Repo URL is required.")
|
|
184
|
-
return false
|
|
185
|
-
}
|
|
186
187
|
input.nextValues.repoUrl = input.buffer
|
|
187
188
|
input.nextValues.outDir = resolveDefaultOutDir(input.cwd, input.buffer)
|
|
188
189
|
return true
|
|
@@ -222,11 +223,6 @@ const finalizeCreateFlow = (input: {
|
|
|
222
223
|
readonly setActiveDir: (dir: string | null) => void
|
|
223
224
|
}) => {
|
|
224
225
|
const inputs = resolveCreateInputs(input.state.cwd, input.nextValues)
|
|
225
|
-
if (inputs.repoUrl.length === 0) {
|
|
226
|
-
input.setMessage("Repo URL is required.")
|
|
227
|
-
return
|
|
228
|
-
}
|
|
229
|
-
|
|
230
226
|
const parsed = parseArgs(buildCreateArgs(inputs))
|
|
231
227
|
if (Either.isLeft(parsed)) {
|
|
232
228
|
input.setMessage(formatParseError(parsed.left))
|
|
@@ -28,7 +28,7 @@ import { createSteps, menuItems } from "./menu-types.js"
|
|
|
28
28
|
|
|
29
29
|
export const renderStepLabel = (step: CreateStep, defaults: CreateInputs): string =>
|
|
30
30
|
Match.value(step).pipe(
|
|
31
|
-
Match.when("repoUrl", () => "Repo URL"),
|
|
31
|
+
Match.when("repoUrl", () => "Repo URL (optional for empty workspace)"),
|
|
32
32
|
Match.when("repoRef", () => `Repo ref [${defaults.repoRef}]`),
|
|
33
33
|
Match.when("outDir", () => `Output dir [${defaults.outDir}]`),
|
|
34
34
|
Match.when("runUp", () => `Run docker compose up now? [${defaults.runUp ? "Y" : "n"}]`),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Command, ParseError } from "@effect-template/lib/core/domain"
|
|
2
2
|
import { createProject } from "@effect-template/lib/usecases/actions"
|
|
3
|
+
import { applyProjectConfig } from "@effect-template/lib/usecases/apply"
|
|
3
4
|
import {
|
|
4
5
|
authClaudeLogin,
|
|
5
6
|
authClaudeLogout,
|
|
@@ -97,6 +98,7 @@ const handleNonBaseCommand = (command: NonBaseCommand) =>
|
|
|
97
98
|
Match.when({ _tag: "SessionsKill" }, (cmd) => killTerminalProcess(cmd))
|
|
98
99
|
)
|
|
99
100
|
.pipe(
|
|
101
|
+
Match.when({ _tag: "Apply" }, (cmd) => applyProjectConfig(cmd)),
|
|
100
102
|
Match.when({ _tag: "SessionsLogs" }, (cmd) => tailTerminalLogs(cmd)),
|
|
101
103
|
Match.when({ _tag: "ScrapExport" }, (cmd) => exportScrap(cmd)),
|
|
102
104
|
Match.when({ _tag: "ScrapImport" }, (cmd) => importScrap(cmd)),
|
|
@@ -17,12 +17,23 @@ describe("renderEntrypoint auth bridge", () => {
|
|
|
17
17
|
"GIT_AUTH_TOKEN=\"${GIT_AUTH_TOKEN:-${GITHUB_TOKEN:-${GH_TOKEN:-}}}\""
|
|
18
18
|
)
|
|
19
19
|
expect(entrypoint).toContain("GITHUB_TOKEN=\"${GITHUB_TOKEN:-${GH_TOKEN:-}}\"")
|
|
20
|
-
expect(entrypoint).toContain("
|
|
20
|
+
expect(entrypoint).toContain("AUTH_LABEL_RAW=\"${GIT_AUTH_LABEL:-${GITHUB_AUTH_LABEL:-}}\"")
|
|
21
|
+
expect(entrypoint).toContain("LABELED_GITHUB_TOKEN_KEY=\"GITHUB_TOKEN__$RESOLVED_AUTH_LABEL\"")
|
|
22
|
+
expect(entrypoint).toContain("LABELED_GIT_TOKEN_KEY=\"GIT_AUTH_TOKEN__$RESOLVED_AUTH_LABEL\"")
|
|
23
|
+
expect(entrypoint).toContain("if [[ -n \"$EFFECTIVE_GH_TOKEN\" ]]; then")
|
|
21
24
|
expect(entrypoint).toContain(String.raw`printf "export GITHUB_TOKEN=%q\n" "$EFFECTIVE_GITHUB_TOKEN"`)
|
|
25
|
+
expect(entrypoint).toContain(String.raw`printf "export GH_TOKEN=%q\n" "$EFFECTIVE_GH_TOKEN"`)
|
|
26
|
+
expect(entrypoint).toContain(String.raw`printf "export GIT_AUTH_TOKEN=%q\n" "$EFFECTIVE_GITHUB_TOKEN"`)
|
|
22
27
|
expect(entrypoint).toContain("docker_git_upsert_ssh_env \"GITHUB_TOKEN\" \"$EFFECTIVE_GITHUB_TOKEN\"")
|
|
28
|
+
expect(entrypoint).toContain("docker_git_upsert_ssh_env \"GH_TOKEN\" \"$EFFECTIVE_GH_TOKEN\"")
|
|
29
|
+
expect(entrypoint).toContain("docker_git_upsert_ssh_env \"GIT_AUTH_TOKEN\" \"$EFFECTIVE_GITHUB_TOKEN\"")
|
|
23
30
|
expect(entrypoint).toContain("GIT_CREDENTIAL_HELPER_PATH=\"/usr/local/bin/docker-git-credential-helper\"")
|
|
24
|
-
expect(entrypoint).toContain("
|
|
25
|
-
expect(entrypoint).toContain("
|
|
31
|
+
expect(entrypoint).toContain("CLAUDE_REAL_BIN=\"/usr/local/bin/.docker-git-claude-real\"")
|
|
32
|
+
expect(entrypoint).toContain("CLAUDE_WRAPPER_BIN=\"/usr/local/bin/claude\"")
|
|
33
|
+
expect(entrypoint).toContain("cat <<'EOF' > \"$CLAUDE_WRAPPER_BIN\"")
|
|
34
|
+
expect(entrypoint).toContain("CLAUDE_CONFIG_DIR=\"${CLAUDE_CONFIG_DIR:-$HOME/.claude}\"")
|
|
35
|
+
expect(entrypoint).toContain("token=\"${GITHUB_TOKEN:-}\"")
|
|
36
|
+
expect(entrypoint).toContain("token=\"${GH_TOKEN:-}\"")
|
|
26
37
|
expect(entrypoint).toContain(String.raw`printf "%s\n" "password=$token"`)
|
|
27
38
|
expect(entrypoint).toContain("git config --global credential.helper")
|
|
28
39
|
}))
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { describe, expect, it } from "@effect/vitest"
|
|
2
|
+
import { Effect, Either } from "effect"
|
|
3
|
+
|
|
4
|
+
import { parseArgs } from "../../src/docker-git/cli/parser.js"
|
|
5
|
+
|
|
6
|
+
describe("parseArgs network options", () => {
|
|
7
|
+
it.effect("parses create network mode options", () =>
|
|
8
|
+
Effect.sync(() => {
|
|
9
|
+
const parsed = parseArgs([
|
|
10
|
+
"create",
|
|
11
|
+
"--repo-url",
|
|
12
|
+
"https://github.com/org/repo.git",
|
|
13
|
+
"--network-mode",
|
|
14
|
+
"project",
|
|
15
|
+
"--shared-network",
|
|
16
|
+
"ignored-shared-network"
|
|
17
|
+
])
|
|
18
|
+
if (Either.isLeft(parsed)) {
|
|
19
|
+
throw new Error(`unexpected parse error: ${parsed.left._tag}`)
|
|
20
|
+
}
|
|
21
|
+
const command = parsed.right
|
|
22
|
+
if (command._tag !== "Create") {
|
|
23
|
+
throw new Error("expected Create command")
|
|
24
|
+
}
|
|
25
|
+
expect(command.config.dockerNetworkMode).toBe("project")
|
|
26
|
+
expect(command.config.dockerSharedNetworkName).toBe("ignored-shared-network")
|
|
27
|
+
}))
|
|
28
|
+
|
|
29
|
+
it.effect("fails on invalid network mode", () =>
|
|
30
|
+
Effect.sync(() => {
|
|
31
|
+
const command = parseArgs([
|
|
32
|
+
"create",
|
|
33
|
+
"--repo-url",
|
|
34
|
+
"https://github.com/org/repo.git",
|
|
35
|
+
"--network-mode",
|
|
36
|
+
"invalid"
|
|
37
|
+
])
|
|
38
|
+
Either.match(command, {
|
|
39
|
+
onLeft: (error) => {
|
|
40
|
+
expect(error._tag).toBe("InvalidOption")
|
|
41
|
+
},
|
|
42
|
+
onRight: () => {
|
|
43
|
+
throw new Error("expected parse error")
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
}))
|
|
47
|
+
})
|