Skip to content

Commit eb7ca74

Browse files
authored
feat: Validate session on create and fetch (#471)
1 parent b45c4f5 commit eb7ca74

File tree

8 files changed

+82
-109
lines changed

8 files changed

+82
-109
lines changed

src/codegen/cli/auth/decorators.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def wrapper(*args, **kwargs):
1919
session = CodegenSession.from_active_session()
2020

2121
# Check for valid session
22-
if session is None or not session.is_valid():
22+
if session is None:
2323
pretty_print_error("There is currently no active session.\nPlease run 'codegen init' to initialize the project.")
2424
raise click.Abort()
2525

src/codegen/cli/auth/session.py

Lines changed: 64 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
from pathlib import Path
22

3-
from pygit2.repository import Repository
3+
import click
4+
import rich
5+
from github import BadCredentialsException
6+
from github.MainClass import Github
47

58
from codegen.cli.git.repo import get_git_repo
9+
from codegen.cli.rich.codeblocks import format_command
10+
from codegen.git.repo_operator.local_git_repo import LocalGitRepo
611
from codegen.shared.configs.constants import CODEGEN_DIR_NAME, CONFIG_FILENAME
712
from codegen.shared.configs.models.session import SessionConfig
813
from codegen.shared.configs.session_configs import global_config, load_session_config
@@ -11,16 +16,25 @@
1116
class CodegenSession:
1217
"""Represents an authenticated codegen session with user and repository context"""
1318

14-
repo_path: Path # TODO: rename to root_path
19+
repo_path: Path
20+
local_git: LocalGitRepo
1521
codegen_dir: Path
1622
config: SessionConfig
1723
existing: bool
1824

19-
def __init__(self, repo_path: Path):
25+
def __init__(self, repo_path: Path, git_token: str | None = None) -> None:
26+
if not repo_path.exists() or get_git_repo(repo_path) is None:
27+
rich.print(f"\n[bold red]Error:[/bold red] Path to git repo does not exist at {self.repo_path}")
28+
raise click.Abort()
29+
2030
self.repo_path = repo_path
31+
self.local_git = LocalGitRepo(repo_path=repo_path)
2132
self.codegen_dir = repo_path / CODEGEN_DIR_NAME
22-
self.existing = global_config.get_session(repo_path) is not None
2333
self.config = load_session_config(self.codegen_dir / CONFIG_FILENAME)
34+
self.config.secrets.github_token = git_token or self.config.secrets.github_token
35+
self.existing = global_config.get_session(repo_path) is not None
36+
37+
self._initialize()
2438
global_config.set_active_session(repo_path)
2539

2640
@classmethod
@@ -31,19 +45,52 @@ def from_active_session(cls) -> "CodegenSession | None":
3145

3246
return cls(active_session)
3347

34-
def is_valid(self) -> bool:
35-
"""Validates that the session configuration is correct"""
36-
# TODO: also make sure all the expected prompt, jupyter, codemods are present
37-
# TODO: make sure there is still a git instance here.
38-
return self.repo_path.exists() and self.codegen_dir.exists() and Path(self.config.file_path).exists()
39-
40-
@property
41-
def git_repo(self) -> Repository:
42-
git_repo = get_git_repo(Path.cwd())
43-
if not git_repo:
44-
msg = "No git repository found"
45-
raise ValueError(msg)
46-
return git_repo
48+
def _initialize(self) -> None:
49+
"""Initialize the codegen session"""
50+
self._validate()
51+
52+
self.config.repository.repo_path = self.config.repository.repo_path or str(self.local_git.repo_path)
53+
self.config.repository.repo_name = self.config.repository.repo_name or self.local_git.name
54+
self.config.repository.full_name = self.config.repository.full_name or self.local_git.full_name
55+
self.config.repository.user_name = self.config.repository.user_name or self.local_git.user_name
56+
self.config.repository.user_email = self.config.repository.user_email or self.local_git.user_email
57+
self.config.repository.language = self.config.repository.language or self.local_git.get_language(access_token=self.config.secrets.github_token).upper()
58+
self.config.save()
59+
60+
def _validate(self) -> None:
61+
"""Validates that the session configuration is correct, otherwise raises an error"""
62+
if not self.codegen_dir.exists():
63+
rich.print(f"\n[bold red]Error:[/bold red] Codegen folder is missing at {self.codegen_dir}")
64+
raise click.Abort()
65+
66+
if not Path(self.config.file_path).exists():
67+
rich.print(f"\n[bold red]Error:[/bold red] Missing config.toml at {self.codegen_dir}")
68+
rich.print("[white]Please remove the codegen folder and reinitialize.[/white]")
69+
rich.print(format_command(f"rm -rf {self.codegen_dir} && codegen init"))
70+
raise click.Abort()
71+
72+
git_token = self.config.secrets.github_token
73+
if git_token is None:
74+
rich.print("\n[bold yellow]Warning:[/bold yellow] GitHub token not found")
75+
rich.print("To enable full functionality, please set your GitHub token:")
76+
rich.print(format_command("export CODEGEN_SECRETS__GITHUB_TOKEN=<your-token>"))
77+
rich.print("Or pass in as a parameter:")
78+
rich.print(format_command("codegen init --token <your-token>"))
79+
raise click.Abort()
80+
81+
if self.local_git.origin_remote is None:
82+
rich.print("\n[bold red]Error:[/bold red] No remote found for repository")
83+
rich.print("[white]Please add a remote to the repository.[/white]")
84+
rich.print("\n[dim]To add a remote to the repository:[/dim]")
85+
rich.print(format_command("git remote add origin <your-repo-url>"))
86+
raise click.Abort()
87+
88+
try:
89+
Github(login_or_token=git_token).get_repo(self.local_git.full_name)
90+
except BadCredentialsException:
91+
rich.print(format_command(f"\n[bold red]Error:[/bold red] Invalid GitHub token={git_token} for repo={self.local_git.full_name}"))
92+
rich.print("[white]Please provide a valid GitHub token for this repository.[/white]")
93+
raise click.Abort()
4794

4895
def __str__(self) -> str:
4996
return f"CodegenSession(user={self.config.repository.user_name}, repo={self.config.repository.repo_name})"

src/codegen/cli/commands/init/main.py

Lines changed: 5 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,11 @@
33

44
import rich
55
import rich_click as click
6-
from github import BadCredentialsException
7-
from github.MainClass import Github
86

97
from codegen.cli.auth.session import CodegenSession
108
from codegen.cli.commands.init.render import get_success_message
119
from codegen.cli.rich.codeblocks import format_command
1210
from codegen.cli.workspace.initialize_workspace import initialize_codegen
13-
from codegen.git.repo_operator.local_git_repo import LocalGitRepo
1411
from codegen.git.utils.path import get_git_root_path
1512

1613

@@ -25,6 +22,7 @@ def init_command(path: str | None = None, token: str | None = None, language: st
2522
path = Path.cwd() if path is None else Path(path)
2623
repo_path = get_git_root_path(path)
2724
rich.print(f"Found git repository at: {repo_path}")
25+
2826
if repo_path is None:
2927
rich.print(f"\n[bold red]Error:[/bold red] Path={path} is not in a git repository")
3028
rich.print("[white]Please run this command from within a git repository.[/white]")
@@ -33,45 +31,10 @@ def init_command(path: str | None = None, token: str | None = None, language: st
3331
rich.print(format_command("codegen init"))
3432
sys.exit(1)
3533

36-
local_git = LocalGitRepo(repo_path=repo_path)
37-
if local_git.origin_remote is None:
38-
rich.print("\n[bold red]Error:[/bold red] No remote found for repository")
39-
rich.print("[white]Please add a remote to the repository.[/white]")
40-
rich.print("\n[dim]To add a remote to the repository:[/dim]")
41-
rich.print(format_command("git remote add origin <your-repo-url>"))
42-
sys.exit(1)
43-
44-
session = CodegenSession(repo_path=repo_path)
45-
config = session.config
46-
47-
if token is None:
48-
token = config.secrets.github_token
49-
else:
50-
config.secrets.github_token = token
51-
52-
if not token:
53-
rich.print("\n[bold yellow]Warning:[/bold yellow] GitHub token not found")
54-
rich.print("To enable full functionality, please set your GitHub token:")
55-
rich.print(format_command("export CODEGEN_SECRETS__GITHUB_TOKEN=<your-token>"))
56-
rich.print("Or pass in as a parameter:")
57-
rich.print(format_command("codegen init --token <your-token>"))
58-
else:
59-
# Validate that the token is valid for the repo path.
60-
try:
61-
Github(login_or_token=token).get_repo(local_git.full_name)
62-
except BadCredentialsException:
63-
rich.print(format_command(f"\n[bold red]Error:[/bold red] Invalid GitHub token={token} for repo={local_git.full_name}"))
64-
rich.print("[white]Please provide a valid GitHub token for this repository.[/white]")
65-
sys.exit(1)
66-
67-
# Save repo config
68-
config.repository.repo_path = str(local_git.repo_path)
69-
config.repository.repo_name = local_git.name
70-
config.repository.full_name = local_git.full_name
71-
config.repository.user_name = local_git.user_name
72-
config.repository.user_email = local_git.user_email
73-
config.repository.language = (language or local_git.get_language(access_token=token)).upper()
74-
config.save()
34+
session = CodegenSession(repo_path=repo_path, git_token=token)
35+
if language:
36+
session.config.repository.language = language.upper()
37+
session.config.save()
7538

7639
action = "Updating" if session.existing else "Initializing"
7740
codegen_dir, docs_dir, examples_dir = initialize_codegen(status=action, session=session, fetch_docs=fetch_docs)

src/codegen/cli/commands/run/run_cloud.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import rich
44
import rich_click as click
5+
from pygit2._pygit2 import Repository
56
from rich.panel import Panel
67

78
from codegen.cli.api.client import RestAPI
@@ -87,7 +88,8 @@ def run_cloud(session: CodegenSession, function, apply_local: bool = False, diff
8788

8889
if apply_local and run_output.observation:
8990
try:
90-
apply_patch(session.git_repo, f"\n{run_output.observation}\n")
91+
git_repo = Repository(str(session.repo_path))
92+
apply_patch(git_repo, f"\n{run_output.observation}\n")
9193
rich.print("")
9294
rich.print("[green]✓ Changes have been applied to your local filesystem[/green]")
9395
rich.print("[yellow]→ Don't forget to commit your changes:[/yellow]")

src/codegen/cli/git/url.py

Lines changed: 0 additions & 11 deletions
This file was deleted.

src/codegen/cli/workspace/decorators.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
import sys
33
from collections.abc import Callable
44

5-
import click
6-
75
from codegen.cli.auth.session import CodegenSession
86
from codegen.cli.rich.pretty_print import pretty_print_error
97

@@ -19,11 +17,6 @@ def wrapper(*args, **kwargs):
1917
pretty_print_error("Codegen not initialized. Please run `codegen init` from a git repo workspace.")
2018
sys.exit(1)
2119

22-
# Check for valid session
23-
if not session.is_valid():
24-
pretty_print_error(f"The session at path {session.repo_path} is missing or corrupt.\nPlease run 'codegen init' to re-initialize the project.")
25-
raise click.Abort()
26-
2720
kwargs["session"] = session
2821
return f(*args, **kwargs)
2922

src/codegen/cli/workspace/initialize_workspace.py

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,13 @@
44

55
import requests
66
import rich
7-
import toml
87
from rich.status import Status
98

109
from codegen.cli.api.client import RestAPI
10+
from codegen.cli.api.endpoints import CODEGEN_SYSTEM_PROMPT_URL
1111
from codegen.cli.auth.constants import CODEGEN_DIR, DOCS_DIR, EXAMPLES_DIR, PROMPTS_DIR
1212
from codegen.cli.auth.session import CodegenSession
1313
from codegen.cli.auth.token_manager import get_current_token
14-
from codegen.cli.git.repo import get_git_repo
15-
from codegen.cli.git.url import get_git_organization_and_repo
1614
from codegen.cli.rich.spinners import create_spinner
1715
from codegen.cli.utils.notebooks import create_notebook
1816
from codegen.cli.workspace.docs_workspace import populate_api_docs
@@ -31,12 +29,10 @@ def initialize_codegen(session: CodegenSession, status: Status | str = "Initiali
3129
Returns:
3230
Tuple of (codegen_folder, docs_folder, examples_folder)
3331
"""
34-
repo = get_git_repo()
3532
CODEGEN_FOLDER = session.repo_path / CODEGEN_DIR
3633
PROMPTS_FOLDER = session.repo_path / PROMPTS_DIR
3734
DOCS_FOLDER = session.repo_path / DOCS_DIR
3835
EXAMPLES_FOLDER = session.repo_path / EXAMPLES_DIR
39-
CONFIG_PATH = CODEGEN_FOLDER / "config.toml"
4036
JUPYTER_DIR = CODEGEN_FOLDER / "jupyter"
4137
CODEMODS_DIR = CODEGEN_FOLDER / "codemods"
4238
SYSTEM_PROMPT_PATH = CODEGEN_FOLDER / "codegen-system-prompt.txt"
@@ -62,35 +58,17 @@ def initialize_codegen(session: CodegenSession, status: Status | str = "Initiali
6258

6359
# Download system prompt
6460
try:
65-
from codegen.cli.api.endpoints import CODEGEN_SYSTEM_PROMPT_URL
66-
6761
response = requests.get(CODEGEN_SYSTEM_PROMPT_URL)
6862
response.raise_for_status()
6963
SYSTEM_PROMPT_PATH.write_text(response.text)
7064
except Exception as e:
7165
rich.print(f"[yellow]Warning: Could not download system prompt: {e}[/yellow]")
7266

73-
if not repo:
74-
rich.print("No git repository found. Please run this command in a git repository.")
75-
else:
76-
status_obj.update(f" {'Updating' if isinstance(status, Status) else status} .gitignore...")
77-
modify_gitignore(CODEGEN_FOLDER)
78-
79-
# Create or update config.toml with basic repo info
80-
org_name, repo_name = get_git_organization_and_repo(repo)
81-
config = {}
82-
if CONFIG_PATH.exists():
83-
config = toml.load(CONFIG_PATH)
84-
config.update(
85-
{
86-
"organization_name": config.get("organization_name", org_name),
87-
"repo_name": config.get("repo_name", repo_name),
88-
}
89-
)
90-
CONFIG_PATH.write_text(toml.dumps(config))
91-
92-
# Create notebook template
93-
create_notebook(JUPYTER_DIR)
67+
status_obj.update(f" {'Updating' if isinstance(status, Status) else status} .gitignore...")
68+
modify_gitignore(CODEGEN_FOLDER)
69+
70+
# Create notebook template
71+
create_notebook(JUPYTER_DIR)
9472

9573
# Only fetch docs and examples if requested and session is provided
9674
if fetch_docs and session:

src/codegen/git/repo_operator/local_git_repo.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from functools import cached_property
33
from pathlib import Path
44

5+
import giturlparse
56
from git import Repo
67
from git.remote import Remote
78

@@ -30,8 +31,8 @@ def full_name(self) -> str | None:
3031
if not self.origin_remote:
3132
return None
3233

33-
url_segments = self.origin_remote.url.split("/")
34-
return f"{url_segments[-2]}/{url_segments[-1].replace('.git', '')}"
34+
parsed = giturlparse.parse(self.origin_remote.url)
35+
return f"{parsed.owner}/{parsed.name}"
3536

3637
@cached_property
3738
def origin_remote(self) -> Remote | None:

0 commit comments

Comments
 (0)