agent-starter-pack 0.9.2__py3-none-any.whl → 0.10.1__py3-none-any.whl

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.
Files changed (57) hide show
  1. {agent_starter_pack-0.9.2.dist-info → agent_starter_pack-0.10.1.dist-info}/METADATA +2 -2
  2. {agent_starter_pack-0.9.2.dist-info → agent_starter_pack-0.10.1.dist-info}/RECORD +54 -52
  3. agents/adk_base/.template/templateconfig.yaml +1 -1
  4. agents/adk_gemini_fullstack/.template/templateconfig.yaml +2 -1
  5. agents/agentic_rag/.template/templateconfig.yaml +1 -1
  6. agents/agentic_rag/README.md +1 -1
  7. agents/live_api/tests/integration/test_server_e2e.py +7 -1
  8. agents/live_api/tests/unit/test_server.py +2 -1
  9. llm.txt +3 -2
  10. src/base_template/Makefile +4 -3
  11. src/base_template/README.md +7 -2
  12. src/base_template/deployment/README.md +6 -121
  13. src/base_template/deployment/terraform/github.tf +284 -0
  14. src/base_template/deployment/terraform/providers.tf +5 -0
  15. src/base_template/deployment/terraform/variables.tf +40 -1
  16. src/base_template/deployment/terraform/vars/env.tfvars +7 -1
  17. src/base_template/deployment/terraform/{% if cookiecutter.cicd_runner == 'github_actions' %}wif.tf{% else %}unused_wif.tf{% endif %} +43 -0
  18. src/base_template/deployment/terraform/{build_triggers.tf → {% if cookiecutter.cicd_runner == 'google_cloud_build' %}build_triggers.tf{% else %}unused_build_triggers.tf{% endif %} } +33 -18
  19. src/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/deploy-to-prod.yaml +114 -0
  20. src/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/pr_checks.yaml +65 -0
  21. src/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/staging.yaml +170 -0
  22. src/base_template/{deployment/cd/deploy-to-prod.yaml → {% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/deploy-to-prod.yaml } +7 -7
  23. src/base_template/{deployment/cd/staging.yaml → {% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/staging.yaml } +7 -7
  24. src/cli/commands/create.py +149 -4
  25. src/cli/commands/list.py +1 -1
  26. src/cli/commands/setup_cicd.py +293 -299
  27. src/cli/utils/cicd.py +19 -7
  28. src/cli/utils/remote_template.py +4 -4
  29. src/cli/utils/template.py +67 -19
  30. src/deployment_targets/cloud_run/app/server.py +19 -0
  31. src/deployment_targets/cloud_run/deployment/terraform/dev/service.tf +4 -3
  32. src/deployment_targets/cloud_run/deployment/terraform/service.tf +4 -3
  33. src/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +35 -0
  34. src/deployment_targets/cloud_run/tests/load_test/README.md +1 -1
  35. src/frontends/live_api_react/frontend/package-lock.json +19 -16
  36. src/frontends/streamlit/frontend/side_bar.py +1 -1
  37. src/frontends/streamlit/frontend/utils/chat_utils.py +1 -1
  38. src/frontends/streamlit/frontend/utils/local_chat_history.py +2 -2
  39. src/resources/docs/adk-cheatsheet.md +1 -1
  40. src/resources/locks/uv-adk_base-agent_engine.lock +164 -131
  41. src/resources/locks/uv-adk_base-cloud_run.lock +177 -144
  42. src/resources/locks/uv-adk_gemini_fullstack-agent_engine.lock +164 -131
  43. src/resources/locks/uv-adk_gemini_fullstack-cloud_run.lock +177 -144
  44. src/resources/locks/uv-agentic_rag-agent_engine.lock +223 -190
  45. src/resources/locks/uv-agentic_rag-cloud_run.lock +251 -218
  46. src/resources/locks/uv-crewai_coding_crew-agent_engine.lock +315 -485
  47. src/resources/locks/uv-crewai_coding_crew-cloud_run.lock +358 -531
  48. src/resources/locks/uv-langgraph_base_react-agent_engine.lock +281 -249
  49. src/resources/locks/uv-langgraph_base_react-cloud_run.lock +323 -290
  50. src/resources/locks/uv-live_api-cloud_run.lock +350 -327
  51. src/resources/setup_cicd/cicd_variables.tf +0 -41
  52. src/resources/setup_cicd/github.tf +0 -87
  53. src/resources/setup_cicd/providers.tf +0 -39
  54. {agent_starter_pack-0.9.2.dist-info → agent_starter_pack-0.10.1.dist-info}/WHEEL +0 -0
  55. {agent_starter_pack-0.9.2.dist-info → agent_starter_pack-0.10.1.dist-info}/entry_points.txt +0 -0
  56. {agent_starter_pack-0.9.2.dist-info → agent_starter_pack-0.10.1.dist-info}/licenses/LICENSE +0 -0
  57. /src/base_template/{deployment/ci → {% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}}/pr_checks.yaml +0 -0
@@ -14,7 +14,6 @@
14
14
 
15
15
  import logging
16
16
  import re
17
- import shutil
18
17
  import subprocess
19
18
  import sys
20
19
  import tempfile
@@ -26,14 +25,10 @@ import click
26
25
  from rich.console import Console
27
26
 
28
27
  from src.cli.utils.cicd import (
29
- E2EDeployment,
30
28
  ProjectConfig,
31
29
  create_github_connection,
32
- create_github_repository,
33
- ensure_apis_enabled,
34
30
  handle_github_authentication,
35
31
  is_github_authenticated,
36
- print_cicd_summary,
37
32
  run_command,
38
33
  )
39
34
 
@@ -86,6 +81,66 @@ def check_gh_cli_installed() -> bool:
86
81
  return False
87
82
 
88
83
 
84
+ def check_github_scopes(cicd_runner: str) -> None:
85
+ """Check if GitHub CLI has required scopes for the CI/CD runner.
86
+
87
+ Args:
88
+ cicd_runner: Either 'github_actions' or 'google_cloud_build'
89
+
90
+ Raises:
91
+ click.ClickException: If required scopes are missing
92
+ """
93
+ try:
94
+ # Get scopes from gh auth status
95
+ result = run_command(["gh", "auth", "status"], capture_output=True, check=True)
96
+
97
+ # Parse scopes from the output
98
+ scopes = []
99
+ for line in result.stdout.split("\n"):
100
+ if "Token scopes:" in line:
101
+ # Extract scopes from line like "- Token scopes: 'gist', 'read:org', 'repo', 'workflow'"
102
+ scopes_part = line.split("Token scopes:")[1].strip()
103
+ # Remove quotes and split by comma
104
+ scopes = [
105
+ s.strip().strip("'\"") for s in scopes_part.split(",") if s.strip()
106
+ ]
107
+ break
108
+
109
+ # Define required scopes based on CI/CD runner
110
+ if cicd_runner == "github_actions":
111
+ required_scopes = ["repo", "workflow"]
112
+ missing_scopes = [scope for scope in required_scopes if scope not in scopes]
113
+
114
+ if missing_scopes:
115
+ console.print(
116
+ f"❌ Missing required GitHub scopes: {', '.join(missing_scopes)}",
117
+ style="bold red",
118
+ )
119
+ console.print("To fix this: gh auth login --scopes repo,workflow")
120
+ raise click.ClickException(
121
+ "GitHub CLI authentication lacks required scopes"
122
+ )
123
+
124
+ elif cicd_runner == "google_cloud_build":
125
+ required_scopes = ["repo"]
126
+ missing_scopes = [scope for scope in required_scopes if scope not in scopes]
127
+
128
+ if missing_scopes:
129
+ console.print(
130
+ f"❌ Missing required GitHub scopes: {', '.join(missing_scopes)}",
131
+ style="bold red",
132
+ )
133
+ console.print("To fix this: gh auth login --scopes repo")
134
+ raise click.ClickException(
135
+ "GitHub CLI authentication lacks required scopes"
136
+ )
137
+
138
+ console.print("✅ GitHub CLI scopes verified")
139
+
140
+ except subprocess.CalledProcessError:
141
+ console.print("⚠️ Could not verify GitHub CLI scopes", style="yellow")
142
+
143
+
89
144
  def prompt_gh_cli_installation() -> None:
90
145
  """Display instructions for installing GitHub CLI and exit."""
91
146
  console.print("\n❌ GitHub CLI not found", style="bold red")
@@ -102,7 +157,7 @@ def setup_git_repository(config: ProjectConfig) -> str:
102
157
  config: Project configuration containing repository details
103
158
 
104
159
  Returns:
105
- str: GitHub username of the authenticated user
160
+ str: Repository owner from the config
106
161
  """
107
162
  console.print("\n🔧 Setting up Git repository...")
108
163
 
@@ -111,27 +166,31 @@ def setup_git_repository(config: ProjectConfig) -> str:
111
166
  run_command(["git", "init", "-b", "main"])
112
167
  console.print("✅ Git repository initialized")
113
168
 
114
- # Get current GitHub username for the remote URL
115
- result = run_command(["gh", "api", "user", "--jq", ".login"], capture_output=True)
116
- github_username = result.stdout.strip()
117
-
118
169
  # Add remote if it doesn't exist
170
+ remote_url = (
171
+ f"https://github.com/{config.repository_owner}/{config.repository_name}.git"
172
+ )
119
173
  try:
120
174
  run_command(
121
175
  ["git", "remote", "get-url", "origin"], capture_output=True, check=True
122
176
  )
123
177
  console.print("✅ Git remote already configured")
124
178
  except subprocess.CalledProcessError:
125
- remote_url = (
126
- f"https://github.com/{github_username}/{config.repository_name}.git"
127
- )
128
- run_command(["git", "remote", "add", "origin", remote_url])
129
- console.print(f"✅ Added git remote: {remote_url}")
179
+ try:
180
+ run_command(
181
+ ["git", "remote", "add", "origin", remote_url],
182
+ capture_output=True,
183
+ check=True,
184
+ )
185
+ console.print(f"✅ Added git remote: {remote_url}")
186
+ except subprocess.CalledProcessError as e:
187
+ console.print(f"❌ Failed to add git remote: {e}", style="bold red")
188
+ raise click.ClickException(f"Failed to add git remote: {e}") from e
130
189
 
131
190
  console.print(
132
191
  "\n💡 Tip: Don't forget to commit and push your changes to the repository!"
133
192
  )
134
- return github_username
193
+ return config.repository_owner
135
194
 
136
195
 
137
196
  def prompt_for_git_provider() -> str:
@@ -163,7 +222,7 @@ def update_build_triggers(tf_dir: Path) -> None:
163
222
  """Update build triggers configuration."""
164
223
  build_triggers_path = tf_dir / "build_triggers.tf"
165
224
  if build_triggers_path.exists():
166
- with open(build_triggers_path) as f:
225
+ with open(build_triggers_path, encoding="utf-8") as f:
167
226
  content = f.read()
168
227
 
169
228
  # Add repository dependency to all trigger resources
@@ -178,7 +237,7 @@ def update_build_triggers(tf_dir: Path) -> None:
178
237
  "repository = google_cloudbuildv2_repository.repo.id",
179
238
  )
180
239
 
181
- with open(build_triggers_path, "w") as f:
240
+ with open(build_triggers_path, "w", encoding="utf-8") as f:
182
241
  f.write(modified_content)
183
242
 
184
243
  console.print("✅ Updated build triggers with repository dependency")
@@ -191,7 +250,7 @@ def prompt_for_repository_details(
191
250
  # Get current GitHub username as default owner
192
251
  result = run_command(["gh", "api", "user", "--jq", ".login"], capture_output=True)
193
252
  default_owner = result.stdout.strip()
194
- repository_exists = False
253
+ create_repository = False
195
254
 
196
255
  if not (repository_name and repository_owner):
197
256
  console.print("\n📦 Repository Configuration", style="bold blue")
@@ -204,9 +263,10 @@ def prompt_for_repository_details(
204
263
  )
205
264
  if choice == "1":
206
265
  # New repository
266
+ create_repository = True
207
267
  if not repository_name:
208
268
  # Get project name from pyproject.toml
209
- with open("pyproject.toml") as f:
269
+ with open("pyproject.toml", encoding="utf-8") as f:
210
270
  for line in f:
211
271
  if line.strip().startswith("name ="):
212
272
  default_name = line.split("=")[1].strip().strip("\"'")
@@ -222,7 +282,7 @@ def prompt_for_repository_details(
222
282
  )
223
283
  else:
224
284
  # Existing repository
225
- repository_exists = True
285
+ create_repository = False
226
286
  while True:
227
287
  repo_url = click.prompt(
228
288
  "Enter existing repository URL (e.g., https://github.com/owner/repo)"
@@ -242,7 +302,7 @@ def prompt_for_repository_details(
242
302
 
243
303
  if repository_name is None or repository_owner is None:
244
304
  raise ValueError("Repository name and owner must be provided")
245
- return repository_name, repository_owner, repository_exists
305
+ return repository_name, repository_owner, create_repository
246
306
 
247
307
 
248
308
  def setup_terraform_backend(
@@ -294,7 +354,7 @@ def setup_terraform_backend(
294
354
  }}
295
355
  }}
296
356
  '''
297
- with open(backend_file, "w") as f:
357
+ with open(backend_file, "w", encoding="utf-8") as f:
298
358
  f.write(backend_content)
299
359
 
300
360
  console.print(
@@ -313,7 +373,7 @@ def create_or_update_secret(secret_id: str, secret_value: str, project_id: str)
313
373
  Raises:
314
374
  subprocess.CalledProcessError: If secret creation/update fails
315
375
  """
316
- with tempfile.NamedTemporaryFile(mode="w") as temp_file:
376
+ with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as temp_file:
317
377
  temp_file.write(secret_value)
318
378
  temp_file.flush()
319
379
 
@@ -379,11 +439,6 @@ console = Console()
379
439
  "--github-app-installation-id",
380
440
  help="GitHub App Installation ID for programmatic auth",
381
441
  )
382
- @click.option(
383
- "--git-provider",
384
- type=click.Choice(["github"]),
385
- help="Git provider to use (currently only GitHub is supported)",
386
- )
387
442
  @click.option(
388
443
  "--local-state",
389
444
  is_flag=True,
@@ -397,10 +452,10 @@ console = Console()
397
452
  help="Skip confirmation prompts and proceed automatically",
398
453
  )
399
454
  @click.option(
400
- "--repository-exists",
455
+ "--create-repository",
401
456
  is_flag=True,
402
457
  default=False,
403
- help="Flag indicating if the repository already exists",
458
+ help="Flag indicating whether to create a new repository",
404
459
  )
405
460
  @backoff.on_exception(
406
461
  backoff.expo,
@@ -419,11 +474,10 @@ def setup_cicd(
419
474
  host_connection_name: str | None,
420
475
  github_pat: str | None,
421
476
  github_app_installation_id: str | None,
422
- git_provider: str | None,
423
477
  local_state: bool,
424
478
  debug: bool,
425
479
  auto_approve: bool,
426
- repository_exists: bool,
480
+ create_repository: bool,
427
481
  ) -> None:
428
482
  """Set up CI/CD infrastructure using Terraform."""
429
483
 
@@ -509,99 +563,123 @@ def setup_cicd(
509
563
  logging.basicConfig(level=logging.DEBUG)
510
564
  console.print("> Debug mode enabled")
511
565
 
512
- # Set git provider through prompt if not provided
513
- if not git_provider:
514
- git_provider = prompt_for_git_provider()
515
-
516
- # Check GitHub authentication if GitHub is selected
517
- if git_provider == "github" and not (github_pat and github_app_installation_id):
518
- # Check if GitHub CLI is installed
519
- if git_provider == "github" or git_provider is None:
520
- if not check_gh_cli_installed():
521
- prompt_gh_cli_installation()
522
- if not is_github_authenticated():
523
- console.print("\n⚠️ Not authenticated with GitHub CLI", style="yellow")
524
- handle_github_authentication()
525
- else:
526
- console.print("✅ GitHub CLI authentication verified")
566
+ # Auto-detect CI/CD runner based on Terraform files
567
+ tf_dir = Path("deployment/terraform")
568
+ is_github_actions = (tf_dir / "wif.tf").exists() and (tf_dir / "github.tf").exists()
569
+ cicd_runner = "github_actions" if is_github_actions else "google_cloud_build"
570
+ if debug:
571
+ logging.debug(f"Detected CI/CD runner: {cicd_runner}")
572
+
573
+ # Ensure GitHub CLI is available and authenticated
574
+ if not check_gh_cli_installed():
575
+ prompt_gh_cli_installation()
576
+ if not is_github_authenticated():
577
+ console.print("\n⚠️ Not authenticated with GitHub CLI", style="yellow")
578
+ handle_github_authentication()
579
+ else:
580
+ console.print("✅ GitHub CLI authentication verified")
581
+
582
+ # Check if GitHub CLI has required scopes for the CI/CD runner
583
+ console.print("\n🔍 Checking GitHub CLI scopes...")
584
+ check_github_scopes(cicd_runner)
527
585
 
528
- # Only prompt for repository details if not provided via CLI
586
+ # Gather repository details if not provided
529
587
  if not (repository_name and repository_owner):
530
- repository_name, repository_owner, repository_exists = (
531
- prompt_for_repository_details(repository_name, repository_owner)
532
- )
588
+ if auto_approve:
589
+ # Auto-generate repository details when auto-approve is used
590
+ if not repository_owner:
591
+ repository_owner = run_command(
592
+ ["gh", "api", "user", "--jq", ".login"], capture_output=True
593
+ ).stdout.strip()
594
+ if not repository_name:
595
+ # Get project name from pyproject.toml or generate one
596
+ try:
597
+ with open("pyproject.toml", encoding="utf-8") as f:
598
+ for line in f:
599
+ if line.strip().startswith("name ="):
600
+ repository_name = (
601
+ line.split("=")[1].strip().strip("\"'")
602
+ )
603
+ break
604
+ else:
605
+ repository_name = f"genai-app-{int(time.time())}"
606
+ except FileNotFoundError:
607
+ repository_name = f"genai-app-{int(time.time())}"
608
+ console.print(
609
+ f"✅ Auto-generated repository: {repository_owner}/{repository_name}"
610
+ )
611
+ # Keep the CLI argument value for create_repository
612
+ else:
613
+ repository_name, repository_owner, create_repository = (
614
+ prompt_for_repository_details(repository_name, repository_owner)
615
+ )
616
+
617
+ assert repository_name is not None, "Repository name must be provided"
618
+ assert repository_owner is not None, "Repository owner must be provided"
619
+
533
620
  # Set default host connection name if not provided
534
621
  if not host_connection_name:
535
622
  host_connection_name = f"git-{repository_name}"
536
- # Check and enable required APIs regardless of auth method
537
- required_apis = ["secretmanager.googleapis.com", "cloudbuild.googleapis.com"]
538
- ensure_apis_enabled(cicd_project, required_apis)
539
623
 
540
- # Create GitHub connection and repository if not using PAT authentication
624
+ # For Cloud Build, determine mode and handle connection creation
541
625
  oauth_token_secret_id = None
626
+ # Track original repository state for Terraform (before we create it)
627
+ terraform_create_repository = create_repository
628
+
629
+ if cicd_runner == "google_cloud_build":
630
+ # Determine if we're in programmatic or interactive mode based on provided credentials
631
+ detected_mode = (
632
+ "programmatic"
633
+ if github_pat and github_app_installation_id
634
+ else "interactive"
635
+ )
542
636
 
543
- # Determine if we're in programmatic or interactive mode based on provided credentials
544
- detected_mode = (
545
- "programmatic" if github_pat and github_app_installation_id else "interactive"
546
- )
547
-
548
- if git_provider == "github" and detected_mode == "interactive":
549
- # First create the repository since we're in interactive mode
550
- if not repository_exists:
551
- create_github_repository(repository_owner, repository_name)
637
+ if detected_mode == "interactive":
638
+ console.print(
639
+ "\n🔗 Interactive mode: Creating GitHub connection using gcloud CLI..."
640
+ )
552
641
 
553
- # Then create the connection
554
- oauth_token_secret_id, github_app_installation_id = create_github_connection(
555
- project_id=cicd_project, region=region, connection_name=host_connection_name
556
- )
557
- repository_exists = True
558
- elif git_provider == "github" and detected_mode == "programmatic":
559
- oauth_token_secret_id = "github-pat"
560
-
561
- if github_pat is None:
562
- raise ValueError("GitHub PAT is required for programmatic mode")
563
-
564
- # Create the GitHub PAT secret if provided
565
- console.print("\n🔐 Creating/updating GitHub PAT secret...")
566
- create_or_update_secret(
567
- secret_id=oauth_token_secret_id,
568
- secret_value=github_pat,
569
- project_id=cicd_project,
570
- )
642
+ # Create connection using gcloud CLI (interactive approach)
643
+ try:
644
+ oauth_token_secret_id, github_app_installation_id = (
645
+ create_github_connection(
646
+ project_id=cicd_project,
647
+ region=region,
648
+ connection_name=host_connection_name,
649
+ )
650
+ )
651
+ create_cb_connection = (
652
+ True # Connection created by gcloud, Terraform will reference it
653
+ )
654
+ console.print(" GitHub connection created successfully")
655
+ except Exception as e:
656
+ console.print(
657
+ f"❌ Failed to create GitHub connection: {e}", style="red"
658
+ )
659
+ raise
571
660
 
572
- else:
573
- # Unsupported git provider
574
- console.print("⚠️ Only GitHub is currently supported.", style="bold yellow")
575
- raise ValueError("Unsupported git provider")
661
+ elif detected_mode == "programmatic":
662
+ console.print(
663
+ "\n🔐 Programmatic mode: Creating GitHub PAT secret using gcloud CLI..."
664
+ )
576
665
 
577
- console.print("\n📦 Starting CI/CD Infrastructure Setup", style="bold blue")
578
- console.print("=====================================")
666
+ oauth_token_secret_id = "github-pat" # Use fixed secret ID like main branch
579
667
 
580
- config = ProjectConfig(
581
- dev_project_id=dev_project,
582
- staging_project_id=staging_project,
583
- prod_project_id=prod_project,
584
- cicd_project_id=cicd_project,
585
- region=region,
586
- repository_name=repository_name,
587
- repository_owner=repository_owner,
588
- host_connection_name=host_connection_name,
589
- agent="", # Not needed for CICD setup
590
- deployment_target="", # Not needed for CICD setup
591
- github_pat=github_pat,
592
- github_app_installation_id=github_app_installation_id,
593
- git_provider=git_provider,
594
- repository_exists=repository_exists,
595
- )
668
+ if github_pat is None:
669
+ raise ValueError("GitHub PAT is required for programmatic mode")
596
670
 
597
- tf_dir = Path("deployment/terraform")
671
+ # Create GitHub PAT secret using gcloud CLI instead of Terraform
672
+ console.print("📝 Creating GitHub PAT secret using gcloud CLI...")
673
+ create_or_update_secret(oauth_token_secret_id, github_pat, cicd_project)
674
+ create_cb_connection = False # Terraform will not create connection, will reference existing secret
675
+ console.print("✅ GitHub PAT secret created using gcloud CLI")
598
676
 
599
- # Copy CICD terraform files
600
- cicd_utils_path = Path(__file__).parent.parent.parent / "resources" / "setup_cicd"
677
+ # For GitHub Actions, no connection management needed
678
+ if cicd_runner == "github_actions":
679
+ create_cb_connection = False
601
680
 
602
- for tf_file in cicd_utils_path.glob("*.tf"):
603
- shutil.copy2(tf_file, tf_dir)
604
- console.print("✅ Copied CICD terraform files")
681
+ console.print("\n📦 Starting CI/CD Infrastructure Setup", style="bold blue")
682
+ console.print("=====================================")
605
683
 
606
684
  # Setup Terraform backend if not using local state
607
685
  if not local_state:
@@ -616,133 +694,65 @@ def setup_cicd(
616
694
  else:
617
695
  console.print("\n📝 Using local Terraform state (remote backend disabled)")
618
696
 
619
- # Update terraform variables using existing function
620
- deployment = E2EDeployment(config)
621
- deployment.update_terraform_vars(
622
- Path.cwd(), is_dev=False
623
- ) # is_dev=False for prod/staging setup
624
-
625
- # Update env.tfvars with additional variables
697
+ # Prepare Terraform variables
626
698
  env_vars_path = tf_dir / "vars" / "env.tfvars"
627
-
628
- # Read existing content
629
- existing_content = ""
630
- if env_vars_path.exists():
631
- with open(env_vars_path) as f:
632
- existing_content = f.read()
633
-
634
- # Prepare new variables
635
- new_vars = {}
636
- if not config.repository_owner:
637
- result = run_command(
699
+ terraform_vars = {
700
+ "staging_project_id": staging_project,
701
+ "prod_project_id": prod_project,
702
+ "cicd_runner_project_id": cicd_project,
703
+ "region": region,
704
+ "repository_name": repository_name,
705
+ "repository_owner": repository_owner
706
+ or run_command(
638
707
  ["gh", "api", "user", "--jq", ".login"], capture_output=True
708
+ ).stdout.strip(),
709
+ }
710
+
711
+ # Add CI/CD runner specific variables
712
+ if cicd_runner == "google_cloud_build":
713
+ terraform_vars.update(
714
+ {
715
+ "host_connection_name": host_connection_name,
716
+ "create_cb_connection": str(create_cb_connection).lower(),
717
+ "create_repository": str(
718
+ terraform_create_repository
719
+ ).lower(), # Use original state
720
+ "github_app_installation_id": github_app_installation_id,
721
+ "github_pat_secret_id": oauth_token_secret_id,
722
+ }
639
723
  )
640
- new_vars["repository_owner"] = result.stdout.strip()
641
- else:
642
- new_vars["repository_owner"] = config.repository_owner
643
-
644
- # Use the app installation ID from the connection if available, otherwise use the provided one
645
- new_vars["github_app_installation_id"] = github_app_installation_id
646
- # Use the OAuth token secret ID if available, otherwise use default PAT secret ID
647
- new_vars["github_pat_secret_id"] = oauth_token_secret_id
648
- # Set connection_exists based on whether we created a new connection
649
- new_vars["connection_exists"] = (
650
- "true" if detected_mode == "interactive" else "false"
651
- )
652
- new_vars["repository_exists"] = "true" if config.repository_exists else "false"
653
-
654
- # Update or append variables
655
- with open(env_vars_path, "w") as f:
656
- # Write existing content excluding lines with variables we're updating
657
- for line in existing_content.splitlines():
658
- if not any(line.startswith(f"{var} = ") for var in new_vars.keys()):
659
- f.write(line + "\n")
660
-
661
- # Write new/updated variables
662
- for var_name, var_value in new_vars.items():
663
- if var_value in ("true", "false"): # For boolean values
724
+ else: # github_actions
725
+ terraform_vars["create_repository"] = str(
726
+ terraform_create_repository
727
+ ).lower() # Use original state
728
+
729
+ # Write Terraform variables
730
+ with open(env_vars_path, "w", encoding="utf-8") as f:
731
+ for var_name, var_value in terraform_vars.items():
732
+ if var_value in ("true", "false"): # Boolean values
664
733
  f.write(f"{var_name} = {var_value}\n")
665
- else: # For string values
734
+ elif var_value is not None: # String values
666
735
  f.write(f'{var_name} = "{var_value}"\n')
667
736
 
668
- console.print("✅ Updated env.tfvars with additional variables")
669
-
670
- # Update dev environment vars
671
- dev_tf_vars_path = tf_dir / "dev" / "vars" / "env.tfvars"
672
- if (
673
- dev_tf_vars_path.exists() and dev_project
674
- ): # Only update if dev_project is provided
675
- with open(dev_tf_vars_path) as f:
676
- dev_content = f.read()
677
-
678
- # Update dev project ID
679
- dev_content = re.sub(
680
- r'dev_project_id\s*=\s*"[^"]*"',
681
- f'dev_project_id = "{dev_project}"',
682
- dev_content,
683
- )
684
-
685
- with open(dev_tf_vars_path, "w") as f:
686
- f.write(dev_content)
687
-
688
- console.print("✅ Updated dev env.tfvars with project configuration")
689
-
690
- # Update build triggers configuration
691
- update_build_triggers(tf_dir)
692
-
693
- # First initialize and apply dev terraform
694
- dev_tf_dir = tf_dir / "dev"
695
- if dev_tf_dir.exists() and dev_project: # Only deploy if dev_project is provided
696
- with console.status("[bold blue]Setting up dev environment..."):
737
+ console.print("✅ Updated env.tfvars with variables")
738
+
739
+ # Update dev environment vars if dev project provided
740
+ if dev_project:
741
+ dev_tf_vars_path = tf_dir / "dev" / "vars" / "env.tfvars"
742
+ if dev_tf_vars_path.exists():
743
+ with open(dev_tf_vars_path, "w", encoding="utf-8") as f:
744
+ f.write(f'dev_project_id = "{dev_project}"\n')
745
+ console.print("✅ Updated dev env.tfvars")
746
+
747
+ # Apply dev Terraform if dev project is provided
748
+ if dev_project:
749
+ dev_tf_dir = tf_dir / "dev"
750
+ if dev_tf_dir.exists():
751
+ console.print("\n🏗️ Applying dev Terraform configuration...")
697
752
  if local_state:
698
753
  run_command(["terraform", "init", "-backend=false"], cwd=dev_tf_dir)
699
754
  else:
700
755
  run_command(["terraform", "init"], cwd=dev_tf_dir)
701
-
702
- try:
703
- run_command(
704
- [
705
- "terraform",
706
- "apply",
707
- "-auto-approve",
708
- "--var-file",
709
- "vars/env.tfvars",
710
- ],
711
- cwd=dev_tf_dir,
712
- )
713
- except subprocess.CalledProcessError as e:
714
- if "Error acquiring the state lock" in str(e):
715
- console.print(
716
- "[yellow]State lock error detected, retrying without lock...[/yellow]"
717
- )
718
- run_command(
719
- [
720
- "terraform",
721
- "apply",
722
- "-auto-approve",
723
- "--var-file",
724
- "vars/env.tfvars",
725
- "-lock=false",
726
- ],
727
- cwd=dev_tf_dir,
728
- )
729
- else:
730
- raise
731
-
732
- console.print("✅ Dev environment Terraform configuration applied")
733
- elif dev_tf_dir.exists():
734
- console.print("ℹ️ Skipping dev environment setup (no dev project provided)")
735
-
736
- # Then apply prod terraform to create GitHub repo
737
- with console.status(
738
- "[bold blue]Setting up Prod/Staging Terraform configuration..."
739
- ):
740
- if local_state:
741
- run_command(["terraform", "init", "-backend=false"], cwd=tf_dir)
742
- else:
743
- run_command(["terraform", "init"], cwd=tf_dir)
744
-
745
- try:
746
756
  run_command(
747
757
  [
748
758
  "terraform",
@@ -751,87 +761,71 @@ def setup_cicd(
751
761
  "--var-file",
752
762
  "vars/env.tfvars",
753
763
  ],
754
- cwd=tf_dir,
764
+ cwd=dev_tf_dir,
755
765
  )
756
- except subprocess.CalledProcessError as e:
757
- if "Error acquiring the state lock" in str(e):
758
- console.print(
759
- "[yellow]State lock error detected, retrying without lock...[/yellow]"
760
- )
761
- run_command(
762
- [
763
- "terraform",
764
- "apply",
765
- "-auto-approve",
766
- "--var-file",
767
- "vars/env.tfvars",
768
- "-lock=false",
769
- ],
770
- cwd=tf_dir,
771
- )
772
- else:
773
- raise
766
+ console.print("✅ Dev environment deployed")
767
+ else:
768
+ console.print("ℹ️ No dev Terraform directory found")
774
769
 
775
- console.print("✅ Prod/Staging Terraform configuration applied")
770
+ # Apply prod Terraform
771
+ console.print("\n🚀 Applying prod Terraform configuration...")
772
+ if local_state:
773
+ run_command(["terraform", "init", "-backend=false"], cwd=tf_dir)
774
+ else:
775
+ run_command(["terraform", "init"], cwd=tf_dir)
776
776
 
777
- # Now we can set up git since the repo exists
778
- if git_provider == "github":
779
- console.print("\n🔧 Setting up Git repository...")
777
+ # Prepare environment variables for Terraform
778
+ terraform_env_vars = {}
779
+ if (
780
+ cicd_runner == "google_cloud_build"
781
+ and detected_mode == "programmatic"
782
+ and github_pat
783
+ ):
784
+ terraform_env_vars["GITHUB_TOKEN"] = (
785
+ github_pat # For GitHub provider authentication
786
+ )
780
787
 
781
- # Initialize git if not already initialized
782
- if not (Path.cwd() / ".git").exists():
783
- run_command(["git", "init", "-b", "main"])
784
- console.print("✅ Git repository initialized")
788
+ run_command(
789
+ ["terraform", "apply", "-auto-approve", "--var-file", "vars/env.tfvars"],
790
+ cwd=tf_dir,
791
+ env_vars=terraform_env_vars if terraform_env_vars else None,
792
+ )
793
+ console.print("✅ Prod/Staging infrastructure deployed")
785
794
 
786
- # Get current GitHub username for the remote URL
787
- result = run_command(
788
- ["gh", "api", "user", "--jq", ".login"], capture_output=True
789
- )
790
- github_username = result.stdout.strip()
795
+ config = ProjectConfig(
796
+ staging_project_id=staging_project,
797
+ prod_project_id=prod_project,
798
+ cicd_project_id=cicd_project,
799
+ agent="", # Not used in git setup
800
+ deployment_target="", # Not used in git setup
801
+ region=region,
802
+ repository_name=repository_name,
803
+ repository_owner=repository_owner,
804
+ )
791
805
 
792
- # Add remote if it doesn't exist
793
- try:
794
- run_command(
795
- ["git", "remote", "get-url", "origin"], capture_output=True, check=True
796
- )
797
- console.print("✅ Git remote already configured")
798
- except subprocess.CalledProcessError:
799
- remote_url = (
800
- f"https://github.com/{github_username}/{config.repository_name}.git"
801
- )
802
- run_command(["git", "remote", "add", "origin", remote_url])
803
- console.print(f"✅ Added git remote: {remote_url}")
806
+ setup_git_repository(config)
807
+
808
+ console.print("\n✅ CI/CD infrastructure setup complete!")
804
809
 
810
+ # Print useful information
811
+ repo_url = f"https://github.com/{repository_owner}/{repository_name}"
812
+
813
+ console.print("\n📋 Summary:")
814
+ console.print(f"• Repository: {repo_url}")
815
+ console.print(f"• CI/CD Runner: {cicd_runner.replace('_', ' ').title()}")
816
+
817
+ if cicd_runner == "google_cloud_build":
805
818
  console.print(
806
- "\n💡 Tip: Don't forget to commit and push your changes to the repository!"
819
+ f" Cloud Build: https://console.cloud.google.com/cloud-build/builds?project={cicd_project}"
807
820
  )
821
+ else:
822
+ console.print(f"• GitHub Actions: {repo_url}/actions")
808
823
 
809
- console.print("\n✅ CICD infrastructure setup complete!")
810
824
  if not local_state:
811
- console.print(
812
- f"📦 Using remote Terraform state in bucket: {cicd_project}-terraform-state"
813
- )
825
+ console.print(f"• Terraform State: gs://{cicd_project}-terraform-state")
814
826
  else:
815
- console.print("📝 Using local Terraform state")
827
+ console.print(" Terraform State: Local")
816
828
 
817
- try:
818
- # Print success message with useful links
819
- result = run_command(
820
- ["gh", "api", "user", "--jq", ".login"], capture_output=True
821
- )
822
- github_username = result.stdout.strip()
823
-
824
- repo_url = f"https://github.com/{github_username}/{config.repository_name}"
825
- cloud_build_url = f"https://console.cloud.google.com/cloud-build/builds?project={config.cicd_project_id}"
826
- # Sleep to allow resources to propagate
827
- console.print("\n⏳ Waiting for resources to propagate...")
828
- time.sleep(10)
829
-
830
- # Print final summary
831
- print_cicd_summary(config, github_username, repo_url, cloud_build_url)
832
-
833
- except Exception as e:
834
- if debug:
835
- logging.exception("An error occurred:")
836
- console.print(f"\n❌ Error: {e!s}", style="bold red")
837
- sys.exit(1)
829
+ console.print("\n💡 Next steps:")
830
+ console.print("1. Commit and push your code to the repository")
831
+ console.print("2. Your CI/CD pipeline will automatically trigger on pushes")