gitarsenal-cli 1.3.7 → 1.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -119,12 +119,12 @@ except Exception as e:
119
119
  # print(f"šŸ” Token ID exists: {'Yes' if os.environ.get('MODAL_TOKEN_ID') else 'No'}")
120
120
  # print(f"šŸ” Token secret exists: {'Yes' if os.environ.get('MODAL_TOKEN_SECRET') else 'No'}")
121
121
  # print(f"šŸ” Token exists: {'Yes' if os.environ.get('MODAL_TOKEN') else 'No'}")
122
- # if os.environ.get('MODAL_TOKEN_ID'):
123
- # print(f"šŸ” Token ID length: {len(os.environ.get('MODAL_TOKEN_ID'))}")
124
- # if os.environ.get('MODAL_TOKEN_SECRET'):
125
- # print(f"šŸ” Token secret length: {len(os.environ.get('MODAL_TOKEN_SECRET'))}")
126
- # if os.environ.get('MODAL_TOKEN'):
127
- # print(f"šŸ” Token length: {len(os.environ.get('MODAL_TOKEN'))}")
122
+ if os.environ.get('MODAL_TOKEN_ID'):
123
+ print(f"šŸ” Token ID length: {len(os.environ.get('MODAL_TOKEN_ID'))}")
124
+ if os.environ.get('MODAL_TOKEN_SECRET'):
125
+ print(f"šŸ” Token secret length: {len(os.environ.get('MODAL_TOKEN_SECRET'))}")
126
+ if os.environ.get('MODAL_TOKEN'):
127
+ print(f"šŸ” Token length: {len(os.environ.get('MODAL_TOKEN'))}")
128
128
  # print(f"āœ… Token setup completed")
129
129
 
130
130
  # Import modal after token setup
@@ -2025,7 +2025,7 @@ cd "{current_dir}"
2025
2025
  print("ā³ Sandbox will remain running until you terminate it with:")
2026
2026
  print(f"modal sandbox terminate {sandbox_id}")
2027
2027
 
2028
- # Try to open a new terminal window and connect to the containera
2028
+ # Try to open a new terminal window and connect to the container
2029
2029
  if container_id:
2030
2030
  print("šŸ–„ļø Attempting to open new terminal window...")
2031
2031
  # Use osascript to open a new terminal with the modal shell command
@@ -2558,6 +2558,15 @@ def create_modal_ssh_container(gpu_type, repo_url=None, repo_name=None, setup_co
2558
2558
  print(f"āŒ Error running container: {e}")
2559
2559
  return None
2560
2560
 
2561
+ def is_local_server_running(url, timeout=2):
2562
+ """Check if a local server is running by attempting a connection."""
2563
+ import requests
2564
+ try:
2565
+ response = requests.get(url, timeout=timeout)
2566
+ return True
2567
+ except requests.exceptions.RequestException:
2568
+ return False
2569
+
2561
2570
  def fetch_setup_commands_from_api(repo_url):
2562
2571
  """Fetch setup commands from the GitIngest API using real repository analysis."""
2563
2572
  import tempfile
@@ -2565,8 +2574,13 @@ def fetch_setup_commands_from_api(repo_url):
2565
2574
  import os
2566
2575
  import shutil
2567
2576
  import json
2577
+ import time
2578
+ import requests
2568
2579
 
2569
- api_url = "https://git-arsenal.vercel.app/api/analyze-with-gitingest"
2580
+ # Define API endpoints to try in order - using only online endpoints
2581
+ api_endpoints = [
2582
+ "https://www.gitarsenal.dev/api/analyze-with-gitingest" # Working endpoint with www prefix
2583
+ ]
2570
2584
 
2571
2585
  print(f"šŸ” Fetching setup commands from API for repository: {repo_url}")
2572
2586
 
@@ -2588,6 +2602,13 @@ def fetch_setup_commands_from_api(repo_url):
2588
2602
  temp_dir = tempfile.mkdtemp(prefix="repo_analysis_")
2589
2603
  output_file = os.path.join(temp_dir, "digest.json")
2590
2604
 
2605
+ # Create a directory to save GitIngest results
2606
+ save_dir = os.path.join(os.path.expanduser("~"), "gitarsenal_results")
2607
+ os.makedirs(save_dir, exist_ok=True)
2608
+ timestamp = time.strftime("%Y%m%d_%H%M%S")
2609
+ repo_name = repo_url.split("/")[-1].replace(".git", "")
2610
+ save_file = os.path.join(save_dir, f"gitingest_{repo_name}_{timestamp}.txt")
2611
+
2591
2612
  try:
2592
2613
  if has_gitingest_cli:
2593
2614
  # Use gitingest CLI tool to analyze the repository directly from URL
@@ -2618,44 +2639,87 @@ def fetch_setup_commands_from_api(repo_url):
2618
2639
  else:
2619
2640
  print(f"āœ… GitIngest analysis completed successfully")
2620
2641
 
2621
- # Read the output file - GitIngest outputs structured text, not JSON
2642
+ # Read the output file - note that the default format might not be JSON
2622
2643
  try:
2623
- with open(output_file, 'r', encoding='utf-8') as f:
2624
- content = f.read()
2625
-
2626
- # Process the text output into a structured format
2627
- print(f"šŸ“„ Processing GitIngest text output")
2628
-
2629
- # Extract key information from the text output
2630
- import re
2631
-
2632
- # Try to identify language
2633
- language_match = re.search(r"(?i)language[s]?:?\s*(\w+)", content)
2634
- detected_language = language_match.group(1) if language_match else "Unknown"
2635
-
2636
- # Try to identify technologies
2637
- tech_matches = re.findall(r"(?i)(python|javascript|typescript|react|node|vue|angular|django|flask|express|pytorch|tensorflow|rust|go|ruby|rails|php|laravel|c\+\+|java|kotlin|swift)", content)
2638
- detected_technologies = list(set(tech_matches)) if tech_matches else []
2639
-
2640
- # Create a structured representation
2641
- gitingest_data = {
2642
- "system_info": {
2643
- "detected_language": detected_language,
2644
- "detected_technologies": detected_technologies,
2645
- },
2646
- "repository_analysis": {
2647
- "summary": content[:1000], # First 1000 chars as summary
2648
- "content_preview": content[:5000] # First 5000 chars as preview
2649
- },
2650
- "raw_text": content[:10000], # Include a portion of the raw text
2651
- "success": True
2652
- }
2653
-
2654
- print(f"šŸ“„ Processed GitIngest output: Detected language: {detected_language}, Technologies: {', '.join(detected_technologies) if detected_technologies else 'None'}")
2655
-
2656
- except FileNotFoundError:
2657
- print(f"āš ļø Output file not found at {output_file}")
2658
- gitingest_data = generate_basic_repo_analysis_from_url(repo_url)
2644
+ # First try to parse as JSON
2645
+ try:
2646
+ with open(output_file, 'r', encoding='utf-8') as f:
2647
+ content = f.read()
2648
+
2649
+ # Save the GitIngest output to the results directory
2650
+ with open(save_file, 'w', encoding='utf-8') as save_f:
2651
+ save_f.write(content)
2652
+ print(f"šŸ“ GitIngest output saved to: {save_file}")
2653
+
2654
+ try:
2655
+ gitingest_data = json.loads(content)
2656
+ print(f"āœ… GitIngest data loaded as JSON from {output_file}")
2657
+ except json.JSONDecodeError:
2658
+ # If not JSON, convert the text output to a basic structure
2659
+ print(f"āš ļø GitIngest output is not in JSON format, converting text to structure")
2660
+
2661
+ # Process the text to extract useful information
2662
+ import re
2663
+
2664
+ # Try to identify language
2665
+ language_match = re.search(r"(?i)language[s]?:?\s*(\w+)", content)
2666
+ detected_language = language_match.group(1) if language_match else "Unknown"
2667
+
2668
+ # Try to identify technologies with stronger evidence requirements
2669
+ tech_patterns = {
2670
+ "python": r"(?i)(python|\.py\b|pip\b|requirements\.txt|setup\.py)",
2671
+ "javascript": r"(?i)(javascript|\.js\b|node|npm|yarn|package\.json)",
2672
+ "typescript": r"(?i)(typescript|\.ts\b|tsc\b|tsconfig\.json)",
2673
+ "go": r"(?i)(\bgo\b|golang|\.go\b|go\.mod|go\.sum)",
2674
+ "rust": r"(?i)(rust|\.rs\b|cargo|Cargo\.toml)",
2675
+ "java": r"(?i)(java\b|\.java\b|maven|gradle|pom\.xml)",
2676
+ "c++": r"(?i)(c\+\+|\.cpp\b|\.hpp\b|cmake\b|CMakeLists\.txt)",
2677
+ "pytorch": r"(?i)(pytorch|torch\b|nn\.Module)",
2678
+ "tensorflow": r"(?i)(tensorflow|tf\.|keras\b)",
2679
+ }
2680
+
2681
+ # Count occurrences to filter out false positives
2682
+ tech_counts = {}
2683
+ for tech, pattern in tech_patterns.items():
2684
+ matches = re.findall(pattern, content)
2685
+ if matches:
2686
+ tech_counts[tech] = len(matches)
2687
+
2688
+ # Filter technologies based on threshold
2689
+ thresholds = {
2690
+ "javascript": 3, # Higher threshold for JavaScript
2691
+ "go": 3, # Higher threshold for Go
2692
+ "default": 2 # Default threshold
2693
+ }
2694
+
2695
+ detected_technologies = []
2696
+ for tech, count in tech_counts.items():
2697
+ threshold = thresholds.get(tech, thresholds["default"])
2698
+ if count >= threshold:
2699
+ detected_technologies.append(tech)
2700
+ print(f"šŸ“Š Detected {tech} with confidence score {count}")
2701
+
2702
+ # Create a structured representation
2703
+ gitingest_data = {
2704
+ "system_info": {
2705
+ "detected_language": detected_language,
2706
+ "detected_technologies": detected_technologies,
2707
+ },
2708
+ "repository_analysis": {
2709
+ "summary": content[:5000], # First 5000 chars as summary
2710
+ "content_preview": content[:10000] # First 10000 chars as preview
2711
+ },
2712
+ "success": True
2713
+ }
2714
+
2715
+ # Save the processed data
2716
+ processed_file = os.path.join(save_dir, f"gitingest_processed_{repo_name}_{timestamp}.json")
2717
+ with open(processed_file, 'w', encoding='utf-8') as proc_f:
2718
+ json.dump(gitingest_data, proc_f, indent=2)
2719
+ print(f"šŸ“ Processed GitIngest data saved to: {processed_file}")
2720
+ except FileNotFoundError:
2721
+ print(f"āš ļø Output file not found at {output_file}")
2722
+ gitingest_data = generate_basic_repo_analysis_from_url(repo_url)
2659
2723
  except Exception as e:
2660
2724
  print(f"āš ļø Error reading GitIngest output: {e}")
2661
2725
  gitingest_data = generate_basic_repo_analysis_from_url(repo_url)
@@ -2672,17 +2736,46 @@ def fetch_setup_commands_from_api(repo_url):
2672
2736
 
2673
2737
  print(f"šŸ“¤ API Request payload prepared (GitIngest data size: {len(json.dumps(gitingest_data))} bytes)")
2674
2738
 
2675
- # Make the API request with a shorter timeout to avoid long waits
2676
- print(f"🌐 Making POST request to: {api_url}")
2677
- try:
2678
- response = requests.post(api_url, json=payload, timeout=30) # Reduced timeout to 30 seconds
2739
+ # Try each endpoint in sequence until one succeeds
2740
+ response = None
2741
+ for api_url in api_endpoints:
2742
+ # Use the retry mechanism for more reliable requests
2743
+ response = make_api_request_with_retry(
2744
+ url=api_url,
2745
+ payload=payload,
2746
+ max_retries=2,
2747
+ timeout=180 # 3 minute timeout
2748
+ )
2679
2749
 
2750
+ # If we got a response and it's successful, break out of the loop
2751
+ if response and response.status_code == 200:
2752
+ print(f"āœ… Successful response from {api_url}")
2753
+ break
2754
+
2755
+ if response:
2756
+ print(f"āš ļø Endpoint {api_url} returned status code {response.status_code}, trying next endpoint...")
2757
+ else:
2758
+ print(f"āš ļø Failed to connect to {api_url}, trying next endpoint...")
2759
+
2760
+ # If we've tried all endpoints and still don't have a response, use fallback
2761
+ if response is None:
2762
+ print("āŒ All API endpoints failed")
2763
+ return generate_fallback_commands(gitingest_data)
2764
+
2765
+ # Continue with the response we got from the successful endpoint
2766
+ if not response:
2767
+ print("āŒ No valid response received from any endpoint")
2768
+ return generate_fallback_commands(gitingest_data)
2769
+
2770
+ try:
2680
2771
  print(f"šŸ“„ API Response status code: {response.status_code}")
2681
2772
 
2682
2773
  if response.status_code == 200:
2683
2774
  try:
2684
2775
  data = response.json()
2685
2776
  print(f"šŸ“„ API Response data received")
2777
+ print(f"šŸ“„ Response size: {len(response.text)} bytes")
2778
+ print(f"šŸ“„ Response URL: {response.url}")
2686
2779
 
2687
2780
  # Extract setup commands from the response
2688
2781
  if "setupInstructions" in data and "commands" in data["setupInstructions"]:
@@ -2720,68 +2813,40 @@ def fetch_setup_commands_from_api(repo_url):
2720
2813
  print("šŸ“‹ Available fields in response:")
2721
2814
  for key in data.keys():
2722
2815
  print(f" - {key}")
2723
-
2724
- # Generate fallback commands based on the gitingest data
2725
- print("\n" + "="*80)
2726
- print("šŸ“‹ GENERATING FALLBACK SETUP COMMANDS")
2727
- print("="*80)
2728
- fallback_commands = generate_fallback_commands(gitingest_data)
2729
- return fallback_commands
2730
- except Exception as e:
2731
- print(f"āš ļø Error parsing API response: {e}")
2816
+ # Return fallback commands
2817
+ return generate_fallback_commands(gitingest_data)
2818
+ except json.JSONDecodeError as e:
2819
+ print(f"āŒ Failed to parse API response as JSON: {e}")
2820
+ print(f"Raw response: {response.text[:500]}...")
2821
+ # Return fallback commands
2822
+ return generate_fallback_commands(gitingest_data)
2732
2823
  elif response.status_code == 504:
2733
2824
  print(f"āŒ API request timed out (504 Gateway Timeout)")
2734
- print(f"āš ļø The server took too long to respond. Using fallback commands instead.")
2735
-
2736
- # Generate fallback commands based on the gitingest data
2737
- print("\n" + "="*80)
2738
- print("šŸ“‹ GENERATING FALLBACK SETUP COMMANDS")
2739
- print("="*80)
2740
- fallback_commands = generate_fallback_commands(gitingest_data)
2741
- return fallback_commands
2825
+ print("āš ļø The server took too long to respond. Using fallback commands instead.")
2826
+ # Return fallback commands
2827
+ return generate_fallback_commands(gitingest_data)
2742
2828
  else:
2743
- print(f"āŒ API request failed with status code {response.status_code}")
2744
- print(f"Response: {response.text[:500]}")
2745
-
2746
- # Generate fallback commands based on the gitingest data
2747
- print("\n" + "="*80)
2748
- print("šŸ“‹ GENERATING FALLBACK SETUP COMMANDS")
2749
- print("="*80)
2750
- fallback_commands = generate_fallback_commands(gitingest_data)
2751
- return fallback_commands
2752
- except requests.exceptions.Timeout:
2753
- print(f"āŒ API request timed out after 30 seconds")
2754
- print(f"āš ļø The server took too long to respond. Using fallback commands instead.")
2755
-
2756
- # Generate fallback commands based on the gitingest data
2757
- print("\n" + "="*80)
2758
- print("šŸ“‹ GENERATING FALLBACK SETUP COMMANDS")
2759
- print("="*80)
2760
- fallback_commands = generate_fallback_commands(gitingest_data)
2761
- return fallback_commands
2829
+ print(f"āŒ API request failed with status code: {response.status_code}")
2830
+ print(f"āŒ Response URL: {response.url}")
2831
+ print(f"āŒ Response headers: {dict(response.headers)}")
2832
+ print(f"āŒ Error response: {response.text[:500]}...")
2833
+ # Return fallback commands
2834
+ return generate_fallback_commands(gitingest_data)
2762
2835
  except Exception as e:
2763
- print(f"āŒ Error making API request: {e}")
2764
-
2765
- # Generate fallback commands based on the gitingest data
2766
- print("\n" + "="*80)
2767
- print("šŸ“‹ GENERATING FALLBACK SETUP COMMANDS")
2768
- print("="*80)
2769
- fallback_commands = generate_fallback_commands(gitingest_data)
2770
- return fallback_commands
2836
+ print(f"āŒ Error processing API response: {str(e)}")
2837
+ print("āš ļø Using fallback commands instead")
2838
+ # Return fallback commands
2839
+ return generate_fallback_commands(gitingest_data)
2840
+ except Exception as e:
2841
+ print(f"āŒ Error fetching setup commands from API: {e}")
2842
+ import traceback
2843
+ traceback.print_exc()
2844
+ # Return fallback commands
2845
+ return generate_fallback_commands(None)
2771
2846
  finally:
2772
- # Clean up temporary directory
2847
+ # Clean up the temporary directory
2773
2848
  print(f"🧹 Cleaning up temporary directory...")
2774
- try:
2775
- shutil.rmtree(temp_dir)
2776
- except Exception as e:
2777
- print(f"āš ļø Error cleaning up temporary directory: {e}")
2778
-
2779
- # If all else fails, return basic setup commands
2780
- return [
2781
- "apt-get update -y",
2782
- "apt-get install -y git curl wget",
2783
- "pip install --upgrade pip setuptools wheel"
2784
- ]
2849
+ shutil.rmtree(temp_dir, ignore_errors=True)
2785
2850
 
2786
2851
  def generate_fallback_commands(gitingest_data):
2787
2852
  """Generate fallback setup commands based on repository analysis"""
@@ -3060,8 +3125,11 @@ def generate_basic_repo_analysis(repo_dir):
3060
3125
  }
3061
3126
 
3062
3127
  def get_setup_commands_from_local_api(repo_url, gitingest_data):
3063
- """Try to get setup commands from the local API."""
3064
- api_url = "http://localhost:3000/api/analyze-with-gitingest"
3128
+ """Try to get setup commands from the API."""
3129
+ # Use only online endpoints
3130
+ api_endpoints = [
3131
+ "https://www.gitarsenal.dev/api/analyze-with-gitingest" # Working endpoint with www prefix
3132
+ ]
3065
3133
 
3066
3134
  # Prepare the request payload
3067
3135
  payload = {
@@ -3070,34 +3138,49 @@ def get_setup_commands_from_local_api(repo_url, gitingest_data):
3070
3138
  "userRequest": "Setup and run the repository"
3071
3139
  }
3072
3140
 
3073
- try:
3074
- # Make the API request
3075
- print(f"🌐 Making POST request to local API: {api_url}")
3076
- response = requests.post(api_url, json=payload, timeout=60)
3141
+ # Try each API endpoint
3142
+ for api_url in api_endpoints:
3143
+ # Use the retry mechanism for more reliable requests
3144
+ response = make_api_request_with_retry(
3145
+ url=api_url,
3146
+ payload=payload,
3147
+ max_retries=2,
3148
+ timeout=180 # 3 minute timeout
3149
+ )
3077
3150
 
3078
- if response.status_code == 200:
3079
- data = response.json()
3080
- if "setupInstructions" in data and "commands" in data["setupInstructions"]:
3081
- commands = data["setupInstructions"]["commands"]
3082
- print(f"āœ… Successfully fetched {len(commands)} setup commands from local API")
3083
-
3084
- # Print the original commands
3085
- print("šŸ“‹ Original commands from local API:")
3086
- for i, cmd in enumerate(commands, 1):
3087
- print(f" {i}. {cmd}")
3088
-
3089
- # Fix the commands
3090
- fixed_commands = fix_setup_commands(commands)
3091
-
3092
- # Print the fixed commands
3093
- print("\nšŸ“‹ Fixed commands:")
3094
- for i, cmd in enumerate(fixed_commands, 1):
3095
- print(f" {i}. {cmd}")
3096
-
3097
- return fixed_commands
3098
- except Exception as e:
3099
- print(f"āŒ Error connecting to local API: {e}")
3151
+ if response and response.status_code == 200:
3152
+ try:
3153
+ data = response.json()
3154
+ print(f"šŸ“„ Response size: {len(response.text)} bytes")
3155
+ print(f"šŸ“„ Response URL: {response.url}")
3156
+ if "setupInstructions" in data and "commands" in data["setupInstructions"]:
3157
+ commands = data["setupInstructions"]["commands"]
3158
+ print(f"āœ… Successfully fetched {len(commands)} setup commands from API at {api_url}")
3159
+
3160
+ # Print the original commands
3161
+ print("šŸ“‹ Original commands from API:")
3162
+ for i, cmd in enumerate(commands, 1):
3163
+ print(f" {i}. {cmd}")
3164
+
3165
+ # Fix the commands
3166
+ fixed_commands = fix_setup_commands(commands)
3167
+
3168
+ # Print the fixed commands
3169
+ print("\nšŸ“‹ Fixed commands:")
3170
+ for i, cmd in enumerate(fixed_commands, 1):
3171
+ print(f" {i}. {cmd}")
3172
+
3173
+ return fixed_commands
3174
+ else:
3175
+ print("āš ļø API response did not contain setupInstructions.commands field")
3176
+ except json.JSONDecodeError:
3177
+ print(f"āŒ Failed to parse API response as JSON")
3178
+ elif response:
3179
+ print(f"āŒ API request failed with status code: {response.status_code}")
3180
+ else:
3181
+ print(f"āŒ Failed to connect to {api_url}")
3100
3182
 
3183
+ print("āŒ All API endpoints failed")
3101
3184
  return None
3102
3185
 
3103
3186
  # Define a function to create and return a properly configured ssh container function
@@ -3388,9 +3471,248 @@ def show_usage_examples():
3388
3471
  print("│ --volume-name my-persistent-volume │")
3389
3472
  print("ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜\n")
3390
3473
 
3474
+ print("With GitIngest API (recommended)")
3475
+ print("ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”")
3476
+ print("│ gitarsenal --gpu A10G --repo-url https://github.com/username/repo.git \\ │")
3477
+ print("│ --use-gitingest │")
3478
+ print("ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜\n")
3479
+
3480
+ print("With Original API")
3481
+ print("ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”")
3482
+ print("│ gitarsenal --gpu A10G --repo-url https://github.com/username/repo.git \\ │")
3483
+ print("│ --use-api │")
3484
+ print("ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜\n")
3485
+
3391
3486
  print("Available GPU Options:")
3392
3487
  print(" T4, L4, A10G, A100-40GB, A100-80GB, L40S, H100, H200, B200")
3393
3488
 
3489
+ def make_api_request_with_retry(url, payload, max_retries=2, timeout=180):
3490
+ """Make an API request with retry mechanism."""
3491
+ import requests
3492
+ import time
3493
+
3494
+ for attempt in range(max_retries + 1):
3495
+ try:
3496
+ if attempt > 0:
3497
+ print(f"šŸ”„ Retry attempt {attempt}/{max_retries}...")
3498
+
3499
+ print(f"🌐 Making POST request to: {url}")
3500
+ print(f"ā³ Waiting up to {timeout//60} minutes for response...")
3501
+
3502
+ # Set allow_redirects=True to follow redirects automatically
3503
+ response = requests.post(
3504
+ url,
3505
+ json=payload,
3506
+ timeout=timeout,
3507
+ allow_redirects=True,
3508
+ headers={
3509
+ 'Content-Type': 'application/json',
3510
+ 'User-Agent': 'GitArsenal-CLI/1.0'
3511
+ }
3512
+ )
3513
+
3514
+ # Print redirect info if any
3515
+ if response.history:
3516
+ print(f"āœ… Request was redirected {len(response.history)} times")
3517
+ for resp in response.history:
3518
+ print(f" - Redirect: {resp.status_code} from {resp.url}")
3519
+ print(f"āœ… Final URL: {response.url}")
3520
+
3521
+ return response
3522
+ except requests.exceptions.RequestException as e:
3523
+ if attempt < max_retries:
3524
+ retry_delay = 2 ** attempt # Exponential backoff
3525
+ print(f"āš ļø Request failed: {str(e)}")
3526
+ print(f"ā³ Waiting {retry_delay} seconds before retrying...")
3527
+ time.sleep(retry_delay)
3528
+ else:
3529
+ print(f"āŒ All retry attempts failed: {str(e)}")
3530
+ return None
3531
+
3532
+ return None
3533
+
3534
+ def get_setup_commands_from_gitingest(repo_url):
3535
+ """
3536
+ Get repository setup commands using the gitingest approach.
3537
+
3538
+ This function is inspired by gitingest_setup_client.py and provides a more
3539
+ robust way to get setup commands for a repository.
3540
+
3541
+ Args:
3542
+ repo_url: URL of the repository to set up
3543
+
3544
+ Returns:
3545
+ List of setup commands or None if failed
3546
+ """
3547
+ import requests
3548
+ import json
3549
+ import os
3550
+ import sys
3551
+ import tempfile
3552
+ import subprocess
3553
+
3554
+ print(f"šŸ” Getting setup commands for repository: {repo_url}")
3555
+
3556
+ # Define API endpoints to try in order
3557
+ api_endpoints = [
3558
+ "https://www.gitarsenal.dev/api/gitingest-setup-commands",
3559
+ "https://gitarsenal.dev/api/gitingest-setup-commands",
3560
+ "https://www.gitarsenal.dev/api/analyze-with-gitingest",
3561
+ "http://localhost:3000/api/gitingest-setup-commands"
3562
+ ]
3563
+
3564
+ # Generate basic gitingest data
3565
+ def generate_basic_gitingest_data():
3566
+ # Extract repo name from URL
3567
+ repo_name = repo_url.split('/')[-1].replace('.git', '')
3568
+
3569
+ return {
3570
+ "system_info": {
3571
+ "platform": "Unknown",
3572
+ "python_version": "Unknown",
3573
+ "detected_language": "Unknown",
3574
+ "detected_technologies": [],
3575
+ "file_count": 0,
3576
+ "repo_stars": 0,
3577
+ "repo_forks": 0,
3578
+ "primary_package_manager": "Unknown",
3579
+ "complexity_level": "Unknown"
3580
+ },
3581
+ "repository_analysis": {
3582
+ "summary": f"Repository: {repo_name}",
3583
+ "tree": "",
3584
+ "content_preview": ""
3585
+ },
3586
+ "success": True
3587
+ }
3588
+
3589
+ # Try to generate gitingest data using CLI if available
3590
+ def generate_gitingest_data_from_cli():
3591
+ try:
3592
+ # Check if gitingest CLI is available
3593
+ subprocess.run(["gitingest", "--help"], check=True, capture_output=True, text=True)
3594
+
3595
+ # Create a temporary file for the output
3596
+ with tempfile.NamedTemporaryFile(suffix='.json', delete=False) as tmp:
3597
+ output_file = tmp.name
3598
+
3599
+ # Run gitingest command
3600
+ print(f"Running gitingest analysis on {repo_url}...")
3601
+ gitingest_cmd = ["gitingest", repo_url, "-o", output_file]
3602
+ result = subprocess.run(gitingest_cmd, capture_output=True, text=True)
3603
+
3604
+ if result.returncode != 0:
3605
+ print(f"GitIngest CLI failed: {result.stderr}")
3606
+ return None
3607
+
3608
+ # Read the output file
3609
+ try:
3610
+ with open(output_file, 'r', encoding='utf-8') as f:
3611
+ content = f.read()
3612
+ try:
3613
+ data = json.loads(content)
3614
+ return data
3615
+ except json.JSONDecodeError:
3616
+ # If not JSON, convert the text output to a basic structure
3617
+ return {
3618
+ "system_info": {
3619
+ "platform": "Unknown",
3620
+ "python_version": "Unknown",
3621
+ "detected_language": "Unknown",
3622
+ "detected_technologies": []
3623
+ },
3624
+ "repository_analysis": {
3625
+ "summary": content[:5000], # First 5000 chars as summary
3626
+ "tree": "",
3627
+ "content_preview": content[:10000] # First 10000 chars as preview
3628
+ },
3629
+ "success": True
3630
+ }
3631
+ except Exception as e:
3632
+ print(f"Error reading gitingest output: {e}")
3633
+ return None
3634
+ finally:
3635
+ # Clean up the temporary file
3636
+ if os.path.exists(output_file):
3637
+ os.unlink(output_file)
3638
+
3639
+ except (subprocess.SubprocessError, FileNotFoundError):
3640
+ print("GitIngest CLI not found")
3641
+ return None
3642
+
3643
+ # First try to get data from CLI
3644
+ gitingest_data = generate_gitingest_data_from_cli()
3645
+
3646
+ # If CLI failed, use basic data
3647
+ if not gitingest_data:
3648
+ print("Using basic gitingest data")
3649
+ gitingest_data = generate_basic_gitingest_data()
3650
+
3651
+ # Try each API endpoint
3652
+ for api_url in api_endpoints:
3653
+ try:
3654
+ print(f"Trying API endpoint: {api_url}")
3655
+
3656
+ payload = {
3657
+ "repoUrl": repo_url,
3658
+ "gitingestData": gitingest_data
3659
+ }
3660
+
3661
+ # Use the retry mechanism for more reliable requests
3662
+ response = make_api_request_with_retry(
3663
+ url=api_url,
3664
+ payload=payload,
3665
+ max_retries=2,
3666
+ timeout=180 # 3 minute timeout
3667
+ )
3668
+
3669
+ if not response:
3670
+ print(f"Failed to connect to {api_url}")
3671
+ continue
3672
+
3673
+ if response.status_code != 200:
3674
+ print(f"API request failed with status code: {response.status_code}")
3675
+ continue
3676
+
3677
+ try:
3678
+ result = response.json()
3679
+
3680
+ # Check if we have commands in the response
3681
+ commands = None
3682
+
3683
+ # Check for different response formats
3684
+ if "commands" in result:
3685
+ commands = result["commands"]
3686
+ elif "setupInstructions" in result and "commands" in result["setupInstructions"]:
3687
+ commands = result["setupInstructions"]["commands"]
3688
+
3689
+ if commands:
3690
+ print(f"āœ… Successfully fetched {len(commands)} setup commands from API at {api_url}")
3691
+
3692
+ # Print the commands
3693
+ print("\nšŸ“‹ Setup Commands:")
3694
+ for i, cmd in enumerate(commands, 1):
3695
+ print(f" {i}. {cmd}")
3696
+
3697
+ # Fix the commands
3698
+ fixed_commands = fix_setup_commands(commands)
3699
+
3700
+ # Print the fixed commands
3701
+ print("\nšŸ“‹ Fixed commands:")
3702
+ for i, cmd in enumerate(fixed_commands, 1):
3703
+ print(f" {i}. {cmd}")
3704
+
3705
+ return fixed_commands
3706
+ else:
3707
+ print("No commands found in API response")
3708
+ except json.JSONDecodeError:
3709
+ print(f"Failed to parse API response as JSON")
3710
+ except Exception as e:
3711
+ print(f"Error with API endpoint {api_url}: {e}")
3712
+
3713
+ print("āŒ All API endpoints failed")
3714
+ return generate_fallback_commands(gitingest_data)
3715
+
3394
3716
  if __name__ == "__main__":
3395
3717
  # Parse command line arguments when script is run directly
3396
3718
  import argparse
@@ -3409,6 +3731,7 @@ if __name__ == "__main__":
3409
3731
  parser.add_argument('--timeout', type=int, default=60, help='Container timeout in minutes (default: 60)')
3410
3732
  parser.add_argument('--ssh-password', type=str, help='SSH password (random if not provided)')
3411
3733
  parser.add_argument('--use-api', action='store_true', help='Fetch setup commands from API')
3734
+ parser.add_argument('--use-gitingest', action='store_true', help='Use gitingest approach to fetch setup commands')
3412
3735
  parser.add_argument('--show-examples', action='store_true', help='Show usage examples')
3413
3736
 
3414
3737
  args = parser.parse_args()
@@ -3424,8 +3747,18 @@ if __name__ == "__main__":
3424
3747
 
3425
3748
  # If --use-api flag is set and repo_url is provided, fetch setup commands from API
3426
3749
 
3750
+ # If --use-gitingest flag is set and repo_url is provided, use the gitingest approach
3751
+ if args.use_gitingest and args.repo_url:
3752
+ print("šŸ”„ Using gitingest approach to fetch setup commands")
3753
+ api_commands = get_setup_commands_from_gitingest(args.repo_url)
3754
+ if api_commands:
3755
+ setup_commands = api_commands
3756
+ print(f"šŸ“‹ Using {len(setup_commands)} commands from gitingest API")
3757
+ else:
3758
+ print("āš ļø Failed to get commands from gitingest API, no fallback commands will be used")
3759
+ setup_commands = []
3427
3760
  # If --use-api flag is set and repo_url is provided, fetch setup commands from API
3428
- if args.use_api and args.repo_url:
3761
+ elif args.use_api and args.repo_url:
3429
3762
  print("šŸ”„ Using API to fetch setup commands")
3430
3763
  api_commands = fetch_setup_commands_from_api(args.repo_url)
3431
3764
  if api_commands: