diff --git a/scripts/admin-web-update.sh b/scripts/admin-web-update.sh index cbe68d9..cfc5c14 100644 --- a/scripts/admin-web-update.sh +++ b/scripts/admin-web-update.sh @@ -20,7 +20,14 @@ run_in_shop() { fi } -git config --global --add safe.directory "$SHOP_ROOT" 2>/dev/null || true +ensure_git_safe() { + git config --global --add safe.directory "$SHOP_ROOT" 2>/dev/null || true + if [ "$(id -u)" -eq 0 ] && id "$RUN_USER" &>/dev/null; then + sudo -u "$RUN_USER" git config --global --add safe.directory "$SHOP_ROOT" 2>/dev/null || true + fi +} + +ensure_git_safe echo "=== Обновление Shop (админка) ===" echo "Каталог: $SHOP_ROOT" diff --git a/scripts/shop-root.sh b/scripts/shop-root.sh index c1eef42..b0d4644 100644 --- a/scripts/shop-root.sh +++ b/scripts/shop-root.sh @@ -32,3 +32,7 @@ fi export SHOP_ROOT cd "$SHOP_ROOT" + +if [ -d "$SHOP_ROOT/.git" ]; then + git config --global --add safe.directory "$SHOP_ROOT" 2>/dev/null || true +fi diff --git a/src/services/git-deploy.js b/src/services/git-deploy.js index 3f5ccaa..8b1d5df 100644 --- a/src/services/git-deploy.js +++ b/src/services/git-deploy.js @@ -30,13 +30,43 @@ function isUpdateEnabled() { return fs.existsSync(path.join(root, 'scripts', 'admin-web-update.sh')); } +function gitEnv(root) { + const resolved = path.resolve(root); + return { + ...process.env, + GIT_TERMINAL_PROMPT: '0', + GIT_CONFIG_COUNT: '1', + GIT_CONFIG_KEY_0: 'safe.directory', + GIT_CONFIG_VALUE_0: resolved, + }; +} + +/** Git 2.35+: репозиторий с другим владельцем (www-data vs root) */ +async function ensureSafeDirectory(root) { + const resolved = path.resolve(root); + const home = process.env.HOME || '/var/www'; + try { + await execFileAsync('git', ['config', '--global', '--add', 'safe.directory', resolved], { + timeout: 15000, + env: { ...process.env, HOME: home }, + }); + } catch { + // глобальный config может быть недоступен — используем -c в gitCmd + } +} + async function gitCmd(args, cwd) { - const { stdout, stderr } = await execFileAsync('git', args, { - cwd, - maxBuffer: 1024 * 1024, - timeout: 90000, - env: { ...process.env, GIT_TERMINAL_PROMPT: '0' }, - }); + const root = path.resolve(cwd); + const { stdout, stderr } = await execFileAsync( + 'git', + ['-c', `safe.directory=${root}`, ...args], + { + cwd: root, + maxBuffer: 1024 * 1024, + timeout: 90000, + env: gitEnv(root), + } + ); return `${stdout}${stderr}`.trim(); } @@ -69,6 +99,7 @@ async function getGitInfo({ fetchRemote = false } = {}) { }; try { + await ensureSafeDirectory(root); info.branch = await gitCmd(['branch', '--show-current'], root); if (!info.branch) { info.branch = '(detached)'; @@ -113,7 +144,7 @@ function runDeployUpdate() { const child = spawn(cmd, args, { cwd: root, - env: { ...process.env, SHOP_ROOT: root }, + env: { ...gitEnv(root), SHOP_ROOT: root }, timeout: 300000, }); diff --git a/src/views/admin/system.ejs b/src/views/admin/system.ejs index 5df360d..b3fd921 100644 --- a/src/views/admin/system.ejs +++ b/src/views/admin/system.ejs @@ -104,6 +104,9 @@
Если служба работает от www-data, добавьте в sudoers (от root):
www-data ALL=(root) NOPASSWD: <%= git.shopRoot || '/opt/shop' %>/scripts/admin-web-update.sh
И в .env: ADMIN_UPDATE_USE_SUDO=1
При ошибке dubious ownership один раз на сервере:
git config --global --add safe.directory <%= git.shopRoot || '/opt/shop/shop10' %>+
(для пользователя службы shop, обычно www-data)