Skip to content

Commit 3815bbc

Browse files
authored
feat: sync changes to local state before run (#696)
1 parent 03f9831 commit 3815bbc

File tree

10 files changed

+37
-35
lines changed

10 files changed

+37
-35
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def set_command(key: str, value: str):
104104
return
105105

106106
cur_value = config.get(key)
107-
if cur_value is None or cur_value.lower() != value.lower():
107+
if cur_value is None or str(cur_value).lower() != value.lower():
108108
try:
109109
config.set(key, value)
110110
except Exception as e:

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
@requires_init
1515
@click.argument("label", required=True)
1616
@click.option("--web", is_flag=True, help="Run the function on the web service instead of locally")
17-
@click.option("--daemon", is_flag=True, help="Run the function against a running daemon")
17+
@click.option("--daemon", "-d", is_flag=True, help="Run the function against a running daemon")
1818
@click.option("--diff-preview", type=int, help="Show a preview of the first N lines of the diff")
1919
@click.option("--arguments", type=str, help="Arguments as a json string to pass as the function's 'arguments' parameter")
2020
def run_command(

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ def _run_docker_container(repo_config: RepoConfig, port: int, detached: bool) ->
132132
"REPOSITORY_PATH": container_repo_path,
133133
"GITHUB_TOKEN": SecretsConfig().github_token,
134134
"PYTHONUNBUFFERED": "1", # Ensure Python output is unbuffered
135+
"CODEBASE_SYNC_ENABLED": "True",
135136
}
136137
envvars_args = [arg for k, v in envvars.items() for arg in ("--env", f"{k}={v}")]
137138
mount_args = ["-v", f"{repo_config.repo_path}:{container_repo_path}"]

src/codegen/git/repo_operator/repo_operator.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,12 +458,18 @@ def get_diffs(self, ref: str | GitCommit, reverse: bool = True) -> list[Diff]:
458458
return [diff for diff in self.git_cli.index.diff(ref, R=reverse)]
459459

460460
@stopwatch
461-
def stage_and_commit_all_changes(self, message: str, verify: bool = False) -> bool:
461+
def stage_and_commit_all_changes(self, message: str, verify: bool = False, exclude_paths: list[str] | None = None) -> bool:
462462
"""TODO: rename to stage_and_commit_changes
463463
Stage all changes and commit them with the given message.
464464
Returns True if a commit was made and False otherwise.
465465
"""
466466
self.git_cli.git.add(A=True)
467+
# Unstage the excluded paths
468+
for path in exclude_paths or []:
469+
try:
470+
self.git_cli.git.reset("HEAD", "--", path)
471+
except GitCommandError as e:
472+
logger.warning(f"Failed to exclude path {path}: {e}")
467473
return self.commit_changes(message, verify)
468474

469475
def commit_changes(self, message: str, verify: bool = False) -> bool:

src/codegen/runner/sandbox/runner.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import sys
22

3-
from git import Commit as GitCommit
4-
3+
from codegen.configs.models.codebase import CodebaseConfig
54
from codegen.git.repo_operator.repo_operator import RepoOperator
65
from codegen.git.schemas.enums import SetupOption
76
from codegen.git.schemas.repo_config import RepoConfig
@@ -21,7 +20,6 @@ class SandboxRunner:
2120

2221
# =====[ __init__ instance attributes ]=====
2322
repo: RepoConfig
24-
commit: GitCommit
2523
op: RepoOperator | None
2624

2725
# =====[ computed instance attributes ]=====
@@ -31,20 +29,19 @@ class SandboxRunner:
3129
def __init__(self, repo_config: RepoConfig, op: RepoOperator | None = None) -> None:
3230
self.repo = repo_config
3331
self.op = op or RepoOperator(repo_config=self.repo, setup_option=SetupOption.PULL_OR_CLONE, bot_commit=True)
34-
self.commit = self.op.git_cli.head.commit
3532

36-
async def warmup(self) -> None:
33+
async def warmup(self, codebase_config: CodebaseConfig | None = None) -> None:
3734
"""Warms up this runner by cloning the repo and parsing the graph."""
3835
logger.info(f"===== Warming runner for {self.repo.full_name or self.repo.name} =====")
3936
sys.setrecursionlimit(10000) # for graph parsing
4037

41-
self.codebase = await self._build_graph()
38+
self.codebase = await self._build_graph(codebase_config)
4239
self.executor = SandboxExecutor(self.codebase)
4340

44-
async def _build_graph(self) -> Codebase:
41+
async def _build_graph(self, codebase_config: CodebaseConfig | None = None) -> Codebase:
4542
logger.info("> Building graph...")
4643
projects = [ProjectConfig(programming_language=self.repo.language, repo_operator=self.op, base_path=self.repo.base_path, subdirectories=self.repo.subdirectories)]
47-
return Codebase(projects=projects)
44+
return Codebase(projects=projects, config=codebase_config)
4845

4946
async def get_diff(self, request: GetDiffRequest) -> GetDiffResponse:
5047
custom_scope = {"context": request.codemod.codemod_context} if request.codemod.codemod_context else {}

src/codegen/runner/sandbox/server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ async def lifespan(server: FastAPI):
4545
runner = SandboxRunner(repo_config=repo_config)
4646
server_info.warmup_state = WarmupState.PENDING
4747
await runner.warmup()
48-
server_info.synced_commit = runner.commit.hexsha
48+
server_info.synced_commit = runner.op.git_cli.head.commit.hexsha
4949
server_info.warmup_state = WarmupState.COMPLETED
5050
except Exception:
5151
logger.exception("Failed to build graph during warmup")

src/codegen/runner/servers/local_daemon.py

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import logging
2-
import os
32
from contextlib import asynccontextmanager
43

54
from fastapi import FastAPI
65

6+
from codegen.configs.models.codebase import DefaultCodebaseConfig
77
from codegen.git.configs.constants import CODEGEN_BOT_EMAIL, CODEGEN_BOT_NAME
88
from codegen.git.repo_operator.repo_operator import RepoOperator
99
from codegen.git.schemas.enums import SetupOption
@@ -47,11 +47,12 @@ async def lifespan(server: FastAPI):
4747
runner.op.git_cli.git.config("user.email", CODEGEN_BOT_EMAIL)
4848
runner.op.git_cli.git.config("user.name", CODEGEN_BOT_NAME)
4949

50-
# Parse the codebase
50+
# Parse the codebase with sync enabled
5151
logger.info(f"Starting up fastapi server for repo_name={repo_config.name}")
5252
server_info.warmup_state = WarmupState.PENDING
53-
await runner.warmup()
54-
server_info.synced_commit = runner.commit.hexsha
53+
codebase_config = DefaultCodebaseConfig.model_copy(update={"sync_enabled": True})
54+
await runner.warmup(codebase_config=codebase_config)
55+
server_info.synced_commit = runner.op.head_commit.hexsha
5556
server_info.warmup_state = WarmupState.COMPLETED
5657

5758
except Exception:
@@ -73,27 +74,24 @@ def health() -> ServerInfo:
7374

7475
@app.post(RUN_FUNCTION_ENDPOINT)
7576
async def run(request: RunFunctionRequest) -> CodemodRunResult:
76-
# TODO: Sync graph to whatever changes are in the repo currently
77-
78-
# Run the request
77+
_save_uncommitted_changes_and_sync()
7978
diff_req = GetDiffRequest(codemod=Codemod(user_code=request.codemod_source))
8079
diff_response = await runner.get_diff(request=diff_req)
8180
if request.commit:
82-
if _should_skip_commit(request.function_name):
83-
logger.info(f"Skipping commit because only changes to {request.function_name} were made")
84-
elif commit_sha := runner.codebase.git_commit(f"[Codegen] {request.function_name}"):
81+
if commit_sha := runner.codebase.git_commit(f"[Codegen] {request.function_name}", exclude_paths=[".codegen/*"]):
8582
logger.info(f"Committed changes to {commit_sha.hexsha}")
8683
return diff_response.result
8784

8885

89-
def _should_skip_commit(function_name: str) -> bool:
90-
changed_files = runner.op.get_modified_files(runner.commit)
91-
if len(changed_files) != 1:
92-
return False
86+
def _save_uncommitted_changes_and_sync() -> None:
87+
if commit := runner.codebase.git_commit("[Codegen] Save uncommitted changes", exclude_paths=[".codegen/*"]):
88+
logger.info(f"Saved uncommitted changes to {commit.hexsha}")
9389

94-
file_path = changed_files[0]
95-
if not file_path.startswith(".codegen/codemods/"):
96-
return False
90+
cur_commit = runner.op.head_commit
91+
if cur_commit != runner.codebase.ctx.synced_commit:
92+
logger.info(f"Syncing codebase to head commit: {cur_commit.hexsha}")
93+
runner.codebase.sync_to_commit(target_commit=cur_commit)
94+
else:
95+
logger.info("Codebase is already synced to head commit")
9796

98-
changed_file_name = os.path.splitext(os.path.basename(file_path))[0]
99-
return changed_file_name == function_name.replace("-", "_")
97+
server_info.synced_commit = cur_commit.hexsha

src/codegen/sdk/core/codebase.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -734,7 +734,7 @@ def get_relative_path(self, from_file: str, to_file: str) -> str:
734734
# State/Git
735735
####################################################################################################################
736736

737-
def git_commit(self, message: str, *, verify: bool = False) -> GitCommit | None:
737+
def git_commit(self, message: str, *, verify: bool = False, exclude_paths: list[str] | None = None) -> GitCommit | None:
738738
"""Stages + commits all changes to the codebase and git.
739739
740740
Args:
@@ -745,7 +745,7 @@ def git_commit(self, message: str, *, verify: bool = False) -> GitCommit | None:
745745
GitCommit | None: The commit object if changes were committed, None otherwise.
746746
"""
747747
self.ctx.commit_transactions(sync_graph=False)
748-
if self._op.stage_and_commit_all_changes(message, verify):
748+
if self._op.stage_and_commit_all_changes(message, verify, exclude_paths):
749749
logger.info(f"Commited repository to {self._op.head_commit} on {self._op.get_active_branch_or_commit()}")
750750
return self._op.head_commit
751751
else:

src/codegen/shared/logging/get_logger.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def get_logger(name: str, level: int = logging.INFO) -> logging.Logger:
3535
handler.setFormatter(formatter)
3636
logger.addHandler(handler)
3737
# Ensure the logger propagates to the root logger
38-
logger.propagate = False
38+
logger.propagate = True
3939
# Set the level on the logger itself
4040
logger.setLevel(level)
4141
return logger

tests/unit/codegen/runner/sandbox/test_runner.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ async def test_sandbox_runner_warmup_starts_with_default_branch(mock_executor, r
4040
# assert len(runner.codebase._op.git_cli.branches) == 1 TODO: fix GHA creating master and main branch
4141
assert not runner.codebase._op.git_cli.head.is_detached
4242
assert runner.codebase._op.git_cli.active_branch.name == runner.codebase.default_branch
43-
assert runner.codebase._op.git_cli.head.commit == runner.commit
43+
assert runner.codebase._op.git_cli.head.commit == runner.op.head_commit
4444

4545

4646
@pytest.mark.asyncio

0 commit comments

Comments
 (0)