Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 32 additions & 27 deletions agent/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from typing import TYPE_CHECKING, Dict, List, TypedDict

import docker
import filelock
import jinja2
import requests
import semantic_version as sv
Expand Down Expand Up @@ -239,33 +240,37 @@ def _clone_repository(self, app_info: AppInfo, clone_dir: str):
commit_hash = app_info["hash"]
clone_path = os.path.join(clone_dir, source, commit_hash[:10])

if os.path.exists(clone_path):
return f"git clone {app_info['app']} CACHED\n"

os.makedirs(clone_path, exist_ok=True)

commands = [
"git init",
f"git remote add origin {app_info['url']}",
"git config credential.helper ''",
f"git fetch --depth 1 origin {commit_hash}",
f"git checkout -B {app_info['branch']}",
f"git checkout {commit_hash}",
]

output = f"git clone {app_info['app']}\n"
try:
for command in commands:
result = self._run_git_command(command, cwd=clone_path)
output += result + "\n"
except CloneError as e:
cleanup_parital_clones(clone_path)
error_output = e.data.get("output", "") if isinstance(e.data, dict) else str(e.data)
raise CloneError(
{"traceback": f"Failed to clone repository for {app_info['app']} - {error_output}"}
) from None

return output
# Parallel workers race on the same app+commit; hold a lock across the
# cache check and the clone so the loser waits or hits the cache
lock_path = os.path.join(clone_dir, f"{source}-{commit_hash[:10]}.lock")
with filelock.FileLock(lock_path, timeout=600):
if os.path.exists(clone_path):
return f"git clone {app_info['app']} CACHED\n"

os.makedirs(clone_path, exist_ok=True)

commands = [
"git init",
f"git remote add origin {app_info['url']}",
"git config credential.helper ''",
f"git fetch --depth 1 origin {commit_hash}",
f"git checkout -B {app_info['branch']}",
f"git checkout {commit_hash}",
]

output = f"git clone {app_info['app']}\n"
try:
for command in commands:
result = self._run_git_command(command, cwd=clone_path)
output += result + "\n"
except CloneError as e:
cleanup_parital_clones(clone_path)
error_output = e.data.get("output", "") if isinstance(e.data, dict) else str(e.data)
raise CloneError(
{"traceback": f"Failed to clone repository for {app_info['app']} - {error_output}"}
) from None

return output

@step("Clone Repositories")
def clone_repositories(self):
Expand Down