forked from Qrytics/autoDocker
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcore.py
More file actions
165 lines (135 loc) · 6.35 KB
/
Copy pathcore.py
File metadata and controls
165 lines (135 loc) · 6.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
import os
import zipfile
import tempfile
import shutil
import os
import docker
from pathlib import Path
from litellm import completion
## Feature 1 / Task 1
class WorkspaceManager:
def __init__(self, zip_path):
self.zip_path = zip_path
self.temp_dir = None
self.file_map = []
def setup(self):
"""Extracts zip to a temp directory and maps the structure."""
self.temp_dir = tempfile.mkdtemp(prefix="auto_docker_")
with zipfile.ZipFile(self.zip_path, 'r') as zip_ref:
zip_ref.extractall(self.temp_dir)
self._build_file_map()
return self.temp_dir
def _build_file_map(self):
"""Scans the directory and creates a string representation of the project."""
exclude_dirs = {'.git', '__pycache__', 'node_modules', '.venv', 'env'}
lines = []
for root, dirs, files in os.walk(self.temp_dir):
# Filter out excluded directories
dirs[:] = [d for d in dirs if d not in exclude_dirs]
level = os.path.relpath(root, self.temp_dir).count(os.sep)
indent = ' ' * 4 * level
folder_name = os.path.basename(root)
if folder_name:
lines.append(f"{indent}📂 {folder_name}/")
sub_indent = ' ' * 4 * (level + 1)
for f in files:
lines.append(f"{sub_indent}📄 {f}")
self.file_map = "\n".join(lines)
def get_context_for_llm(self):
"""Returns the file map and content of key manifest files."""
context = f"Project Structure:\n{self.file_map}\n\n"
# Identify key files that define the tech stack
manifests = ['package.json', 'requirements.txt', 'go.mod', 'pom.xml', 'main.py', 'app.py', 'index.js']
context += "Key File Contents:\n"
for root, _, files in os.walk(self.temp_dir):
for f in files:
if f in manifests:
file_path = os.path.join(root, f)
with open(file_path, 'r', errors='ignore') as content:
context += f"--- {f} ---\n{content.read(1000)}\n" # Read first 1000 chars
return context
def cleanup(self):
"""Deletes the temporary workspace."""
if self.temp_dir and os.path.exists(self.temp_dir):
shutil.rmtree(self.temp_dir)
## Feature 1 / Task 2
class LLMArchitect:
def __init__(self, model="gemini/gemini-pro"): # Defaulting to Gemini, but LiteLLM handles any
self.model = model
def generate_dockerfile(self, project_context):
"""Sends project context to LLM and extracts the Dockerfile code."""
system_prompt = (
"You are an expert DevOps Engineer. Your task is to generate a Dockerfile based on a project structure.\n"
"STRICT REQUIREMENTS:\n"
"1. Use MULTI-STAGE builds to keep the image small.\n"
"2. Use 'distroless' or 'alpine' as the final runtime base for security.\n"
"3. Optimize for layer caching (copy requirements/package files first).\n"
"4. Ensure the entry point is correctly identified from the file list.\n"
"5. Return ONLY the content of the Dockerfile. No markdown code blocks, no explanations."
)
user_prompt = f"Analyze this project and create the most optimized Dockerfile possible:\n\n{project_context}"
try:
response = completion(
model=self.model,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
],
temperature=0.2 # Keep it deterministic
)
dockerfile_content = response.choices[0].message.content
return self._clean_llm_output(dockerfile_content)
except Exception as e:
return f"Error generating Dockerfile: {str(e)}"
def _clean_llm_output(self, text):
"""Removes markdown backticks if the LLM ignores instructions."""
return text.replace("```dockerfile", "").replace("```", "").strip()
def heal_dockerfile(self, project_context, faulty_dockerfile, error_log):
"""Asks the LLM to fix a Dockerfile that failed to build."""
system_prompt = (
"You are a Senior DevOps Engineer. A Dockerfile you generated failed to build.\n"
"Analyze the error log and the original Dockerfile, then provide a FIXED version.\n"
"STRICT: Return ONLY the fixed Dockerfile content."
)
user_prompt = (
f"PROJECT CONTEXT:\n{project_context}\n\n"
f"FAULTY DOCKERFILE:\n{faulty_dockerfile}\n\n"
f"ERROR LOG:\n{error_log}\n\n"
"Please fix the error (e.g., missing dependencies, wrong paths, or incorrect base image)."
)
response = completion(
model=self.model,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
],
temperature=0.1
)
return self._clean_llm_output(response.choices[0].message.content)
## Feature 2 / Task 1
class DockerBuilder:
def __init__(self):
try:
self.client = docker.from_env()
except Exception as e:
raise Exception("Docker is not running. Please start Docker Desktop.")
def build_image(self, path, tag="auto-docker-app:latest"):
"""Builds a docker image from the provided directory path."""
print(f"Building image: {tag}...")
try:
image, build_logs = self.client.images.build(
path=path,
tag=tag,
rm=True, # Remove intermediate containers
forcerm=True # Always remove intermediate containers
)
# Print build logs to show progress
for line in build_logs:
if 'stream' in line:
print(line['stream'].strip())
return image
except docker.errors.BuildError as e:
print("Build Failed!")
# This log is crucial for our 'Self-Healing' feature later
error_log = "".join([str(log) for log in e.build_log])
raise Exception(f"Build Error: {error_log}")