diagram-to-iac 1.8.0__tar.gz → 1.11.0__tar.gz
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.
- diagram_to_iac-1.11.0/MANIFEST.in +0 -0
- {diagram_to_iac-1.8.0/src/diagram_to_iac.egg-info → diagram_to_iac-1.11.0}/PKG-INFO +1 -1
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/pyproject.toml +2 -6
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/core/issue_tracker.py +2 -13
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/core/memory.py +6 -9
- diagram_to_iac-1.11.0/src/diagram_to_iac/core/test_config.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/services/observability.py +1 -12
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/tools/api_utils.py +2 -12
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/tools/git/git.py +6 -21
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/tools/sec_utils.py +38 -95
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0/src/diagram_to_iac.egg-info}/PKG-INFO +1 -1
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac.egg-info/SOURCES.txt +0 -11
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/tests/test_devops_in_a_box.py +48 -95
- diagram_to_iac-1.8.0/MANIFEST.in +0 -7
- diagram_to_iac-1.8.0/src/diagram_to_iac/agents/demonstrator_langgraph/config.yaml +0 -56
- diagram_to_iac-1.8.0/src/diagram_to_iac/agents/git_langgraph/config.yaml +0 -91
- diagram_to_iac-1.8.0/src/diagram_to_iac/agents/hello_langgraph/config.yaml +0 -6
- diagram_to_iac-1.8.0/src/diagram_to_iac/agents/policy_agent/config.yaml +0 -43
- diagram_to_iac-1.8.0/src/diagram_to_iac/agents/supervisor_langgraph/config.yaml +0 -55
- diagram_to_iac-1.8.0/src/diagram_to_iac/agents/terraform_langgraph/config.yaml +0 -25
- diagram_to_iac-1.8.0/src/diagram_to_iac/config.yaml +0 -253
- diagram_to_iac-1.8.0/src/diagram_to_iac/core/test_config.py +0 -123
- diagram_to_iac-1.8.0/src/diagram_to_iac/templates/issue_frontmatter.yml +0 -240
- diagram_to_iac-1.8.0/src/diagram_to_iac/tools/git/git_config.yaml +0 -102
- diagram_to_iac-1.8.0/src/diagram_to_iac/tools/shell/shell_config.yaml +0 -41
- diagram_to_iac-1.8.0/src/diagram_to_iac/tools/tf/terraform_config.yaml +0 -21
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/README.md +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/setup.cfg +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/__init__.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/actions/__init__.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/actions/git_entry.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/actions/supervisor_entry.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/actions/terraform_agent_entry.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/agents/__init__.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/agents/demonstrator_langgraph/__init__.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/agents/demonstrator_langgraph/agent.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/agents/git_langgraph/__init__.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/agents/git_langgraph/agent.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/agents/git_langgraph/pr.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/agents/hello_langgraph/__init__.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/agents/hello_langgraph/agent.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/agents/policy_agent/__init__.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/agents/policy_agent/agent.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/agents/policy_agent/integration_example.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/agents/policy_agent/tools/__init__.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/agents/policy_agent/tools/tfsec_tool.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/agents/shell_langgraph/__init__.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/agents/shell_langgraph/agent.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/agents/shell_langgraph/detector.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/agents/supervisor_langgraph/__init__.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/agents/supervisor_langgraph/agent.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/agents/supervisor_langgraph/demonstrator.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/agents/supervisor_langgraph/github_listener.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/agents/supervisor_langgraph/guards.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/agents/supervisor_langgraph/pat_loop.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/agents/supervisor_langgraph/router.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/agents/terraform_langgraph/__init__.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/agents/terraform_langgraph/agent.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/agents/terraform_langgraph/parser.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/cli.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/core/__init__.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/core/agent_base.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/core/config_loader.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/core/enhanced_memory.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/core/errors.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/core/registry.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/r2d.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/services/__init__.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/services/commenter.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/services/step_summary.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/tools/__init__.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/tools/git/__init__.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/tools/hello/__init__.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/tools/hello/cal_utils.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/tools/hello/text_utils.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/tools/llm_utils/__init__.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/tools/llm_utils/anthropic_driver.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/tools/llm_utils/base_driver.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/tools/llm_utils/gemini_driver.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/tools/llm_utils/grok_driver.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/tools/llm_utils/openai_driver.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/tools/llm_utils/router.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/tools/shell/__init__.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/tools/shell/shell.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/tools/text_utils.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac/tools/tf/terraform.py +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac.egg-info/dependency_links.txt +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac.egg-info/entry_points.txt +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac.egg-info/requires.txt +0 -0
- {diagram_to_iac-1.8.0 → diagram_to_iac-1.11.0}/src/diagram_to_iac.egg-info/top_level.txt +0 -0
File without changes
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "diagram-to-iac"
|
7
|
-
version = "1.
|
7
|
+
version = "1.11.0"
|
8
8
|
description = "Convert architecture diagrams into IaC modules"
|
9
9
|
readme = "README.md"
|
10
10
|
authors = [{ name="vindpro", email="admin@vindpro.com" }]
|
@@ -22,12 +22,8 @@ git-agent = "diagram_to_iac.actions.git_entry:main"
|
|
22
22
|
terraform-agent = "diagram_to_iac.actions.terraform_agent_entry:main"
|
23
23
|
supervisor-agent = "diagram_to_iac.actions.supervisor_entry:main"
|
24
24
|
|
25
|
-
[tool.setuptools]
|
26
|
-
include-package-data = true
|
25
|
+
[tool.setuptools] # (intentionally left empty)
|
27
26
|
|
28
27
|
[tool.setuptools.packages.find] # find‑options live here
|
29
28
|
where = ["src"]
|
30
29
|
exclude = ["tests*", "scripts*"]
|
31
|
-
|
32
|
-
[tool.setuptools.package-data]
|
33
|
-
"diagram_to_iac" = ["config.yaml", "*.yaml"]
|
@@ -1,5 +1,4 @@
|
|
1
1
|
import json
|
2
|
-
import os
|
3
2
|
from pathlib import Path
|
4
3
|
from typing import Dict, Optional
|
5
4
|
|
@@ -8,18 +7,8 @@ class IssueTracker:
|
|
8
7
|
|
9
8
|
def __init__(self, file_path: Optional[str] = None):
|
10
9
|
if file_path is None:
|
11
|
-
|
12
|
-
|
13
|
-
file_path = os.environ['ISSUE_TRACKER_FILE']
|
14
|
-
else:
|
15
|
-
# Check for workspace-based path first (for containers)
|
16
|
-
workspace_base = os.environ.get('WORKSPACE_BASE', '/workspace')
|
17
|
-
if os.path.exists(workspace_base):
|
18
|
-
base_dir = Path(workspace_base)
|
19
|
-
else:
|
20
|
-
# Fallback to package-relative path
|
21
|
-
base_dir = Path(__file__).resolve().parents[3]
|
22
|
-
file_path = base_dir / "data" / "db" / "issue_tracker.json"
|
10
|
+
base_dir = Path(__file__).resolve().parents[3]
|
11
|
+
file_path = base_dir / "data" / "db" / "issue_tracker.json"
|
23
12
|
self.file_path = Path(file_path)
|
24
13
|
self._table: Dict[str, Dict[str, int]] = {}
|
25
14
|
self._load()
|
@@ -87,15 +87,12 @@ class EnhancedMemory(Memory):
|
|
87
87
|
|
88
88
|
# Default path for the persistent agent state JSON file. Allows override via
|
89
89
|
# `AGENT_STATE_FILE` environment variable for testing.
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
return Path(__file__).resolve().parents[3] / "state" / ".agent_state" / "agent_state.json"
|
97
|
-
|
98
|
-
_DEFAULT_AGENT_STATE_PATH = _get_default_agent_state_path()
|
90
|
+
_DEFAULT_AGENT_STATE_PATH = (
|
91
|
+
Path(__file__).resolve().parents[3]
|
92
|
+
/ "state"
|
93
|
+
/ ".agent_state"
|
94
|
+
/ "agent_state.json"
|
95
|
+
)
|
99
96
|
AGENT_STATE_PATH = Path(os.environ.get("AGENT_STATE_FILE", _DEFAULT_AGENT_STATE_PATH))
|
100
97
|
|
101
98
|
|
File without changes
|
@@ -1,7 +1,6 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import json
|
4
|
-
import os
|
5
4
|
from datetime import datetime, timezone
|
6
5
|
from pathlib import Path
|
7
6
|
import threading
|
@@ -12,17 +11,7 @@ class LogBus:
|
|
12
11
|
"""Simple JSONL logging service."""
|
13
12
|
|
14
13
|
def __init__(self, log_dir: str | Path | None = None) -> None:
|
15
|
-
if log_dir
|
16
|
-
self.log_dir = Path(log_dir)
|
17
|
-
else:
|
18
|
-
# Check for workspace-based path first (for containers)
|
19
|
-
workspace_base = os.environ.get('WORKSPACE_BASE', '/workspace')
|
20
|
-
if os.path.exists(workspace_base):
|
21
|
-
self.log_dir = Path(workspace_base) / "logs"
|
22
|
-
else:
|
23
|
-
# Fallback to package-relative path
|
24
|
-
self.log_dir = Path(__file__).resolve().parents[3] / "logs"
|
25
|
-
|
14
|
+
self.log_dir = Path(log_dir) if log_dir else Path(__file__).resolve().parents[3] / "logs"
|
26
15
|
self.log_dir.mkdir(parents=True, exist_ok=True)
|
27
16
|
timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
|
28
17
|
self.log_path = self.log_dir / f"run-{timestamp}.jsonl"
|
@@ -3,14 +3,8 @@ import os
|
|
3
3
|
from openai import OpenAI
|
4
4
|
from anthropic import Anthropic
|
5
5
|
import requests
|
6
|
-
|
7
|
-
|
8
|
-
except ImportError:
|
9
|
-
genai = None
|
10
|
-
try:
|
11
|
-
import googleapiclient.discovery
|
12
|
-
except ImportError:
|
13
|
-
googleapiclient = None
|
6
|
+
import google.generativeai as genai
|
7
|
+
import googleapiclient.discovery
|
14
8
|
from concurrent.futures import ThreadPoolExecutor, TimeoutError
|
15
9
|
|
16
10
|
# Import centralized configuration
|
@@ -51,10 +45,6 @@ def test_openai_api():
|
|
51
45
|
|
52
46
|
def test_gemini_api():
|
53
47
|
try:
|
54
|
-
if genai is None:
|
55
|
-
print("❌ Gemini API error: google-generativeai package not installed.")
|
56
|
-
return False
|
57
|
-
|
58
48
|
google_api_key = os.environ.get("GOOGLE_API_KEY")
|
59
49
|
if not google_api_key:
|
60
50
|
print("❌ Gemini API error: GOOGLE_API_KEY environment variable not set.")
|
@@ -767,25 +767,10 @@ class GitExecutor:
|
|
767
767
|
labels_str = ",".join(gh_input.labels)
|
768
768
|
gh_command += f" --label \"{labels_str}\""
|
769
769
|
|
770
|
-
#
|
770
|
+
# Handle assignees - auto-assign to repository owner or github-copilot if none provided
|
771
771
|
assignees_to_use = gh_input.assignees or []
|
772
|
-
|
773
|
-
|
774
|
-
# If assignees are provided, apply smart prioritization
|
775
|
-
if "github-copilot" in assignees_to_use:
|
776
|
-
# Multiple assignees with Copilot -> assign to Copilot only
|
777
|
-
assignees_to_use = ["github-copilot"]
|
778
|
-
self.logger.info("Multiple assignees detected with @github-copilot - assigning to @github-copilot only")
|
779
|
-
elif len(assignees_to_use) == 1 and assignees_to_use[0] == owner:
|
780
|
-
# Only owner as assignee -> keep as owner
|
781
|
-
self.logger.info(f"Single assignee is repository owner - assigning to @{owner}")
|
782
|
-
elif len(assignees_to_use) > 1 and "github-copilot" not in assignees_to_use:
|
783
|
-
# Multiple assignees without Copilot -> assign to owner only
|
784
|
-
assignees_to_use = [owner]
|
785
|
-
self.logger.info(f"Multiple assignees without @github-copilot - assigning to repository owner @{owner} only")
|
786
|
-
# Single non-owner assignee -> keep as provided
|
787
|
-
else:
|
788
|
-
# No assignees provided - auto-assign with Copilot preference
|
772
|
+
if not assignees_to_use:
|
773
|
+
# Try to assign to @github-copilot first, fallback to repository owner
|
789
774
|
try:
|
790
775
|
# Check if github-copilot exists as a user
|
791
776
|
check_copilot_cmd = "gh api /users/github-copilot"
|
@@ -794,15 +779,15 @@ class GitExecutor:
|
|
794
779
|
|
795
780
|
if check_result.exit_code == 0:
|
796
781
|
assignees_to_use = ["github-copilot"]
|
797
|
-
self.logger.info("
|
782
|
+
self.logger.info("Auto-assigning issue to @github-copilot")
|
798
783
|
else:
|
799
784
|
# Fallback to repository owner
|
800
785
|
assignees_to_use = [owner]
|
801
|
-
self.logger.info(f"
|
786
|
+
self.logger.info(f"Auto-assigning issue to repository owner: @{owner}")
|
802
787
|
except Exception as e:
|
803
788
|
# Fallback to repository owner if check fails
|
804
789
|
assignees_to_use = [owner]
|
805
|
-
self.logger.info(f"Failed to check @github-copilot, assigning to repository owner @{owner}. Error: {e}")
|
790
|
+
self.logger.info(f"Failed to check @github-copilot, assigning to repository owner: @{owner}. Error: {e}")
|
806
791
|
|
807
792
|
if assignees_to_use:
|
808
793
|
assignees_str = ",".join(assignees_to_use)
|
@@ -37,77 +37,44 @@ except ImportError:
|
|
37
37
|
# Path inside container where the encoded YAML is mounted (dev only)
|
38
38
|
_YAML_PATH = pathlib.Path("/run/secrets.yaml")
|
39
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
|
-
"ANTHROPIC_API_KEY",
|
66
|
-
"GROK_API_KEY"
|
67
|
-
]
|
68
|
-
|
69
|
-
return expected_secrets, required_secrets, ai_secrets, full_mapping
|
70
|
-
except Exception as e:
|
71
|
-
print(f"⚠️ Warning: Could not load secret config from config.yaml: {e}")
|
72
|
-
|
73
|
-
# Fallback to hardcoded values
|
74
|
-
expected_secrets = [
|
75
|
-
"DOCKERHUB_API_KEY",
|
76
|
-
"DOCKERHUB_USERNAME",
|
77
|
-
"TF_API_KEY",
|
78
|
-
"PYPI_API_KEY",
|
79
|
-
"OPENAI_API_KEY",
|
80
|
-
"GOOGLE_API_KEY",
|
81
|
-
"ANTHROPIC_API_KEY",
|
82
|
-
"GROK_API_KEY",
|
83
|
-
"REPO_API_KEY"
|
84
|
-
]
|
85
|
-
|
86
|
-
required_secrets = ["REPO_API_KEY"]
|
87
|
-
|
88
|
-
ai_secrets = [
|
89
|
-
"OPENAI_API_KEY",
|
90
|
-
"GOOGLE_API_KEY",
|
91
|
-
"ANTHROPIC_API_KEY",
|
92
|
-
"GROK_API_KEY"
|
93
|
-
]
|
94
|
-
|
95
|
-
secret_mapping = {
|
96
|
-
"REPO_API_KEY": "GITHUB_TOKEN",
|
97
|
-
"TF_API_KEY": "TFE_TOKEN",
|
98
|
-
"DOCKERHUB_API_KEY": "DOCKERHUB_API_KEY",
|
99
|
-
"DOCKERHUB_USERNAME": "DOCKERHUB_USERNAME",
|
100
|
-
"PYPI_API_KEY": "PYPI_API_KEY",
|
101
|
-
"OPENAI_API_KEY": "OPENAI_API_KEY",
|
102
|
-
"GOOGLE_API_KEY": "GOOGLE_API_KEY",
|
103
|
-
"ANTHROPIC_API_KEY": "ANTHROPIC_API_KEY",
|
104
|
-
"GROK_API_KEY": "GROK_API_KEY"
|
105
|
-
}
|
106
|
-
|
107
|
-
return expected_secrets, required_secrets, ai_secrets, secret_mapping
|
40
|
+
# Expected secrets based on secrets_example.yaml
|
41
|
+
EXPECTED_SECRETS = [
|
42
|
+
"DOCKERHUB_API_KEY",
|
43
|
+
"DOCKERHUB_USERNAME",
|
44
|
+
"TF_API_KEY",
|
45
|
+
"PYPI_API_KEY",
|
46
|
+
"OPENAI_API_KEY",
|
47
|
+
"GOOGLE_API_KEY",
|
48
|
+
"ANTHROPIC_API_KEY",
|
49
|
+
"GROK_API_KEY",
|
50
|
+
"REPO_API_KEY"
|
51
|
+
]
|
52
|
+
|
53
|
+
# Required secrets that must be present (others are optional)
|
54
|
+
REQUIRED_SECRETS = [
|
55
|
+
"REPO_API_KEY" # GITHUB_TOKEN is required for repo operations
|
56
|
+
]
|
57
|
+
|
58
|
+
# Optional AI API secrets (at least one should be present for AI functionality)
|
59
|
+
AI_API_SECRETS = [
|
60
|
+
"OPENAI_API_KEY",
|
61
|
+
"GOOGLE_API_KEY",
|
62
|
+
"ANTHROPIC_API_KEY",
|
63
|
+
"GROK_API_KEY"
|
64
|
+
]
|
108
65
|
|
109
|
-
#
|
110
|
-
|
66
|
+
# Map internal secret names to environment variable names
|
67
|
+
SECRET_ENV_MAPPING = {
|
68
|
+
"REPO_API_KEY": "GITHUB_TOKEN",
|
69
|
+
"TF_API_KEY": "TFE_TOKEN",
|
70
|
+
"DOCKERHUB_API_KEY": "DOCKERHUB_API_KEY",
|
71
|
+
"DOCKERHUB_USERNAME": "DOCKERHUB_USERNAME",
|
72
|
+
"PYPI_API_KEY": "PYPI_API_KEY",
|
73
|
+
"OPENAI_API_KEY": "OPENAI_API_KEY",
|
74
|
+
"GOOGLE_API_KEY": "GOOGLE_API_KEY",
|
75
|
+
"ANTHROPIC_API_KEY": "ANTHROPIC_API_KEY",
|
76
|
+
"GROK_API_KEY": "GROK_API_KEY"
|
77
|
+
}
|
111
78
|
|
112
79
|
|
113
80
|
def _decode_b64(enc: str) -> str:
|
@@ -140,35 +107,12 @@ def _is_dev_environment() -> bool:
|
|
140
107
|
)
|
141
108
|
|
142
109
|
|
143
|
-
def _is_ci_environment() -> bool:
|
144
|
-
"""Check if running in CI environment."""
|
145
|
-
return os.environ.get("CI") == "true" or os.environ.get("GITHUB_ACTIONS") == "true"
|
146
|
-
|
147
|
-
|
148
110
|
def _get_env_secrets() -> Dict[str, Optional[str]]:
|
149
111
|
"""Get secrets from environment variables."""
|
150
112
|
env_secrets = {}
|
151
|
-
is_ci = _is_ci_environment()
|
152
|
-
|
153
113
|
for secret_key in EXPECTED_SECRETS:
|
154
114
|
env_name = SECRET_ENV_MAPPING.get(secret_key, secret_key)
|
155
|
-
raw_value =
|
156
|
-
|
157
|
-
if is_ci:
|
158
|
-
# In CI, check for _ENCODED variant first (since that's how they're provided)
|
159
|
-
encoded_env_name = f"{secret_key}_ENCODED" # Use secret_key directly for CI
|
160
|
-
raw_value = os.environ.get(encoded_env_name)
|
161
|
-
|
162
|
-
# If not found with secret_key, try with env_name
|
163
|
-
if raw_value is None:
|
164
|
-
encoded_env_name = f"{env_name}_ENCODED"
|
165
|
-
raw_value = os.environ.get(encoded_env_name)
|
166
|
-
|
167
|
-
# If still not found, try the direct environment variable name
|
168
|
-
if raw_value is None:
|
169
|
-
raw_value = os.environ.get(env_name)
|
170
|
-
|
171
|
-
# If we found a value, process it
|
115
|
+
raw_value = os.environ.get(env_name)
|
172
116
|
if raw_value:
|
173
117
|
# Check if value is already decoded, use as-is; otherwise decode it
|
174
118
|
if (secret_key == "TF_API_KEY" and ".atlasv1." in raw_value) or \
|
@@ -182,7 +126,6 @@ def _get_env_secrets() -> Dict[str, Optional[str]]:
|
|
182
126
|
env_secrets[secret_key] = _decode_b64(raw_value)
|
183
127
|
else:
|
184
128
|
env_secrets[secret_key] = None
|
185
|
-
|
186
129
|
return env_secrets
|
187
130
|
|
188
131
|
|
@@ -3,7 +3,6 @@ README.md
|
|
3
3
|
pyproject.toml
|
4
4
|
src/diagram_to_iac/__init__.py
|
5
5
|
src/diagram_to_iac/cli.py
|
6
|
-
src/diagram_to_iac/config.yaml
|
7
6
|
src/diagram_to_iac/r2d.py
|
8
7
|
src/diagram_to_iac.egg-info/PKG-INFO
|
9
8
|
src/diagram_to_iac.egg-info/SOURCES.txt
|
@@ -18,17 +17,13 @@ src/diagram_to_iac/actions/terraform_agent_entry.py
|
|
18
17
|
src/diagram_to_iac/agents/__init__.py
|
19
18
|
src/diagram_to_iac/agents/demonstrator_langgraph/__init__.py
|
20
19
|
src/diagram_to_iac/agents/demonstrator_langgraph/agent.py
|
21
|
-
src/diagram_to_iac/agents/demonstrator_langgraph/config.yaml
|
22
20
|
src/diagram_to_iac/agents/git_langgraph/__init__.py
|
23
21
|
src/diagram_to_iac/agents/git_langgraph/agent.py
|
24
|
-
src/diagram_to_iac/agents/git_langgraph/config.yaml
|
25
22
|
src/diagram_to_iac/agents/git_langgraph/pr.py
|
26
23
|
src/diagram_to_iac/agents/hello_langgraph/__init__.py
|
27
24
|
src/diagram_to_iac/agents/hello_langgraph/agent.py
|
28
|
-
src/diagram_to_iac/agents/hello_langgraph/config.yaml
|
29
25
|
src/diagram_to_iac/agents/policy_agent/__init__.py
|
30
26
|
src/diagram_to_iac/agents/policy_agent/agent.py
|
31
|
-
src/diagram_to_iac/agents/policy_agent/config.yaml
|
32
27
|
src/diagram_to_iac/agents/policy_agent/integration_example.py
|
33
28
|
src/diagram_to_iac/agents/policy_agent/tools/__init__.py
|
34
29
|
src/diagram_to_iac/agents/policy_agent/tools/tfsec_tool.py
|
@@ -37,7 +32,6 @@ src/diagram_to_iac/agents/shell_langgraph/agent.py
|
|
37
32
|
src/diagram_to_iac/agents/shell_langgraph/detector.py
|
38
33
|
src/diagram_to_iac/agents/supervisor_langgraph/__init__.py
|
39
34
|
src/diagram_to_iac/agents/supervisor_langgraph/agent.py
|
40
|
-
src/diagram_to_iac/agents/supervisor_langgraph/config.yaml
|
41
35
|
src/diagram_to_iac/agents/supervisor_langgraph/demonstrator.py
|
42
36
|
src/diagram_to_iac/agents/supervisor_langgraph/github_listener.py
|
43
37
|
src/diagram_to_iac/agents/supervisor_langgraph/guards.py
|
@@ -45,7 +39,6 @@ src/diagram_to_iac/agents/supervisor_langgraph/pat_loop.py
|
|
45
39
|
src/diagram_to_iac/agents/supervisor_langgraph/router.py
|
46
40
|
src/diagram_to_iac/agents/terraform_langgraph/__init__.py
|
47
41
|
src/diagram_to_iac/agents/terraform_langgraph/agent.py
|
48
|
-
src/diagram_to_iac/agents/terraform_langgraph/config.yaml
|
49
42
|
src/diagram_to_iac/agents/terraform_langgraph/parser.py
|
50
43
|
src/diagram_to_iac/core/__init__.py
|
51
44
|
src/diagram_to_iac/core/agent_base.py
|
@@ -60,14 +53,12 @@ src/diagram_to_iac/services/__init__.py
|
|
60
53
|
src/diagram_to_iac/services/commenter.py
|
61
54
|
src/diagram_to_iac/services/observability.py
|
62
55
|
src/diagram_to_iac/services/step_summary.py
|
63
|
-
src/diagram_to_iac/templates/issue_frontmatter.yml
|
64
56
|
src/diagram_to_iac/tools/__init__.py
|
65
57
|
src/diagram_to_iac/tools/api_utils.py
|
66
58
|
src/diagram_to_iac/tools/sec_utils.py
|
67
59
|
src/diagram_to_iac/tools/text_utils.py
|
68
60
|
src/diagram_to_iac/tools/git/__init__.py
|
69
61
|
src/diagram_to_iac/tools/git/git.py
|
70
|
-
src/diagram_to_iac/tools/git/git_config.yaml
|
71
62
|
src/diagram_to_iac/tools/hello/__init__.py
|
72
63
|
src/diagram_to_iac/tools/hello/cal_utils.py
|
73
64
|
src/diagram_to_iac/tools/hello/text_utils.py
|
@@ -80,7 +71,5 @@ src/diagram_to_iac/tools/llm_utils/openai_driver.py
|
|
80
71
|
src/diagram_to_iac/tools/llm_utils/router.py
|
81
72
|
src/diagram_to_iac/tools/shell/__init__.py
|
82
73
|
src/diagram_to_iac/tools/shell/shell.py
|
83
|
-
src/diagram_to_iac/tools/shell/shell_config.yaml
|
84
74
|
src/diagram_to_iac/tools/tf/terraform.py
|
85
|
-
src/diagram_to_iac/tools/tf/terraform_config.yaml
|
86
75
|
tests/test_devops_in_a_box.py
|
@@ -1,12 +1,6 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
2
|
"""
|
3
3
|
Test script to validate DevOps-in-a-Box R2D functionality
|
4
|
-
This is an integration test that validates the entire R2D system including:
|
5
|
-
- CLI functionality
|
6
|
-
- Container build capability
|
7
|
-
- GitHub Action and Workflow definitions
|
8
|
-
|
9
|
-
This test is more suitable for integration/end-to-end testing rather than unit testing.
|
10
4
|
"""
|
11
5
|
|
12
6
|
import sys
|
@@ -14,10 +8,7 @@ import os
|
|
14
8
|
import subprocess
|
15
9
|
import json
|
16
10
|
from pathlib import Path
|
17
|
-
import pytest
|
18
|
-
|
19
11
|
|
20
|
-
@pytest.mark.integration
|
21
12
|
def test_r2d_cli():
|
22
13
|
"""Test the R2D CLI functionality"""
|
23
14
|
print("🧪 Testing DevOps-in-a-Box R2D CLI...")
|
@@ -64,9 +55,8 @@ def test_r2d_cli():
|
|
64
55
|
# Test passed successfully
|
65
56
|
print("✅ All R2D CLI tests passed")
|
66
57
|
|
67
|
-
@pytest.mark.integration
|
68
58
|
def test_container_build():
|
69
|
-
"""Test container build locally
|
59
|
+
"""Test container build locally"""
|
70
60
|
print("\n🐳 Testing DevOps-in-a-Box Container Build...")
|
71
61
|
|
72
62
|
dockerfile_path = Path(__file__).parent.parent / ".github/actions/r2d/Dockerfile"
|
@@ -77,81 +67,31 @@ def test_container_build():
|
|
77
67
|
|
78
68
|
print("✅ Dockerfile exists")
|
79
69
|
|
80
|
-
# Check if
|
81
|
-
is_ci = os.environ.get('GITHUB_ACTIONS') == 'true' or os.environ.get('CI') == 'true'
|
82
|
-
|
83
|
-
# Check if Docker is available
|
84
|
-
docker_available = False
|
70
|
+
# Check if Docker is available and working
|
85
71
|
try:
|
86
|
-
|
87
|
-
if
|
88
|
-
print(f"✅ Docker available: {result.stdout.strip()}")
|
89
|
-
docker_available = True
|
90
|
-
else:
|
72
|
+
version_result = subprocess.run(["docker", "--version"], capture_output=True, text=True)
|
73
|
+
if version_result.returncode != 0:
|
91
74
|
print("⚠️ Docker not available - skipping container build test")
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
if not docker_available:
|
96
|
-
print("✅ Container build test skipped (Docker not available)")
|
97
|
-
return
|
98
|
-
|
99
|
-
# In CI environment, be more conservative about Docker builds
|
100
|
-
if is_ci:
|
101
|
-
print("🔍 CI environment detected - performing lightweight container validation...")
|
102
|
-
|
103
|
-
# Just validate Dockerfile syntax and basic structure
|
104
|
-
try:
|
105
|
-
with open(dockerfile_path, 'r') as f:
|
106
|
-
dockerfile_content = f.read()
|
107
|
-
|
108
|
-
# Basic Dockerfile validation
|
109
|
-
required_instructions = ['FROM', 'COPY', 'RUN', 'ENTRYPOINT']
|
110
|
-
for instruction in required_instructions:
|
111
|
-
if instruction not in dockerfile_content:
|
112
|
-
print(f"⚠️ Dockerfile missing {instruction} instruction")
|
113
|
-
|
114
|
-
print("✅ Dockerfile structure validation passed")
|
115
|
-
|
116
|
-
# Try a very basic Docker build check (dry-run style)
|
117
|
-
try:
|
118
|
-
result = subprocess.run([
|
119
|
-
"docker", "build",
|
120
|
-
"-t", "diagram-to-iac-r2d:test",
|
121
|
-
"-f", str(dockerfile_path),
|
122
|
-
".",
|
123
|
-
"--build-arg", "PACKAGE_VERSION=test",
|
124
|
-
"--dry-run" # This might not work on all Docker versions
|
125
|
-
], capture_output=True, text=True, cwd=dockerfile_path.parent, timeout=30)
|
126
|
-
|
127
|
-
if result.returncode == 0:
|
128
|
-
print("✅ Docker build dry-run successful")
|
129
|
-
else:
|
130
|
-
# If dry-run fails, just validate the command would be valid
|
131
|
-
print("ℹ️ Docker build validation completed (dry-run not supported)")
|
132
|
-
|
133
|
-
except subprocess.TimeoutExpired:
|
134
|
-
print("⚠️ Docker build check timed out in CI - this is expected")
|
135
|
-
except Exception as e:
|
136
|
-
print(f"ℹ️ Docker build check skipped in CI: {e}")
|
75
|
+
print("✅ Container build test skipped (Docker not available)")
|
76
|
+
return
|
137
77
|
|
138
|
-
|
78
|
+
print(f"✅ Docker available: {version_result.stdout.strip()}")
|
79
|
+
|
80
|
+
# Check if Docker daemon is running
|
81
|
+
info_result = subprocess.run(["docker", "info"], capture_output=True, text=True)
|
82
|
+
if info_result.returncode != 0:
|
83
|
+
print("⚠️ Docker daemon not running - skipping container build test")
|
84
|
+
print("✅ Container build test skipped (Docker daemon not running)")
|
139
85
|
return
|
140
86
|
|
141
|
-
|
142
|
-
print(f"❌ Dockerfile validation failed: {e}")
|
143
|
-
assert False, f"Dockerfile validation failed: {e}"
|
144
|
-
|
145
|
-
# Full container build test for local development
|
146
|
-
try:
|
147
|
-
print("🔨 Testing full container build (local development mode)...")
|
87
|
+
print("🔨 Testing container build (this may take a few minutes)...")
|
148
88
|
result = subprocess.run([
|
149
89
|
"docker", "build",
|
150
90
|
"-t", "diagram-to-iac-r2d:test",
|
151
91
|
"-f", str(dockerfile_path),
|
152
92
|
".",
|
153
|
-
"--build-arg", "PACKAGE_VERSION=
|
154
|
-
], capture_output=True, text=True, cwd=dockerfile_path.parent
|
93
|
+
"--build-arg", "PACKAGE_VERSION=latest"
|
94
|
+
], capture_output=True, text=True, cwd=dockerfile_path.parent)
|
155
95
|
|
156
96
|
if result.returncode == 0:
|
157
97
|
print("✅ Container builds successfully")
|
@@ -161,7 +101,7 @@ def test_container_build():
|
|
161
101
|
"docker", "run", "--rm",
|
162
102
|
"diagram-to-iac-r2d:test",
|
163
103
|
"--help"
|
164
|
-
], capture_output=True, text=True
|
104
|
+
], capture_output=True, text=True)
|
165
105
|
|
166
106
|
if test_result.returncode == 0:
|
167
107
|
print("✅ Container runs successfully")
|
@@ -172,30 +112,44 @@ def test_container_build():
|
|
172
112
|
subprocess.run(["docker", "rmi", "diagram-to-iac-r2d:test"], capture_output=True)
|
173
113
|
|
174
114
|
else:
|
175
|
-
|
176
|
-
|
177
|
-
if
|
178
|
-
|
115
|
+
# Check if this is a known environment issue rather than a code issue
|
116
|
+
stderr_lower = result.stderr.lower()
|
117
|
+
if any(keyword in stderr_lower for keyword in [
|
118
|
+
"building with \"default\" instance",
|
119
|
+
"no space left on device",
|
120
|
+
"permission denied",
|
121
|
+
"cannot connect to the docker daemon",
|
122
|
+
"docker daemon is not running",
|
123
|
+
"insufficient memory",
|
124
|
+
"network"
|
125
|
+
]):
|
126
|
+
print(f"⚠️ Container build failed due to environment issue: {result.stderr[:200]}...")
|
127
|
+
print("✅ Container build test skipped (environment limitations)")
|
128
|
+
return
|
179
129
|
else:
|
180
|
-
print("
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
130
|
+
print(f"❌ Container build failed: {result.stderr}")
|
131
|
+
assert False, f"Container build failed: {result.stderr}"
|
132
|
+
|
133
|
+
except FileNotFoundError:
|
134
|
+
print("⚠️ Docker not installed - skipping container build test")
|
135
|
+
print("✅ Container build test skipped (Docker not installed)")
|
186
136
|
except Exception as e:
|
187
137
|
print(f"❌ Container build test failed: {e}")
|
188
|
-
# Don't fail the test if Docker is not available
|
189
|
-
if
|
190
|
-
|
191
|
-
|
192
|
-
|
138
|
+
# Don't fail the test if Docker is not available or has environment issues
|
139
|
+
if any(phrase in str(e).lower() for phrase in [
|
140
|
+
"no such file or directory: 'docker'",
|
141
|
+
"permission denied",
|
142
|
+
"cannot connect",
|
143
|
+
"daemon"
|
144
|
+
]):
|
145
|
+
print("✅ Container build test skipped (Docker environment issue)")
|
193
146
|
else:
|
194
|
-
|
147
|
+
# Only fail for actual code/test issues, not environment issues
|
148
|
+
print("⚠️ Container build test encountered unexpected error - skipping")
|
149
|
+
print("✅ Container build test skipped (unexpected environment issue)")
|
195
150
|
|
196
151
|
print("✅ Container build tests completed successfully")
|
197
152
|
|
198
|
-
@pytest.mark.integration
|
199
153
|
def test_github_action_definition():
|
200
154
|
"""Test GitHub Action definition"""
|
201
155
|
print("\n🎬 Testing GitHub Action Definition...")
|
@@ -246,7 +200,6 @@ def test_github_action_definition():
|
|
246
200
|
|
247
201
|
print("✅ GitHub Action definition tests passed")
|
248
202
|
|
249
|
-
@pytest.mark.integration
|
250
203
|
def test_workflow_definition():
|
251
204
|
"""Test GitHub workflow definition"""
|
252
205
|
print("\n⚙️ Testing GitHub Workflow Definition...")
|
diagram_to_iac-1.8.0/MANIFEST.in
DELETED