diff --git a/src/google/adk/cli/cli_create.py b/src/google/adk/cli/cli_create.py index e3dc3a1d30..ecb2d5f242 100644 --- a/src/google/adk/cli/cli_create.py +++ b/src/google/adk/cli/cli_create.py @@ -78,6 +78,7 @@ _SUCCESS_MSG_CODE = """ Agent created in {agent_folder}: - .env +- .gitignore - __init__.py - agent.py @@ -88,6 +89,7 @@ _SUCCESS_MSG_CONFIG = """ Agent created in {agent_folder}: - .env +- .gitignore - __init__.py - root_agent.yaml @@ -188,6 +190,28 @@ def _prompt_for_google_api_key( return google_api_key +def _ensure_dotenv_gitignored(agent_folder: str) -> None: + """Ensures generated dotenv files are ignored by git.""" + gitignore_file_path = os.path.join(agent_folder, ".gitignore") + dotenv_ignore_entry = ".env" + + if not os.path.exists(gitignore_file_path): + with open(gitignore_file_path, "w", encoding="utf-8") as f: + f.write(f"{dotenv_ignore_entry}\n") + return + + with open(gitignore_file_path, "r", encoding="utf-8") as f: + content = f.read() + + if dotenv_ignore_entry in (line.strip() for line in content.splitlines()): + return + + with open(gitignore_file_path, "a", encoding="utf-8") as f: + if content and not content.endswith("\n"): + f.write("\n") + f.write(f"{dotenv_ignore_entry}\n") + + def _generate_files( agent_folder: str, *, @@ -218,6 +242,7 @@ def _generate_files( if google_cloud_region: lines.append(f"GOOGLE_CLOUD_LOCATION={google_cloud_region}") f.write("\n".join(lines)) + _ensure_dotenv_gitignored(agent_folder) if type == "config": with open(agent_config_file_path, "w", encoding="utf-8") as f: diff --git a/tests/unittests/cli/utils/test_cli_create.py b/tests/unittests/cli/utils/test_cli_create.py index 708a6a210d..c3ec782450 100644 --- a/tests/unittests/cli/utils/test_cli_create.py +++ b/tests/unittests/cli/utils/test_cli_create.py @@ -68,6 +68,7 @@ def test_generate_files_with_api_key(agent_folder: Path) -> None: env_content = (agent_folder / ".env").read_text() assert "GOOGLE_API_KEY=dummy-key" in env_content assert "GOOGLE_GENAI_USE_VERTEXAI=0" in env_content + assert (agent_folder / ".gitignore").read_text() == ".env\n" assert (agent_folder / "agent.py").exists() assert (agent_folder / "__init__.py").exists() @@ -149,6 +150,34 @@ def test_generate_files_no_params(agent_folder: Path) -> None: assert key not in env_content +def test_generate_files_appends_dotenv_to_existing_gitignore( + agent_folder: Path, +) -> None: + """Existing .gitignore entries should be preserved.""" + agent_folder.mkdir(parents=True, exist_ok=True) + (agent_folder / ".gitignore").write_text("__pycache__") + + cli_create._generate_files( + str(agent_folder), model="gemini-2.0-flash-001", type="code" + ) + + assert (agent_folder / ".gitignore").read_text() == "__pycache__\n.env\n" + + +def test_generate_files_does_not_duplicate_dotenv_gitignore_entry( + agent_folder: Path, +) -> None: + """Existing .env ignore entries should not be duplicated.""" + agent_folder.mkdir(parents=True, exist_ok=True) + (agent_folder / ".gitignore").write_text("__pycache__\n.env\n") + + cli_create._generate_files( + str(agent_folder), model="gemini-2.0-flash-001", type="code" + ) + + assert (agent_folder / ".gitignore").read_text() == "__pycache__\n.env\n" + + # run_cmd def test_run_cmd_overwrite_reject( monkeypatch: pytest.MonkeyPatch, tmp_path: Path @@ -230,6 +259,7 @@ def test_run_cmd_with_type_config( env_file = agent_dir / ".env" assert env_file.exists() assert "GOOGLE_API_KEY=test-key" in env_file.read_text() + assert (agent_dir / ".gitignore").read_text() == ".env\n" # Prompt helpers