From 65c4799a05a1ab0fb432d10b0ac853b0a35e587c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=A5=E6=B5=A9?= Date: Sun, 8 Mar 2026 17:51:12 +0800 Subject: [PATCH] Reorganize container helper files Group the Git helper scripts under a container-specific directory so container integration is easier to understand and mount. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus --- container/configure-git.sh | 46 ++++++++++++++ container/git-credential-hostproxy | 19 ++++++ container/helper.mjs | 98 ++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+) create mode 100755 container/configure-git.sh create mode 100755 container/git-credential-hostproxy create mode 100644 container/helper.mjs diff --git a/container/configure-git.sh b/container/configure-git.sh new file mode 100755 index 0000000..d08c7f3 --- /dev/null +++ b/container/configure-git.sh @@ -0,0 +1,46 @@ +#!/bin/sh +set -eu + +script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +helper_path="$script_dir/git-credential-hostproxy" +scope='global' +target_repo=$(pwd) + +while [ "$#" -gt 0 ]; do + case "$1" in + --global) + scope='global' + ;; + --local) + scope='local' + ;; + --repo) + shift + if [ "$#" -eq 0 ]; then + printf 'Missing value for --repo\n' >&2 + exit 1 + fi + target_repo="$1" + ;; + *) + printf 'Usage: %s [--global|--local] [--repo PATH]\n' "$0" >&2 + exit 1 + ;; + esac + shift +done + +if [ "$scope" = 'global' ]; then + git config --global --replace-all credential.helper '' + git config --global --add credential.helper "$helper_path" + git config --global credential.useHttpPath true + printf 'Configured global Git credential helper: %s\n' "$helper_path" +else + git -C "$target_repo" config --local --replace-all credential.helper '' + git -C "$target_repo" config --local --add credential.helper "$helper_path" + git -C "$target_repo" config --local credential.useHttpPath true + printf 'Configured local Git credential helper for %s\n' "$target_repo" +fi + +printf 'Proxy URL default: %s\n' "${GIT_CRED_PROXY_URL:-http://host.docker.internal:18765}" +printf 'Protocol filter is configured on the host side\n' diff --git a/container/git-credential-hostproxy b/container/git-credential-hostproxy new file mode 100755 index 0000000..ebb8bba --- /dev/null +++ b/container/git-credential-hostproxy @@ -0,0 +1,19 @@ +#!/bin/sh +set -eu + +script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) + +if [ -n "${GIT_CRED_PROXY_RUNTIME:-}" ]; then + exec "$GIT_CRED_PROXY_RUNTIME" "$script_dir/helper.mjs" "$@" +fi + +if command -v bun >/dev/null 2>&1; then + exec bun "$script_dir/helper.mjs" "$@" +fi + +if command -v node >/dev/null 2>&1; then + exec node "$script_dir/helper.mjs" "$@" +fi + +printf 'Either bun or node is required to run git-credential-hostproxy\n' >&2 +exit 1 diff --git a/container/helper.mjs b/container/helper.mjs new file mode 100644 index 0000000..9d5889a --- /dev/null +++ b/container/helper.mjs @@ -0,0 +1,98 @@ +#!/usr/bin/env node + +import fs from 'node:fs/promises'; +import http from 'node:http'; +import https from 'node:https'; +import path from 'node:path'; +import process from 'node:process'; +import { fileURLToPath } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const operation = process.argv[2] ?? ''; +const supportedOperations = new Set(['get', 'store', 'erase']); + +if (!supportedOperations.has(operation)) { + process.exit(0); +} + +const proxyUrl = process.env.GIT_CRED_PROXY_URL ?? 'http://host.docker.internal:18765'; +const tokenFile = + process.env.GIT_CRED_PROXY_TOKEN_FILE ?? path.join(__dirname, '..', 'host', 'state', 'token'); + +function readStdin() { + return new Promise((resolve, reject) => { + const chunks = []; + + process.stdin.on('data', (chunk) => { + chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)); + }); + + process.stdin.on('end', () => { + resolve(Buffer.concat(chunks).toString('utf8')); + }); + + process.stdin.on('error', reject); + }); +} + +function request(url, body, token) { + const client = url.protocol === 'https:' ? https : http; + + return new Promise((resolve, reject) => { + const req = client.request( + url, + { + method: 'POST', + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'text/plain; charset=utf-8', + 'Content-Length': Buffer.byteLength(body), + }, + }, + (res) => { + const chunks = []; + + res.on('data', (chunk) => { + chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)); + }); + + res.on('end', () => { + resolve({ + statusCode: res.statusCode ?? 0, + body: Buffer.concat(chunks).toString('utf8'), + }); + }); + }, + ); + + req.on('error', reject); + req.end(body); + }); +} + +async function main() { + const token = (process.env.GIT_CRED_PROXY_TOKEN ?? (await fs.readFile(tokenFile, 'utf8'))).trim(); + const body = await readStdin(); + const url = new URL(`/${operation}`, proxyUrl); + const response = await request(url, body, token); + + if (response.statusCode === 200) { + process.stdout.write(response.body); + return; + } + + if (response.body) { + process.stderr.write(response.body.trimEnd() + '\n'); + } else { + process.stderr.write(`Proxy request failed with status ${response.statusCode}\n`); + } + + process.exit(1); +} + +main().catch((error) => { + process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`); + process.exit(1); +});