agent-starter-pack 0.11.1__py3-none-any.whl → 0.12.0__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 (72) hide show
  1. {agent_starter_pack-0.11.1.dist-info → agent_starter_pack-0.12.0.dist-info}/METADATA +1 -1
  2. {agent_starter_pack-0.11.1.dist-info → agent_starter_pack-0.12.0.dist-info}/RECORD +45 -70
  3. agents/adk_base/app/__init__.py +17 -0
  4. agents/adk_base/notebooks/adk_app_testing.ipynb +4 -1
  5. agents/adk_base/tests/integration/test_agent.py +1 -1
  6. agents/agentic_rag/app/__init__.py +17 -0
  7. agents/agentic_rag/app/agent.py +2 -2
  8. agents/agentic_rag/notebooks/adk_app_testing.ipynb +4 -1
  9. agents/agentic_rag/tests/integration/test_agent.py +2 -2
  10. agents/crewai_coding_crew/tests/integration/test_agent.py +1 -1
  11. agents/langgraph_base_react/tests/integration/test_agent.py +1 -1
  12. agents/live_api/tests/unit/test_server.py +6 -6
  13. llm.txt +15 -4
  14. src/base_template/Makefile +5 -5
  15. src/base_template/README.md +4 -4
  16. src/base_template/deployment/terraform/dev/variables.tf +3 -2
  17. src/base_template/deployment/terraform/iam.tf +10 -34
  18. src/base_template/deployment/terraform/variables.tf +3 -3
  19. src/base_template/deployment/terraform/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}build_triggers.tf{% else %}unused_build_triggers.tf{% endif %} +2 -2
  20. src/base_template/pyproject.toml +2 -2
  21. src/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/deploy-to-prod.yaml +1 -1
  22. src/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/pr_checks.yaml +1 -1
  23. src/base_template/{% if cookiecutter.cicd_runner == 'github_actions' %}.github{% else %}unused_github{% endif %}/workflows/staging.yaml +2 -2
  24. src/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/deploy-to-prod.yaml +1 -1
  25. src/base_template/{% if cookiecutter.cicd_runner == 'google_cloud_build' %}.cloudbuild{% else %}unused_.cloudbuild{% endif %}/staging.yaml +1 -1
  26. src/cli/commands/create.py +25 -2
  27. src/cli/commands/enhance.py +94 -15
  28. src/cli/commands/list.py +1 -1
  29. src/cli/commands/setup_cicd.py +50 -41
  30. src/cli/utils/remote_template.py +1 -1
  31. src/cli/utils/template.py +120 -41
  32. src/deployment_targets/agent_engine/tests/integration/test_agent_engine_app.py +3 -3
  33. src/deployment_targets/agent_engine/{app → {{cookiecutter.agent_directory}}}/agent_engine_app.py +10 -10
  34. src/deployment_targets/cloud_run/Dockerfile +2 -2
  35. src/deployment_targets/cloud_run/tests/integration/test_server_e2e.py +3 -3
  36. src/deployment_targets/cloud_run/tests/load_test/README.md +1 -1
  37. src/deployment_targets/cloud_run/tests/load_test/load_test.py +2 -2
  38. {agents/live_api/app → src/deployment_targets/cloud_run/{{cookiecutter.agent_directory}}}/server.py +186 -7
  39. src/resources/docs/adk-cheatsheet.md +3 -3
  40. src/base_template/app/__init__.py +0 -3
  41. src/deployment_targets/cloud_run/app/server.py +0 -206
  42. src/frontends/adk_gemini_fullstack/frontend/components.json +0 -21
  43. src/frontends/adk_gemini_fullstack/frontend/eslint.config.js +0 -28
  44. src/frontends/adk_gemini_fullstack/frontend/index.html +0 -12
  45. src/frontends/adk_gemini_fullstack/frontend/package-lock.json +0 -6105
  46. src/frontends/adk_gemini_fullstack/frontend/package.json +0 -47
  47. src/frontends/adk_gemini_fullstack/frontend/src/App.tsx +0 -564
  48. src/frontends/adk_gemini_fullstack/frontend/src/components/ActivityTimeline.tsx +0 -244
  49. src/frontends/adk_gemini_fullstack/frontend/src/components/ChatMessagesView.tsx +0 -420
  50. src/frontends/adk_gemini_fullstack/frontend/src/components/InputForm.tsx +0 -60
  51. src/frontends/adk_gemini_fullstack/frontend/src/components/WelcomeScreen.tsx +0 -56
  52. src/frontends/adk_gemini_fullstack/frontend/src/components/ui/badge.tsx +0 -46
  53. src/frontends/adk_gemini_fullstack/frontend/src/components/ui/button.tsx +0 -59
  54. src/frontends/adk_gemini_fullstack/frontend/src/components/ui/card.tsx +0 -92
  55. src/frontends/adk_gemini_fullstack/frontend/src/components/ui/input.tsx +0 -21
  56. src/frontends/adk_gemini_fullstack/frontend/src/components/ui/scroll-area.tsx +0 -56
  57. src/frontends/adk_gemini_fullstack/frontend/src/components/ui/select.tsx +0 -183
  58. src/frontends/adk_gemini_fullstack/frontend/src/components/ui/tabs.tsx +0 -64
  59. src/frontends/adk_gemini_fullstack/frontend/src/components/ui/textarea.tsx +0 -18
  60. src/frontends/adk_gemini_fullstack/frontend/src/global.css +0 -154
  61. src/frontends/adk_gemini_fullstack/frontend/src/main.tsx +0 -13
  62. src/frontends/adk_gemini_fullstack/frontend/src/utils.ts +0 -7
  63. src/frontends/adk_gemini_fullstack/frontend/src/vite-env.d.ts +0 -1
  64. src/frontends/adk_gemini_fullstack/frontend/tsconfig.json +0 -28
  65. src/frontends/adk_gemini_fullstack/frontend/tsconfig.node.json +0 -24
  66. src/frontends/adk_gemini_fullstack/frontend/vite.config.ts +0 -41
  67. {agent_starter_pack-0.11.1.dist-info → agent_starter_pack-0.12.0.dist-info}/WHEEL +0 -0
  68. {agent_starter_pack-0.11.1.dist-info → agent_starter_pack-0.12.0.dist-info}/entry_points.txt +0 -0
  69. {agent_starter_pack-0.11.1.dist-info → agent_starter_pack-0.12.0.dist-info}/licenses/LICENSE +0 -0
  70. /src/base_template/{app → {{cookiecutter.agent_directory}}}/utils/gcs.py +0 -0
  71. /src/base_template/{app → {{cookiecutter.agent_directory}}}/utils/tracing.py +0 -0
  72. /src/base_template/{app → {{cookiecutter.agent_directory}}}/utils/typing.py +0 -0
@@ -29,7 +29,7 @@ resource "google_cloudbuild_trigger" "pr_checks" {
29
29
 
30
30
  filename = ".cloudbuild/pr_checks.yaml"
31
31
  included_files = [
32
- "app/**",
32
+ "{{cookiecutter.agent_directory}}/**",
33
33
  "data_ingestion/**",
34
34
  "tests/**",
35
35
  "deployment/**",
@@ -64,7 +64,7 @@ resource "google_cloudbuild_trigger" "cd_pipeline" {
64
64
 
65
65
  filename = ".cloudbuild/staging.yaml"
66
66
  included_files = [
67
- "app/**",
67
+ "{{cookiecutter.agent_directory}}/**",
68
68
  "data_ingestion/**",
69
69
  "tests/**",
70
70
  "deployment/**",
@@ -75,7 +75,7 @@ select = [
75
75
  ignore = ["E501", "C901"] # ignore line too long, too complex
76
76
 
77
77
  [tool.ruff.lint.isort]
78
- known-first-party = ["app", "frontend"]
78
+ known-first-party = ["{{cookiecutter.agent_directory}}", "frontend"]
79
79
 
80
80
  [tool.mypy]
81
81
  disallow_untyped_calls = true
@@ -113,4 +113,4 @@ pythonpath = "."
113
113
  asyncio_default_fixture_loop_scope = "function"
114
114
 
115
115
  [tool.hatch.build.targets.wheel]
116
- packages = ["app","frontend"]
116
+ packages = ["{{cookiecutter.agent_directory}}","frontend"]
@@ -108,7 +108,7 @@ jobs:
108
108
  - name: Deploy to Production (Agent Engine)
109
109
  run: |
110
110
  uv export --no-hashes --no-sources --no-header --no-dev --no-emit-project --no-annotate --locked > .requirements.txt
111
- uv run app/agent_engine_app.py \
111
+ uv run {{cookiecutter.agent_directory}}/agent_engine_app.py \
112
112
  --project {% raw %}${{ vars.PROD_PROJECT_ID }}{% endraw %} \
113
113
  --location {% raw %}${{ vars.REGION }}{% endraw %} \
114
114
  --service-account {% raw %}${{ vars.APP_SA_EMAIL_PROD }}{% endraw %} \
@@ -19,7 +19,7 @@ on:
19
19
  branches:
20
20
  - main
21
21
  paths:
22
- - 'app/**'
22
+ - '{{cookiecutter.agent_directory}}/**'
23
23
  - 'data_ingestion/**'
24
24
  - 'tests/**'
25
25
  - 'deployment/**'
@@ -19,7 +19,7 @@ on:
19
19
  branches:
20
20
  - main
21
21
  paths:
22
- - 'app/**'
22
+ - '{{cookiecutter.agent_directory}}/**'
23
23
  - 'data_ingestion/**'
24
24
  - 'tests/**'
25
25
  - 'deployment/**'
@@ -119,7 +119,7 @@ jobs:
119
119
  - name: Deploy to Staging (Agent Engine)
120
120
  run: |
121
121
  uv export --no-hashes --no-sources --no-header --no-dev --no-emit-project --no-annotate --locked > .requirements.txt
122
- uv run app/agent_engine_app.py \
122
+ uv run {{cookiecutter.agent_directory}}/agent_engine_app.py \
123
123
  --project {% raw %}${{ vars.STAGING_PROJECT_ID }}{% endraw %} \
124
124
  --location {% raw %}${{ vars.REGION }}{% endraw %} \
125
125
  --service-account {% raw %}${{ vars.APP_SA_EMAIL_STAGING }}{% endraw %} \
@@ -74,7 +74,7 @@ steps:
74
74
  - "-c"
75
75
  - |
76
76
  uv export --no-hashes --no-sources --no-header --no-dev --no-emit-project --no-annotate --locked > .requirements.txt
77
- uv run app/agent_engine_app.py \
77
+ uv run {{cookiecutter.agent_directory}}/agent_engine_app.py \
78
78
  --project ${_PROD_PROJECT_ID} \
79
79
  --location ${_REGION} \
80
80
  --service-account ${_APP_SA_EMAIL_PROD} \
@@ -108,7 +108,7 @@ steps:
108
108
  - "-c"
109
109
  - |
110
110
  uv export --no-hashes --no-sources --no-header --no-dev --no-emit-project --no-annotate --locked > .requirements.txt
111
- uv run app/agent_engine_app.py \
111
+ uv run {{cookiecutter.agent_directory}}/agent_engine_app.py \
112
112
  --project ${_STAGING_PROJECT_ID} \
113
113
  --location ${_REGION} \
114
114
  --service-account ${_APP_SA_EMAIL_STAGING} \
@@ -19,10 +19,10 @@ import pathlib
19
19
  import shutil
20
20
  import subprocess
21
21
  import tempfile
22
+ import tomllib
22
23
  from collections.abc import Callable
23
24
 
24
25
  import click
25
- import tomllib
26
26
  from click.core import ParameterSource
27
27
  from rich.console import Console
28
28
  from rich.prompt import IntPrompt, Prompt
@@ -101,6 +101,11 @@ def shared_template_options(f: Callable) -> Callable:
101
101
  type=click.Choice(["agent_engine", "cloud_run"]),
102
102
  help="Deployment target name",
103
103
  )(f)
104
+ f = click.option(
105
+ "--agent-directory",
106
+ "-dir",
107
+ help="Name of the agent directory (overrides template default)",
108
+ )(f)
104
109
  return f
105
110
 
106
111
 
@@ -232,8 +237,10 @@ def create(
232
237
  region: str,
233
238
  skip_checks: bool,
234
239
  in_folder: bool,
240
+ agent_directory: str | None,
235
241
  base_template: str | None = None,
236
242
  skip_welcome: bool = False,
243
+ cli_overrides: dict | None = None,
237
244
  ) -> None:
238
245
  """Create GCP-based AI agent projects from templates."""
239
246
  try:
@@ -434,6 +441,14 @@ def create(
434
441
  / ".template"
435
442
  )
436
443
  config = load_template_config(template_path)
444
+
445
+ # Apply CLI overrides for local templates if provided (e.g., from enhance command)
446
+ if cli_overrides:
447
+ config = merge_template_configs(config, cli_overrides)
448
+ if debug:
449
+ logging.debug(
450
+ f"Applied CLI overrides to local template config: {cli_overrides}"
451
+ )
437
452
  # Data ingestion and datastore selection
438
453
  if include_data_ingestion or datastore:
439
454
  include_data_ingestion = True
@@ -610,6 +625,13 @@ def create(
610
625
  if debug:
611
626
  logging.debug(f"Output directory: {destination_dir}")
612
627
 
628
+ # Construct CLI overrides for template processing
629
+ final_cli_overrides = cli_overrides or {}
630
+ if agent_directory:
631
+ if "settings" not in final_cli_overrides:
632
+ final_cli_overrides["settings"] = {}
633
+ final_cli_overrides["settings"]["agent_directory"] = agent_directory
634
+
613
635
  try:
614
636
  # Process template (handles both local and remote templates)
615
637
  process_template(
@@ -623,8 +645,9 @@ def create(
623
645
  session_type=final_session_type,
624
646
  output_dir=destination_dir,
625
647
  remote_template_path=template_source_path,
626
- remote_config=config if template_source_path else None,
648
+ remote_config=config,
627
649
  in_folder=in_folder,
650
+ cli_overrides=final_cli_overrides,
628
651
  )
629
652
 
630
653
  # Replace region in all files if a different region was specified
@@ -13,6 +13,7 @@
13
13
  # limitations under the License.
14
14
 
15
15
  import pathlib
16
+ from typing import Any
16
17
 
17
18
  import click
18
19
  from rich.console import Console
@@ -48,7 +49,7 @@ def display_base_template_selection(current_base: str) -> str:
48
49
  choice_num = 1
49
50
  current_choice = None
50
51
 
51
- for _num, agent in agents.items():
52
+ for agent in agents.values():
52
53
  template_choices[choice_num] = agent["name"]
53
54
  current_indicator = " (current)" if agent["name"] == current_base else ""
54
55
  console.print(
@@ -89,6 +90,10 @@ def display_base_template_selection(current_base: str) -> str:
89
90
  "--base-template",
90
91
  help="Base template to inherit from (e.g., adk_base, langgraph_base_react, agentic_rag)",
91
92
  )
93
+ @click.option(
94
+ "--agent-directory",
95
+ help="Custom directory name for agent files (default: 'app' or auto-detected from pyproject.toml)",
96
+ )
92
97
  @shared_template_options
93
98
  @handle_cli_error
94
99
  def enhance(
@@ -105,6 +110,7 @@ def enhance(
105
110
  region: str,
106
111
  skip_checks: bool,
107
112
  base_template: str | None,
113
+ agent_directory: str | None,
108
114
  ) -> None:
109
115
  """Enhance your existing project with AI agent capabilities.
110
116
 
@@ -113,7 +119,8 @@ def enhance(
113
119
  creating a new project directory.
114
120
 
115
121
  For best compatibility, your project should follow the agent-starter-pack structure
116
- with agent code organized in an /app folder (containing agent.py, etc.).
122
+ with agent code organized in an agent directory (default: /app, configurable via
123
+ --agent-directory).
117
124
 
118
125
  TEMPLATE_PATH can be:
119
126
  - A local directory path (e.g., . for current directory)
@@ -191,10 +198,13 @@ def enhance(
191
198
  load_remote_template_config,
192
199
  )
193
200
 
194
- # Prepare CLI overrides for base template
195
- cli_overrides = {}
201
+ # Prepare CLI overrides for base template and agent directory
202
+ cli_overrides: dict[str, Any] = {}
196
203
  if base_template:
197
204
  cli_overrides["base_template"] = base_template
205
+ if agent_directory:
206
+ cli_overrides["settings"] = cli_overrides.get("settings", {})
207
+ cli_overrides["settings"]["agent_directory"] = agent_directory
198
208
 
199
209
  # Load config from current directory for inheritance info
200
210
  current_dir = pathlib.Path.cwd()
@@ -209,6 +219,10 @@ def enhance(
209
219
  if selected_base_template != original_base_template_name:
210
220
  # Update CLI overrides with the selected base template
211
221
  cli_overrides["base_template"] = selected_base_template
222
+ # Preserve agent_directory override if it was set
223
+ if agent_directory:
224
+ cli_overrides["settings"] = cli_overrides.get("settings", {})
225
+ cli_overrides["settings"]["agent_directory"] = agent_directory
212
226
  base_template = selected_base_template
213
227
  console.print(
214
228
  f"✅ Selected base template: [cyan]{selected_base_template}[/cyan]"
@@ -232,9 +246,53 @@ def enhance(
232
246
  # Validate project structure when using current directory template
233
247
  if template_path == pathlib.Path("."):
234
248
  current_dir = pathlib.Path.cwd()
235
- app_folder = current_dir / "app"
236
249
 
237
- if not app_folder.exists() or not app_folder.is_dir():
250
+ # Determine agent directory: CLI param > pyproject.toml detection > default
251
+ detected_agent_directory = "app" # default
252
+ if not agent_directory: # Only try to detect if not provided via CLI
253
+ pyproject_path = current_dir / "pyproject.toml"
254
+ if pyproject_path.exists():
255
+ try:
256
+ import tomllib
257
+
258
+ with open(pyproject_path, "rb") as f:
259
+ pyproject_data = tomllib.load(f)
260
+ packages = (
261
+ pyproject_data.get("tool", {})
262
+ .get("hatch", {})
263
+ .get("build", {})
264
+ .get("targets", {})
265
+ .get("wheel", {})
266
+ .get("packages", [])
267
+ )
268
+ if packages:
269
+ # Find the first package that isn't 'frontend'
270
+ for pkg in packages:
271
+ if pkg != "frontend":
272
+ detected_agent_directory = pkg
273
+ break
274
+ except Exception as e:
275
+ if debug:
276
+ console.print(
277
+ f"[dim]Could not auto-detect agent directory: {e}[/dim]"
278
+ )
279
+ pass # Fall back to default
280
+
281
+ final_agent_directory = agent_directory or detected_agent_directory
282
+
283
+ # Show info about agent directory selection
284
+ if agent_directory:
285
+ console.print(
286
+ f"ℹ️ Using CLI-specified agent directory: [cyan]{agent_directory}[/cyan]"
287
+ )
288
+ elif detected_agent_directory != "app":
289
+ console.print(
290
+ f"ℹ️ Auto-detected agent directory: [cyan]{detected_agent_directory}[/cyan]"
291
+ )
292
+
293
+ agent_folder = current_dir / final_agent_directory
294
+
295
+ if not agent_folder.exists() or not agent_folder.is_dir():
238
296
  console.print()
239
297
  console.print(
240
298
  "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
@@ -245,42 +303,61 @@ def enhance(
245
303
  )
246
304
  console.print()
247
305
  console.print(
248
- "📁 [bold]Expected Structure:[/bold] [cyan]/app[/cyan] folder containing your agent code"
306
+ f"📁 [bold]Expected Structure:[/bold] [cyan]/{final_agent_directory}[/cyan] folder containing your agent code"
249
307
  )
250
308
  console.print(f"📍 [bold]Current Directory:[/bold] {current_dir}")
251
- console.print("❌ [bold red]Missing:[/bold red] /app folder")
309
+ console.print(
310
+ f"❌ [bold red]Missing:[/bold red] /{final_agent_directory} folder"
311
+ )
252
312
  console.print()
253
313
  console.print(
254
- "[dim]The enhance command can still proceed, but for best compatibility"
255
- " your agent code should be organized in an /app folder structure.[/dim]"
314
+ f"The enhance command can still proceed, but for best compatibility"
315
+ f" your agent code should be organized in a /{final_agent_directory} folder structure."
256
316
  )
257
317
  console.print()
258
318
 
259
319
  # Ask for confirmation after showing the structure warning
320
+ console.print("💡 Options:")
260
321
  console.print(
261
- "💡 [dim]Consider creating an /app folder for better compatibility.[/dim]"
322
+ f" Create a /{final_agent_directory} folder and move your agent code there"
262
323
  )
324
+ if final_agent_directory == "app":
325
+ console.print(
326
+ " • Use [cyan]--agent-directory <custom_name>[/cyan] if your agent code is in a different directory"
327
+ )
328
+ else:
329
+ console.print(
330
+ " • Use [cyan]--agent-directory <custom_name>[/cyan] to specify your existing agent directory"
331
+ )
263
332
  console.print()
264
333
 
265
334
  if not auto_approve:
266
335
  if not click.confirm(
267
- "Continue with enhancement despite missing /app folder?",
336
+ f"Continue with enhancement despite missing /{final_agent_directory} folder?",
268
337
  default=True,
269
338
  ):
270
339
  console.print("✋ [yellow]Enhancement cancelled.[/yellow]")
271
340
  return
272
341
  else:
273
342
  # Check for common agent files
274
- agent_py = app_folder / "agent.py"
343
+ agent_py = agent_folder / "agent.py"
275
344
  if agent_py.exists():
276
345
  console.print(
277
- "Detected existing agent structure with [cyan]/app/agent.py[/cyan]"
346
+ f"Detected existing agent structure with [cyan]/{final_agent_directory}/agent.py[/cyan]"
278
347
  )
279
348
  else:
280
349
  console.print(
281
- "ℹ️ [blue]Found /app folder[/blue] - ensure your agent code is properly organized within it"
350
+ f"ℹ️ [blue]Found /{final_agent_directory} folder[/blue] - ensure your agent code is properly organized within it, including an agent.py file"
282
351
  )
283
352
 
353
+ # Prepare CLI overrides to pass to create command
354
+ final_cli_overrides: dict[str, Any] = {}
355
+ if base_template:
356
+ final_cli_overrides["base_template"] = base_template
357
+ if agent_directory:
358
+ final_cli_overrides["settings"] = final_cli_overrides.get("settings", {})
359
+ final_cli_overrides["settings"]["agent_directory"] = agent_directory
360
+
284
361
  # Call the create command with in-folder mode enabled
285
362
  ctx.invoke(
286
363
  create,
@@ -297,6 +374,8 @@ def enhance(
297
374
  region=region,
298
375
  skip_checks=skip_checks,
299
376
  in_folder=True, # Always use in-folder mode for enhance
377
+ agent_directory=agent_directory,
300
378
  base_template=base_template,
301
379
  skip_welcome=True, # Skip welcome message since enhance shows its own
380
+ cli_overrides=final_cli_overrides if final_cli_overrides else None,
302
381
  )
src/cli/commands/list.py CHANGED
@@ -14,9 +14,9 @@
14
14
 
15
15
  import logging
16
16
  import pathlib
17
+ import tomllib
17
18
 
18
19
  import click
19
- import tomllib
20
20
  from rich.console import Console
21
21
  from rich.table import Table
22
22
 
@@ -48,7 +48,7 @@ def display_intro_message() -> None:
48
48
  "This command helps set up a basic CI/CD pipeline for development and testing purposes."
49
49
  )
50
50
  console.print("It will:")
51
- console.print("- Create a GitHub repository and connect it to Cloud Build")
51
+ console.print("- Create a GitHub repository and connect it to your CI/CD runner")
52
52
  console.print("- Set up development environment infrastructure")
53
53
  console.print("- Configure basic CI/CD triggers for PR checks and deployments")
54
54
  console.print(
@@ -218,6 +218,34 @@ def validate_working_directory() -> None:
218
218
  )
219
219
 
220
220
 
221
+ def detect_region_from_terraform_vars() -> str | None:
222
+ """Detect region from Terraform vars file.
223
+
224
+ Returns:
225
+ str | None: The detected region, or None if not found or is default
226
+ """
227
+ try:
228
+ tf_vars_path = Path("deployment/terraform/vars/env.tfvars")
229
+ if not tf_vars_path.exists():
230
+ return None
231
+
232
+ with open(tf_vars_path, encoding="utf-8") as f:
233
+ content = f.read()
234
+
235
+ # Look for region = "value" pattern
236
+ region_match = re.search(r'region\s*=\s*"([^"]+)"', content)
237
+ if region_match:
238
+ detected_region = region_match.group(1)
239
+ # Don't auto-detect if it's the default value
240
+ if detected_region != "us-central1":
241
+ return detected_region
242
+
243
+ return None
244
+ except Exception:
245
+ # If any error occurs, return None to use default
246
+ return None
247
+
248
+
221
249
  def update_build_triggers(tf_dir: Path) -> None:
222
250
  """Update build triggers configuration."""
223
251
  build_triggers_path = tf_dir / "build_triggers.tf"
@@ -427,7 +455,9 @@ console = Console()
427
455
  @click.option(
428
456
  "--cicd-project", help="CICD project ID (defaults to prod project if not specified)"
429
457
  )
430
- @click.option("--region", default="us-central1", help="GCP region")
458
+ @click.option(
459
+ "--region", help="GCP region (auto-detects from Terraform vars if not specified)"
460
+ )
431
461
  @click.option("--repository-name", help="Repository name (optional)")
432
462
  @click.option(
433
463
  "--repository-owner",
@@ -468,7 +498,7 @@ def setup_cicd(
468
498
  staging_project: str | None,
469
499
  prod_project: str | None,
470
500
  cicd_project: str | None,
471
- region: str,
501
+ region: str | None,
472
502
  repository_name: str | None,
473
503
  repository_owner: str | None,
474
504
  host_connection_name: str | None,
@@ -502,23 +532,24 @@ def setup_cicd(
502
532
  cicd_project = prod_project
503
533
  console.print(f"Using production project '{prod_project}' for CI/CD resources")
504
534
 
505
- console.print(
506
- "\n⚠️ WARNING: The setup-cicd command is experimental and may have unexpected behavior.",
507
- style="bold yellow",
508
- )
509
- console.print("Please report any issues you encounter.\n")
535
+ # Auto-detect region if not provided
536
+ if region is None:
537
+ detected_region = detect_region_from_terraform_vars()
538
+ if detected_region:
539
+ region = detected_region
540
+ console.print(f"Auto-detected region from Terraform vars: {region}")
541
+ else:
542
+ region = "us-central1"
543
+ console.print(f"Using default region: {region}")
544
+ else:
545
+ console.print(f"Using provided region: {region}")
510
546
 
511
- console.print("\n📋 About this command:", style="bold blue")
512
- console.print(
513
- "This command helps set up a basic CI/CD pipeline for development and testing purposes."
514
- )
515
- console.print("It will:")
516
- console.print("- Create a GitHub repository and connect it to Cloud Build")
517
- console.print("- Set up development environment infrastructure")
518
- console.print("- Configure basic CI/CD triggers for PR checks and deployments")
519
- console.print(
520
- "- Configure remote Terraform state in GCS (use --local-state to use local state instead)"
521
- )
547
+ # Auto-detect CI/CD runner based on Terraform files (moved earlier)
548
+ tf_dir = Path("deployment/terraform")
549
+ is_github_actions = (tf_dir / "wif.tf").exists() and (tf_dir / "github.tf").exists()
550
+ cicd_runner = "github_actions" if is_github_actions else "google_cloud_build"
551
+
552
+ display_intro_message()
522
553
 
523
554
  console.print("\n⚡ Production Setup Note:", style="bold yellow")
524
555
  console.print(
@@ -537,28 +568,6 @@ def setup_cicd(
537
568
  console.print("\n🛑 Setup cancelled by user", style="bold yellow")
538
569
  return
539
570
 
540
- console.print(
541
- "This command helps set up a basic CI/CD pipeline for development and testing purposes."
542
- )
543
- console.print("It will:")
544
- console.print("- Create a GitHub repository and connect it to Cloud Build")
545
- console.print("- Set up development environment infrastructure")
546
- console.print("- Configure basic CI/CD triggers for PR checks and deployments")
547
- console.print(
548
- "- Configure remote Terraform state in GCS (use --local-state to use local state instead)"
549
- )
550
-
551
- console.print("\n⚡ Production Setup Note:", style="bold yellow")
552
- console.print(
553
- "For production deployments and maximum flexibility, we recommend following"
554
- )
555
- console.print("the manual setup instructions in deployment/README.md")
556
- console.print("This will give you more control over:")
557
- console.print("- Security configurations")
558
- console.print("- Custom deployment workflows")
559
- console.print("- Environment-specific settings")
560
- console.print("- Advanced CI/CD pipeline customization\n")
561
-
562
571
  if debug:
563
572
  logging.basicConfig(level=logging.DEBUG)
564
573
  console.print("> Debug mode enabled")
@@ -19,10 +19,10 @@ import re
19
19
  import shutil
20
20
  import subprocess
21
21
  import tempfile
22
+ import tomllib
22
23
  from dataclasses import dataclass
23
24
  from typing import Any
24
25
 
25
- import tomllib
26
26
  from jinja2 import Environment
27
27
 
28
28