Skip to content

Commit 9b512a1

Browse files
working e2e scirpt with modal
1 parent be3459b commit 9b512a1

File tree

5 files changed

+747
-534
lines changed

5 files changed

+747
-534
lines changed

scripts/end_to_end_bug_gen.py

Lines changed: 221 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,139 @@ def get_org_gh_from_profile(repo_id: str) -> Optional[str]:
281281
return None
282282

283283

284+
def build_docker_image(repo_id: str) -> bool:
285+
"""Build Docker image for the repository.
286+
287+
Note: generate_profile.py cleans up images after Stage 2, so we need to rebuild.
288+
289+
Args:
290+
repo_id: Repository ID (e.g., google__gson.dd2fe59c)
291+
292+
Returns:
293+
True if build successful, False if failed
294+
"""
295+
try:
296+
from swesmith.profiles import registry
297+
profile = registry.get(repo_id)
298+
image_name = profile.image_name
299+
300+
print(f"\n🏗️ Building Docker image for Modal...")
301+
print(f" Image: {image_name}")
302+
print(f" (Note: generate_profile.py cleans up after Stage 2, so we rebuild for Modal)")
303+
304+
# Check if image already exists
305+
check_result = subprocess.run(
306+
["docker", "image", "inspect", image_name],
307+
capture_output=True,
308+
check=False
309+
)
310+
311+
if check_result.returncode == 0:
312+
print(f"✅ Docker image already exists locally")
313+
return True
314+
315+
# Build from the profile's Dockerfile property
316+
from swesmith.profiles import registry
317+
profile = registry.get(repo_id)
318+
319+
# Create temporary build directory
320+
import tempfile
321+
with tempfile.TemporaryDirectory() as tmpdir:
322+
dockerfile_path = Path(tmpdir) / "Dockerfile"
323+
324+
# Write the profile's Dockerfile
325+
print(f" Using Dockerfile from profile registry")
326+
with open(dockerfile_path, 'w') as f:
327+
f.write(profile.dockerfile)
328+
329+
# Build the image
330+
build_cmd = [
331+
"docker",
332+
"build",
333+
"--no-cache", # Force rebuild to avoid stale cached layers
334+
"-f", str(dockerfile_path),
335+
"-t", image_name,
336+
tmpdir # Build context
337+
]
338+
339+
print(f" Building: {' '.join(build_cmd)}")
340+
build_result = subprocess.run(
341+
build_cmd,
342+
capture_output=False, # Show build progress
343+
check=False
344+
)
345+
346+
# Check if build was successful by verifying the image exists
347+
verify_result = subprocess.run(
348+
["docker", "image", "inspect", image_name],
349+
capture_output=True,
350+
check=False
351+
)
352+
353+
if verify_result.returncode == 0:
354+
print(f"✅ Successfully built Docker image")
355+
return True
356+
else:
357+
print(f"❌ Failed to build Docker image")
358+
print(f" The build command may have failed internally")
359+
print(f" Check the output above for errors (e.g., missing GITHUB_TOKEN)")
360+
return False
361+
362+
except Exception as e:
363+
print(f"❌ Error building Docker image: {e}")
364+
return False
365+
366+
367+
def push_docker_image(repo_id: str) -> bool:
368+
"""Push Docker image to Docker Hub after building.
369+
370+
Args:
371+
repo_id: Repository ID (e.g., google__gson.dd2fe59c)
372+
373+
Returns:
374+
True if push successful or not needed, False if failed
375+
"""
376+
try:
377+
from swesmith.profiles import registry
378+
profile = registry.get(repo_id)
379+
image_name = profile.image_name
380+
381+
print(f"\n🐳 Pushing Docker image to Docker Hub...")
382+
print(f" Image: {image_name}")
383+
384+
# Check if image exists locally first
385+
check_result = subprocess.run(
386+
["docker", "image", "inspect", image_name],
387+
capture_output=True,
388+
check=False
389+
)
390+
391+
if check_result.returncode != 0:
392+
print(f"⚠️ Warning: Docker image {image_name} not found locally")
393+
print(" Skipping push - image will need to be built first")
394+
return False
395+
396+
# Push the image
397+
push_result = subprocess.run(
398+
["docker", "push", image_name],
399+
capture_output=False, # Show progress
400+
check=False
401+
)
402+
403+
if push_result.returncode == 0:
404+
print(f"✅ Successfully pushed Docker image to Docker Hub")
405+
return True
406+
else:
407+
print(f"❌ Failed to push Docker image (exit code: {push_result.returncode})")
408+
print(" Make sure you're logged in to Docker Hub: docker login")
409+
print(" Make sure you have permission to push to this repository")
410+
return False
411+
412+
except Exception as e:
413+
print(f"❌ Error pushing Docker image: {e}")
414+
return False
415+
416+
284417
def main():
285418
parser = argparse.ArgumentParser(
286419
description="End-to-end bug generation pipeline: profile → bugs → analysis",
@@ -369,8 +502,8 @@ def main():
369502
)
370503
parser.add_argument(
371504
'--org-dh',
372-
default='cs329a-swesmith',
373-
help='Docker Hub organization for images (default: cs329a-swesmith)'
505+
default='priyank0003',
506+
help='Docker Hub organization for images (default: priyank0003)'
374507
)
375508

376509
# Pipeline control
@@ -379,6 +512,11 @@ def main():
379512
action='store_true',
380513
help='Skip profile generation (profile already exists in registry)'
381514
)
515+
parser.add_argument(
516+
'--skip-agent',
517+
action='store_true',
518+
help='Skip agent run but regenerate profile from existing agent-result directory'
519+
)
382520
parser.add_argument(
383521
'--skip-bug-gen',
384522
action='store_true',
@@ -389,6 +527,11 @@ def main():
389527
action='store_true',
390528
help='Skip bug analysis'
391529
)
530+
parser.add_argument(
531+
'--force-docker-push',
532+
action='store_true',
533+
help='Force Docker image build and push (useful for testing Docker workflow without re-running profile gen)'
534+
)
392535

393536
args = parser.parse_args()
394537

@@ -408,30 +551,44 @@ def main():
408551

409552
output_log = Path(f"{owner}-{repo}-gen.out")
410553

411-
# Build command for generate_profile.py
412-
gen_cmd = [
413-
sys.executable,
414-
"mini-swe-agent-automate-repo-installation/generate_profile.py",
415-
args.repo_name,
416-
"--model", args.model,
417-
"--max-cost", str(args.max_cost),
418-
"--max-time", str(args.max_time),
419-
]
420-
421-
if args.language.lower() == 'python':
422-
gen_cmd.append('--python-repo')
423-
424-
if args.livestream:
425-
gen_cmd.append('--livestream')
426-
427-
if args.verify:
428-
gen_cmd.append('--verify')
429-
430-
exit_code, _ = run_command(gen_cmd, "Generating profile", tee_output=output_log)
431-
432-
if exit_code != 0:
433-
print(f"\n❌ Profile generation failed. Check {output_log} for details.")
434-
sys.exit(1)
554+
# Check if we should skip agent run but regenerate profile
555+
if args.skip_agent:
556+
print("\n⏭️ Skipping agent run (--skip-agent)")
557+
print(" Reusing existing agent-result directory")
558+
559+
# Check if agent-result directory exists
560+
agent_result_dir = Path(f"agent-result/{owner}-{repo}")
561+
if not agent_result_dir.exists():
562+
print(f"\n❌ Agent result directory not found: {agent_result_dir}")
563+
print(" Cannot skip agent run without existing agent results")
564+
sys.exit(1)
565+
566+
print(f"✅ Found existing agent-result directory: {agent_result_dir}")
567+
else:
568+
# Build command for generate_profile.py
569+
gen_cmd = [
570+
sys.executable,
571+
"mini-swe-agent-automate-repo-installation/generate_profile.py",
572+
args.repo_name,
573+
"--model", args.model,
574+
"--max-cost", str(args.max_cost),
575+
"--max-time", str(args.max_time),
576+
]
577+
578+
if args.language.lower() == 'python':
579+
gen_cmd.append('--python-repo')
580+
581+
if args.livestream:
582+
gen_cmd.append('--livestream')
583+
584+
if args.verify:
585+
gen_cmd.append('--verify')
586+
587+
exit_code, _ = run_command(gen_cmd, "Generating profile", tee_output=output_log)
588+
589+
if exit_code != 0:
590+
print(f"\n❌ Profile generation failed. Check {output_log} for details.")
591+
sys.exit(1)
435592

436593
# Load and insert the generated profile
437594
result = load_generated_profile(args.repo_name)
@@ -488,6 +645,38 @@ def main():
488645

489646
print(f"\n✅ Found repo_id in registry: {repo_id}")
490647

648+
# Build and push Docker image to Docker Hub if using Modal validation
649+
# (Modal needs to pull the image from a registry)
650+
# Note: generate_profile.py cleans up the image after Stage 2, so we rebuild
651+
if (args.use_modal and not args.skip_profile_gen) or args.force_docker_push:
652+
if args.force_docker_push and args.skip_profile_gen:
653+
print("\n" + "="*80)
654+
print("DOCKER BUILD & PUSH (--force-docker-push)")
655+
print("="*80)
656+
657+
# First, rebuild the image (it was cleaned up by generate_profile.py)
658+
build_success = build_docker_image(repo_id)
659+
if not build_success:
660+
print("\n❌ Failed to build Docker image")
661+
print(" Modal validation requires a Docker image")
662+
sys.exit(1)
663+
664+
# Then, push it to Docker Hub
665+
push_success = push_docker_image(repo_id)
666+
if not push_success:
667+
print("\n⚠️ Warning: Failed to push Docker image to Docker Hub")
668+
print(" Modal validation requires the image to be available on Docker Hub")
669+
print(" You can:")
670+
print(" 1. Login to Docker Hub: docker login")
671+
print(" 2. Manually push the image later")
672+
print(" 3. Continue with local validation (remove --use-modal flag)")
673+
674+
# Ask user if they want to continue
675+
response = input("\n❓ Continue anyway? (y/N): ").strip().lower()
676+
if response not in ['y', 'yes']:
677+
print("Exiting...")
678+
sys.exit(1)
679+
491680
# Step 2: Generate bugs (unless skipped)
492681
if not args.skip_bug_gen:
493682
print("\n" + "="*80)
@@ -586,16 +775,12 @@ def main():
586775
print("STEP 3: BUG ANALYSIS")
587776
print("="*80)
588777

589-
# Get org_gh to construct the correct path
590-
org_gh = get_org_gh_from_profile(repo_id)
591-
analysis_repo_id = f"{org_gh}/{repo_id}" if org_gh else repo_id
592-
593-
print(f"📂 Analysis path: logs/bug_gen/{analysis_repo_id}")
778+
print(f"📂 Analysis path: logs/bug_gen/{repo_id}")
594779

595780
analysis_cmd = [
596781
sys.executable,
597782
"scripts/analyze_bugs.py",
598-
analysis_repo_id
783+
repo_id
599784
]
600785

601786
exit_code, _ = run_command(analysis_cmd, "Analyzing generated bugs", capture_output=True)
@@ -617,16 +802,12 @@ def main():
617802
if not args.skip_bug_gen:
618803
print(f"Validation: {'Modal (parallel)' if args.use_modal else 'Local'}")
619804

620-
# Construct paths with org_gh if available
621-
org_gh = get_org_gh_from_profile(repo_id)
622-
path_prefix = f"{org_gh}/{repo_id}" if org_gh else repo_id
623-
624805
print(f"\nGenerated artifacts:")
625806
print(f" - Profile: {get_profile_file_for_language(args.language)}")
626-
print(f" - Bugs: logs/bug_gen/{path_prefix}/")
627-
print(f" - Patches: logs/bug_gen/{path_prefix}_all_patches.json")
628-
print(f" - Validation: logs/run_validation/{path_prefix}/")
629-
print(f" - Analysis: logs/analysis/{path_prefix}_analysis.json")
807+
print(f" - Bugs: logs/bug_gen/{repo_id}/")
808+
print(f" - Patches: logs/bug_gen/{repo_id}_all_patches.json")
809+
print(f" - Validation: logs/run_validation/{repo_id}/")
810+
print(f" - Analysis: logs/analysis/{repo_id}_analysis.json")
630811
print("\n" + "="*80)
631812

632813
except KeyboardInterrupt:

scripts/procgen.py

-15.3 KB
Binary file not shown.

0 commit comments

Comments
 (0)