From af5b7c562174bba9020de867344479c93b991878 Mon Sep 17 00:00:00 2001 From: skulidropek <66840575+skulidropek@users.noreply.github.com> Date: Fri, 20 Feb 2026 19:53:46 +0000 Subject: [PATCH 1/4] fix(app): bundle docker-git CLI and drop workspace lib runtime dep --- packages/app/package.json | 5 ++-- packages/app/vite.docker-git.config.ts | 34 ++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 packages/app/vite.docker-git.config.ts diff --git a/packages/app/package.json b/packages/app/package.json index 9070b7fd..f2014462 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -13,13 +13,14 @@ "prebuild": "pnpm -C ../lib build", "build": "pnpm run build:app && pnpm run build:docker-git", "build:app": "vite build --ssr src/app/main.ts", + "prepack": "pnpm run build:docker-git", "dev": "vite build --watch --ssr src/app/main.ts", "prelint": "pnpm -C ../lib build", "lint": "PATH=../../scripts:$PATH vibecode-linter src/", "lint:tests": "PATH=../../scripts:$PATH vibecode-linter tests/", "lint:effect": "PATH=../../scripts:$PATH eslint --config eslint.effect-ts-check.config.mjs .", "prebuild:docker-git": "pnpm -C ../lib build", - "build:docker-git": "tsc -p tsconfig.build.json", + "build:docker-git": "vite build --config vite.docker-git.config.ts", "check": "pnpm run typecheck", "clone": "pnpm -C ../.. run clone", "docker-git": "node dist/src/docker-git/main.js", @@ -53,7 +54,6 @@ "homepage": "https://github.com/ProverCoderAI/docker-git#readme", "packageManager": "pnpm@10.28.0", "dependencies": { - "@effect-template/lib": "workspace:*", "@effect/cli": "^0.73.0", "@effect/cluster": "^0.56.1", "@effect/experimental": "^0.58.0", @@ -73,6 +73,7 @@ "ts-morph": "^27.0.2" }, "devDependencies": { + "@effect-template/lib": "workspace:*", "@biomejs/biome": "^2.3.11", "@effect/eslint-plugin": "^0.3.2", "@effect/language-service": "latest", diff --git a/packages/app/vite.docker-git.config.ts b/packages/app/vite.docker-git.config.ts new file mode 100644 index 00000000..7605ac45 --- /dev/null +++ b/packages/app/vite.docker-git.config.ts @@ -0,0 +1,34 @@ +import path from "node:path" +import { fileURLToPath } from "node:url" +import { defineConfig } from "vite" +import tsconfigPaths from "vite-tsconfig-paths" + +const __filename = fileURLToPath(import.meta.url) +const __dirname = path.dirname(__filename) + +export default defineConfig({ + plugins: [tsconfigPaths()], + publicDir: false, + resolve: { + alias: { + "@": path.resolve(__dirname, "src"), + "@effect-template/lib": path.resolve(__dirname, "../lib/src") + } + }, + build: { + target: "node20", + outDir: "dist", + sourcemap: true, + ssr: "src/docker-git/main.ts", + rollupOptions: { + output: { + format: "es", + entryFileNames: "src/docker-git/main.js", + inlineDynamicImports: true + } + } + }, + ssr: { + target: "node" + } +}) From ffdca8e6a5b3dac3ce7e78f32173a970e5195ead Mon Sep 17 00:00:00 2001 From: skulidropek <66840575+skulidropek@users.noreply.github.com> Date: Fri, 20 Feb 2026 20:02:45 +0000 Subject: [PATCH 2/4] test(e2e): verify local packed docker-git CLI via pnpm --- .github/workflows/check.yml | 11 ++++++ scripts/e2e/local-package-cli.sh | 68 ++++++++++++++++++++++++++++++++ scripts/e2e/run-all.sh | 3 +- 3 files changed, 80 insertions(+), 2 deletions(-) create mode 100755 scripts/e2e/local-package-cli.sh diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 18fd6a2c..a6780b5e 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -85,6 +85,17 @@ jobs: - name: Lint Effect-TS (lib) run: pnpm --filter ./packages/lib lint:effect + e2e-local-package: + name: E2E (Local package CLI) + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v6 + - name: Install dependencies + uses: ./.github/actions/setup + - name: Pack and run local package via pnpm + run: bash scripts/e2e/local-package-cli.sh + e2e-opencode: name: E2E (OpenCode) runs-on: ubuntu-latest diff --git a/scripts/e2e/local-package-cli.sh b/scripts/e2e/local-package-cli.sh new file mode 100755 index 00000000..4c269f0a --- /dev/null +++ b/scripts/e2e/local-package-cli.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +set -euo pipefail + +RUN_ID="$(date +%s)-$RANDOM" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +ROOT_BASE="${DOCKER_GIT_E2E_ROOT_BASE:-/tmp/docker-git-e2e-root}" +mkdir -p "$ROOT_BASE" +ROOT="$(mktemp -d "$ROOT_BASE/local-package-cli.XXXXXX")" +KEEP="${KEEP:-0}" + +PACK_LOG="$ROOT/npm-pack.log" +HELP_LOG="$ROOT/docker-git-help.log" +PACKED_TARBALL="" + +fail() { + echo "e2e/local-package-cli: $*" >&2 + exit 1 +} + +on_error() { + local line="$1" + echo "e2e/local-package-cli: failed at line $line" >&2 + if [[ -f "$PACK_LOG" ]]; then + echo "--- npm pack log ---" >&2 + cat "$PACK_LOG" >&2 || true + fi + if [[ -f "$HELP_LOG" ]]; then + echo "--- docker-git --help log ---" >&2 + cat "$HELP_LOG" >&2 || true + fi +} + +cleanup() { + if [[ "$KEEP" == "1" ]]; then + echo "e2e/local-package-cli: KEEP=1 set; preserving temp dir: $ROOT" >&2 + return + fi + if [[ -n "$PACKED_TARBALL" ]] && [[ -f "$PACKED_TARBALL" ]]; then + rm -f "$PACKED_TARBALL" >/dev/null 2>&1 || true + fi + rm -rf "$ROOT" >/dev/null 2>&1 || true +} + +trap 'on_error $LINENO' ERR +trap cleanup EXIT + +cd "$REPO_ROOT/packages/app" +npm pack --silent >"$PACK_LOG" +tarball_name="$(tail -n 1 "$PACK_LOG" | tr -d '\r')" +[[ -n "$tarball_name" ]] || fail "npm pack did not return tarball name" + +PACKED_TARBALL="$REPO_ROOT/packages/app/$tarball_name" +[[ -f "$PACKED_TARBALL" ]] || fail "packed tarball not found: $PACKED_TARBALL" + +dep_keys="$(tar -xOf "$PACKED_TARBALL" package/package.json | node -e 'let s="";process.stdin.on("data",(c)=>{s+=c});process.stdin.on("end",()=>{const pkg=JSON.parse(s);const deps=Object.keys(pkg.dependencies ?? {});if (deps.includes("@effect-template/lib")) {console.error("@effect-template/lib must not be a runtime dependency in packed package");process.exit(1)}process.stdout.write(deps.join(","));});')" +[[ "$dep_keys" == *"effect"* ]] || fail "packed dependency set looks invalid: $dep_keys" + +mkdir -p "$ROOT/project" +cd "$ROOT/project" +npm init -y >/dev/null +pnpm add "$PACKED_TARBALL" --silent --lockfile=false +pnpm docker-git --help >"$HELP_LOG" 2>&1 + +grep -Fq -- "docker-git clone [options]" "$HELP_LOG" \ + || fail "expected docker-git help output from local packed package" + +echo "e2e/local-package-cli: local tarball install + pnpm docker-git --help OK" >&2 diff --git a/scripts/e2e/run-all.sh b/scripts/e2e/run-all.sh index dee24740..b575dfe8 100755 --- a/scripts/e2e/run-all.sh +++ b/scripts/e2e/run-all.sh @@ -5,7 +5,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cases=("$@") if [[ "${#cases[@]}" -eq 0 ]]; then - cases=("clone-cache" "login-context" "opencode-autoconnect") + cases=("local-package-cli" "clone-cache" "login-context" "opencode-autoconnect") fi for case_name in "${cases[@]}"; do @@ -20,4 +20,3 @@ for case_name in "${cases[@]}"; do done echo "e2e/run-all: all cases OK" >&2 - From b4a7d40bf70b579d46464e4027804f0a6377e292 Mon Sep 17 00:00:00 2001 From: skulidropek <66840575+skulidropek@users.noreply.github.com> Date: Fri, 20 Feb 2026 20:05:47 +0000 Subject: [PATCH 3/4] chore(lockfile): sync app deps after packaging changes --- pnpm-lock.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 55ac4bdf..7a42e78c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,9 +22,6 @@ importers: packages/app: dependencies: - '@effect-template/lib': - specifier: workspace:* - version: link:../lib '@effect/cli': specifier: ^0.73.0 version: 0.73.0(@effect/platform@0.94.1(effect@3.19.14))(@effect/printer-ansi@0.47.0(@effect/typeclass@0.38.0(effect@3.19.14))(effect@3.19.14))(@effect/printer@0.47.0(@effect/typeclass@0.38.0(effect@3.19.14))(effect@3.19.14))(effect@3.19.14) @@ -80,6 +77,9 @@ importers: '@biomejs/biome': specifier: ^2.3.11 version: 2.3.11 + '@effect-template/lib': + specifier: workspace:* + version: link:../lib '@effect/eslint-plugin': specifier: ^0.3.2 version: 0.3.2 From db6e1e6d78611d96d458a909b93254a8f430cff5 Mon Sep 17 00:00:00 2001 From: skulidropek <66840575+skulidropek@users.noreply.github.com> Date: Fri, 20 Feb 2026 20:40:18 +0000 Subject: [PATCH 4/4] fix(lib): split create command builder for lint stability --- packages/lib/src/core/command-builders.ts | 106 ++++++++++++++++------ 1 file changed, 76 insertions(+), 30 deletions(-) diff --git a/packages/lib/src/core/command-builders.ts b/packages/lib/src/core/command-builders.ts index 94fc0959..2aaba64a 100644 --- a/packages/lib/src/core/command-builders.ts +++ b/packages/lib/src/core/command-builders.ts @@ -200,6 +200,69 @@ const resolvePaths = ( } }) +type CreateBehavior = { + readonly runUp: boolean + readonly openSsh: boolean + readonly force: boolean + readonly forceEnv: boolean + readonly enableMcpPlaywright: boolean +} + +const resolveCreateBehavior = (raw: RawOptions): CreateBehavior => ({ + runUp: raw.up ?? true, + openSsh: raw.openSsh ?? false, + force: raw.force ?? false, + forceEnv: raw.forceEnv ?? false, + enableMcpPlaywright: raw.enableMcpPlaywright ?? false +}) + +type BuildTemplateConfigInput = { + readonly repo: RepoBasics + readonly names: NameConfig + readonly paths: PathConfig + readonly dockerNetworkMode: CreateCommand["config"]["dockerNetworkMode"] + readonly dockerSharedNetworkName: string + readonly gitTokenLabel: string | undefined + readonly codexAuthLabel: string | undefined + readonly claudeAuthLabel: string | undefined + readonly enableMcpPlaywright: boolean +} + +const buildTemplateConfig = ({ + claudeAuthLabel, + codexAuthLabel, + dockerNetworkMode, + dockerSharedNetworkName, + enableMcpPlaywright, + gitTokenLabel, + names, + paths, + repo +}: BuildTemplateConfigInput): CreateCommand["config"] => ({ + containerName: names.containerName, + serviceName: names.serviceName, + sshUser: repo.sshUser, + sshPort: repo.sshPort, + repoUrl: repo.repoUrl, + repoRef: repo.repoRef, + gitTokenLabel, + codexAuthLabel, + claudeAuthLabel, + targetDir: repo.targetDir, + volumeName: names.volumeName, + dockerGitPath: paths.dockerGitPath, + authorizedKeysPath: paths.authorizedKeysPath, + envGlobalPath: paths.envGlobalPath, + envProjectPath: paths.envProjectPath, + codexAuthPath: paths.codexAuthPath, + codexSharedAuthPath: paths.codexSharedAuthPath, + codexHome: paths.codexHome, + dockerNetworkMode, + dockerSharedNetworkName, + enableMcpPlaywright, + pnpmVersion: defaultTemplateConfig.pnpmVersion +}) + // CHANGE: build a typed create command from raw options (CLI or API) // WHY: share deterministic command construction across CLI and server // QUOTE(ТЗ): "В lib ты оставляешь бизнес логику, а все CLI морду хранишь в app" @@ -217,11 +280,7 @@ export const buildCreateCommand = ( const repo = yield* _(resolveRepoBasics(raw)) const names = yield* _(resolveNames(raw, repo.projectSlug)) const paths = yield* _(resolvePaths(raw, repo.repoPath)) - const runUp = raw.up ?? true - const openSsh = raw.openSsh ?? false - const force = raw.force ?? false - const forceEnv = raw.forceEnv ?? false - const enableMcpPlaywright = raw.enableMcpPlaywright ?? false + const behavior = resolveCreateBehavior(raw) const gitTokenLabel = normalizeGitTokenLabel(raw.gitTokenLabel) const codexAuthLabel = normalizeAuthLabel(raw.codexTokenLabel) const claudeAuthLabel = normalizeAuthLabel(raw.claudeTokenLabel) @@ -233,34 +292,21 @@ export const buildCreateCommand = ( return { _tag: "Create", outDir: paths.outDir, - runUp, - openSsh, - force, - forceEnv, + runUp: behavior.runUp, + openSsh: behavior.openSsh, + force: behavior.force, + forceEnv: behavior.forceEnv, waitForClone: false, - config: { - containerName: names.containerName, - serviceName: names.serviceName, - sshUser: repo.sshUser, - sshPort: repo.sshPort, - repoUrl: repo.repoUrl, - repoRef: repo.repoRef, + config: buildTemplateConfig({ + repo, + names, + paths, + dockerNetworkMode, + dockerSharedNetworkName, gitTokenLabel, codexAuthLabel, claudeAuthLabel, - targetDir: repo.targetDir, - volumeName: names.volumeName, - dockerGitPath: paths.dockerGitPath, - authorizedKeysPath: paths.authorizedKeysPath, - envGlobalPath: paths.envGlobalPath, - envProjectPath: paths.envProjectPath, - codexAuthPath: paths.codexAuthPath, - codexSharedAuthPath: paths.codexSharedAuthPath, - codexHome: paths.codexHome, - dockerNetworkMode, - dockerSharedNetworkName, - enableMcpPlaywright, - pnpmVersion: defaultTemplateConfig.pnpmVersion - } + enableMcpPlaywright: behavior.enableMcpPlaywright + }) } })