fix: stop double-executing git clone / setup hooks via run()

run() re-splits its argument as a bare string with no eval, so any
call like run "git clone \"$url\" \"$dir\"" corrupts the quoted
arguments (embedded escaped quotes survive as literal characters,
e.g. git sees the protocol as '"https). Each of the 4 affected call
sites papered over this by following run() with a second, correctly
quoted manual execution of the same command - meaning every real run
double-executed (git clone twice, pip install -e twice, conda env
create twice), with the first, corrupted attempt's failure masked by
the working second attempt.

Fixed by dropping run() at these sites entirely and gating on
$dry_run directly with a single, properly quoted real execution path
(matches the pattern already used in the new model-cache-redirect
extra):
- lib/cf-apps.functions: install-cf-app's clone, provision-orchard-node's clone
- cf-apps/circuitforge-core.manifest: pip install -e setup hook
- cf-apps/kiwi.manifest: conda env create setup hook

Verified on sif via a scratch-dir harness (install-cf-app
circuitforge-core): first run now clones cleanly with no mangled-URL
error, second run correctly skips the clone and re-runs the
idempotent pip install; state.conf stays single-entry across both.
This commit is contained in:
pyr0ball 2026-07-18 22:12:05 -07:00
parent 3d00409b4e
commit bc9d1e564f
3 changed files with 22 additions and 14 deletions

View file

@ -13,8 +13,11 @@ app_needs_core=false
app_setup_bare_metal() { app_setup_bare_metal() {
local dir="$1" local dir="$1"
run "conda run -n ${app_conda_env} pip install -e \"${dir}\"" if [[ $dry_run == true ]]; then
if [[ $dry_run != true ]]; then boxline "DryRun: conda run -n ${app_conda_env} pip install -e ${dir}"
else
# run() re-splits its argument as a bare string with no eval, which
# mangles the quoted path - execute directly instead.
conda run -n "${app_conda_env}" pip install -e "${dir}" conda run -n "${app_conda_env}" pip install -e "${dir}"
fi fi
} }

View file

@ -24,8 +24,11 @@ app_setup_docker() {
# the docs for the LLM backend choice rather than guessing one. # the docs for the LLM backend choice rather than guessing one.
app_setup_bare_metal() { app_setup_bare_metal() {
local dir="$1" local dir="$1"
run "conda env create -f \"${dir}/environment.yml\" -n \"${app_conda_env}\"" if [[ $dry_run == true ]]; then
if [[ $dry_run != true ]]; then boxline "DryRun: conda env create -f ${dir}/environment.yml -n ${app_conda_env}"
else
# run() re-splits its argument as a bare string with no eval, which
# mangles the quoted paths - execute directly instead.
conda env create -f "${dir}/environment.yml" -n "${app_conda_env}" conda env create -f "${dir}/environment.yml" -n "${app_conda_env}"
fi fi
boxline "${app_name}: conda env '${app_conda_env}' created. See docs/getting-started/installation.md and llm-setup.md for the remaining BYOK LLM backend setup (Ollama/vLLM/OpenAI/Anthropic)." boxline "${app_name}: conda env '${app_conda_env}' created. See docs/getting-started/installation.md and llm-setup.md for the remaining BYOK LLM backend setup (Ollama/vLLM/OpenAI/Anthropic)."

View file

@ -181,13 +181,15 @@ install-cf-app() {
local clone_dir="${apps_base_dir}/${app_name}" local clone_dir="${apps_base_dir}/${app_name}"
if [ -d "$clone_dir" ]; then if [ -d "$clone_dir" ]; then
warn "${clone_dir} already exists, skipping clone (will not overwrite)" warn "${clone_dir} already exists, skipping clone (will not overwrite)"
elif [[ $dry_run == true ]]; then
boxline "DryRun: git clone --recurse-submodules ${repo_url} ${clone_dir}"
else else
# run() re-splits its argument as a bare string with no eval, which
# mangles anything with embedded quotes (URLs/paths) - execute directly
# instead, gated on $dry_run above rather than double-executing via run().
boxline "Cloning ${app_name} from ${repo_url}" boxline "Cloning ${app_name} from ${repo_url}"
run "git clone --recurse-submodules \"${repo_url}\" \"${clone_dir}\"" mkdir -p "$apps_base_dir"
if [[ $dry_run != true ]]; then git clone --recurse-submodules "$repo_url" "$clone_dir"
mkdir -p "$apps_base_dir"
git clone --recurse-submodules "$repo_url" "$clone_dir"
fi
fi fi
if [ -n "$app_env_template" ] && [ -f "${clone_dir}/${app_env_template}" ] && [ ! -f "${clone_dir}/.env" ]; then if [ -n "$app_env_template" ] && [ -f "${clone_dir}/${app_env_template}" ] && [ ! -f "${clone_dir}/.env" ]; then
@ -260,13 +262,13 @@ provision-orchard-node() {
if [ -d "$clone_dir" ]; then if [ -d "$clone_dir" ]; then
warn "${clone_dir} already exists, skipping clone (will not overwrite)" warn "${clone_dir} already exists, skipping clone (will not overwrite)"
elif [[ $dry_run == true ]]; then
boxline "DryRun: git clone ${repo_url} ${clone_dir}"
else else
# See install-cf-app for why this doesn't route through run().
boxline "Cloning circuitforge-orch from Forgejo" boxline "Cloning circuitforge-orch from Forgejo"
run "git clone \"${repo_url}\" \"${clone_dir}\"" mkdir -p "$apps_base_dir"
if [[ $dry_run != true ]]; then git clone "$repo_url" "$clone_dir"
mkdir -p "$apps_base_dir"
git clone "$repo_url" "$clone_dir"
fi
fi fi
boxline "Handing off to circuitforge-orch's own install.sh..." boxline "Handing off to circuitforge-orch's own install.sh..."