Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/bot/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "automa-bot"
version = "0.3.0"
version = "0.4.0"

authors = [{ name = "Sunkara, Inc.", email = "engineering@automa.app" }]
description = "Bot SDK for Automa"
Expand Down
53 changes: 31 additions & 22 deletions packages/bot/src/automa/bot/resources/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,15 @@


# TODO: Use programmatic git instead of git command
def get_diff(path: str) -> str:
def get_diff(path: str, base_commit: str | None = None) -> str:
"""Get the diff for the given git repository path."""
args = ["git", "diff"]

if base_commit:
args.append(base_commit)

result = subprocess.run(
["git", "diff"],
args,
cwd=path,
capture_output=True,
text=True,
Expand Down Expand Up @@ -78,12 +83,12 @@ def _read_token(self, folder: str) -> str | None:

return token

def _read_base_commit(self, folder: str) -> str | None:
def _read_base_commit(self, folder: str, type: str) -> str | None:
base_commit = None

try:
with open(
f"{folder}/.git/automa_proposal_base_commit", "r", encoding="utf8"
f"{folder}/.git/automa_{type}_base_commit", "r", encoding="utf8"
) as f:
base_commit = f.read().strip()
except FileNotFoundError:
Expand All @@ -103,16 +108,8 @@ def _clone_code(self, folder: str, url: str) -> None:
check=True,
)

# Note down the base commit
result = subprocess.run(
["git", "rev-parse", "HEAD"],
cwd=path,
capture_output=True,
text=True,
check=True,
)

self._write_base_commit(folder, result.stdout.strip())
# Note down the base commit to send in proposal
self._write_base_commit(folder, "proposal")

def _extract_download(self, folder: str) -> None:
rmtree(folder, ignore_errors=True)
Expand All @@ -121,17 +118,27 @@ def _extract_download(self, folder: str) -> None:
with tarfile.open(f"{folder}.tar.gz", "r:gz") as tar:
tar.extractall(path=folder)

# Note down the base commit to be used in generating diff
self._write_base_commit(folder, "diff")

def _write_token(self, folder: str, token: str) -> None:
# Save the proposal token for later use
with open(f"{folder}/.git/automa_proposal_token", "w", encoding="utf8") as f:
f.write(token)

def _write_base_commit(self, folder: str, base_commit: str) -> None:
# Save the base commit for later use
def _write_base_commit(self, folder: str, type: str) -> None:
result = subprocess.run(
["git", "rev-parse", "HEAD"],
cwd=folder,
capture_output=True,
text=True,
check=True,
)

with open(
f"{folder}/.git/automa_proposal_base_commit", "w", encoding="utf8"
f"{folder}/.git/automa_{type}_base_commit", "w", encoding="utf8"
) as f:
f.write(base_commit)
f.write(result.stdout.strip())


class CodeResource(SyncAPIResource, BaseCodeResource):
Expand Down Expand Up @@ -202,12 +209,13 @@ def propose(
):
path = self._path(body["task"])
token = self._read_token(path)
base_commit = self._read_base_commit(path)
base_commit = self._read_base_commit(path, "proposal")
diff_base_commit = self._read_base_commit(path, "diff")

if not token:
raise ValueError("Failed to read the stored proposal token")

diff = get_diff(path)
diff = get_diff(path, base_commit or diff_base_commit)

return self._client.post(
"/bot/code/propose",
Expand Down Expand Up @@ -295,12 +303,13 @@ async def propose(
):
path = self._path(body["task"])
token = await to_thread(self._read_token, path)
base_commit = await to_thread(self._read_base_commit, path)
base_commit = await to_thread(self._read_base_commit, path, "proposal")
diff_base_commit = await to_thread(self._read_base_commit, path, "diff")

if not token:
raise ValueError("Failed to read the stored proposal token")

diff = await to_thread(get_diff, path)
diff = await to_thread(get_diff, path, base_commit or diff_base_commit)

return await self._client.post(
"/bot/code/propose",
Expand Down
Empty file.
5 changes: 5 additions & 0 deletions packages/bot/tests/fixtures/download/_git/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
Binary file added packages/bot/tests/fixtures/download/_git/index
Binary file not shown.
1 change: 1 addition & 0 deletions packages/bot/tests/fixtures/download/_git/logs/HEAD
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0000000000000000000000000000000000000000 5575f40bc4ead411b690b6f2f09636e3468ef12e automa[bot] <60525818+automa[bot]@users.noreply.github.com> 1772718655 +0530 commit (initial): Downloaded code
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0000000000000000000000000000000000000000 5575f40bc4ead411b690b6f2f09636e3468ef12e automa[bot] <60525818+automa[bot]@users.noreply.github.com> 1772718655 +0530 commit (initial): Downloaded code
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
5575f40bc4ead411b690b6f2f09636e3468ef12e
Binary file removed packages/bot/tests/fixtures/download_git/index
Binary file not shown.
Binary file not shown.
Binary file not shown.
1 change: 0 additions & 1 deletion packages/bot/tests/fixtures/download_git/refs/heads/master

This file was deleted.

98 changes: 82 additions & 16 deletions packages/bot/tests/resources/test_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
folder = "/tmp/automa/tasks/28"
proposal_token_file = f"{folder}/.git/automa_proposal_token"
proposal_base_commit_file = f"{folder}/.git/automa_proposal_base_commit"
diff_base_commit_file = f"{folder}/.git/automa_diff_base_commit"


@pytest.fixture
Expand Down Expand Up @@ -45,7 +46,7 @@ def async_code_resource():
@pytest.fixture
def fixture_git():
tests_folder = Path(__file__).parent.parent
git_path = tests_folder / "fixtures" / "download_git"
git_path = tests_folder / "fixtures" / "download" / "_git"

yield git_path

Expand All @@ -55,23 +56,20 @@ def fixture_tarfile():
tests_folder = Path(__file__).parent.parent
fixture = tests_folder / "fixtures" / "download"
tarfile_path = tests_folder / "fixture.tar.gz"
git_path = fixture / ".git"
_git_path = fixture / "_git"

run(["git", "init"], cwd=fixture, capture_output=True)
run(["git", "add", "."], cwd=fixture, capture_output=True)
run(["git", "config", "user.name", "Tmp"], cwd=fixture, capture_output=True)
run(
["git", "config", "user.email", "tmp@tmp.com"], cwd=fixture, capture_output=True
)
run(["git", "commit", "-m", "Initial commit"], cwd=fixture, capture_output=True)
_git_path.rename(git_path)

with tarfile.open(tarfile_path, "w:gz") as tar:
for subpath in listdir(fixture):
tar.add(fixture / subpath, arcname=subpath)

git_path.rename(_git_path)

yield tarfile_path

remove(tarfile_path)
rmtree(fixture / ".git", ignore_errors=True)


def test_cleanup_proxy(code_resource):
Expand Down Expand Up @@ -154,7 +152,7 @@ def test_download_invalid_token(code_resource):


@pytest.mark.asyncio
async def test_download_async_invalid_token(async_code_resource):
async def test_download_invalid_token_async(async_code_resource):
# Mock client response
response_mock = MagicMock()
response_mock.status_code = 403
Expand Down Expand Up @@ -299,13 +297,19 @@ def test_download_proxy(fixture_tarfile, code_resource):
assert path.exists(folder)
assert sorted(listdir(folder)) == [
".git",
"LICENSE",
"README.md",
]

# Saves proposal token
with open(proposal_token_file, "r") as f:
assert f.read() == "ghijkl"

# Saves diff base commit
assert not path.exists(proposal_base_commit_file)
with open(diff_base_commit_file, "r") as f:
assert f.read() == "5575f40bc4ead411b690b6f2f09636e3468ef12e"

return created_folder


Expand Down Expand Up @@ -352,13 +356,19 @@ async def test_download_proxy_async(fixture_tarfile, async_code_resource):
assert path.exists(folder)
assert sorted(listdir(folder)) == [
".git",
"LICENSE",
"README.md",
]

# Saves proposal token
with open(proposal_token_file, "r") as f:
assert f.read() == "ghijkl"

# Saves diff base commit
assert not path.exists(proposal_base_commit_file)
with open(diff_base_commit_file, "r") as f:
assert f.read() == "5575f40bc4ead411b690b6f2f09636e3468ef12e"


def test_propose_no_token(fixture_tarfile, code_resource):
test_download_proxy(fixture_tarfile, code_resource)
Expand All @@ -373,7 +383,7 @@ def test_propose_no_token(fixture_tarfile, code_resource):


@pytest.mark.asyncio
async def test_propose_async_no_token(fixture_tarfile, async_code_resource):
async def test_propose_no_token_async(fixture_tarfile, async_code_resource):
await test_download_proxy_async(fixture_tarfile, async_code_resource)

remove(proposal_token_file)
Expand Down Expand Up @@ -421,7 +431,7 @@ def test_propose_invalid_token(fixture_tarfile, code_resource):


@pytest.mark.asyncio
async def test_propose_async_invalid_token(fixture_tarfile, async_code_resource):
async def test_propose_invalid_token_async(fixture_tarfile, async_code_resource):
await test_download_proxy_async(fixture_tarfile, async_code_resource)

with open(f"{folder}/README.md", "w") as f:
Expand Down Expand Up @@ -593,6 +603,58 @@ def test_propose_with_added_files_using_add_all(fixture_tarfile, code_resource):
)


def test_propose_with_intermediate_commits(fixture_tarfile, code_resource):
test_download_proxy(fixture_tarfile, code_resource)

with open(f"{folder}/LICENSE", "w") as f:
f.write("MIT\n")

run(["git", "add", "LICENSE"], cwd=folder, capture_output=True)
run(
[
"git",
"-c",
"user.name=Tmp",
"-c",
"user.email=tmp@tmp.com",
"commit",
"-m",
"Intermediate commit",
],
cwd=folder,
capture_output=True,
)

with open(f"{folder}/README.md", "w") as f:
f.write("Content\n")

# Mock client response
response_mock = MagicMock()
response_mock.status_code = 204
response_mock.is_error = False

code_resource._client._client.request.return_value = response_mock

code_resource.propose({"task": {"id": 28, "token": "abcdef"}})

# Hits the API
code_resource._client._client.request.assert_called_once_with(
"post",
"/bot/code/propose",
json={
"task": {"id": 28, "token": "abcdef"},
"proposal": {
"token": "ghijkl",
"diff": "diff --git a/LICENSE b/LICENSE\nindex e69de29..a22a2da 100644\n--- a/LICENSE\n+++ b/LICENSE\n@@ -0,0 +1 @@\n+MIT\ndiff --git a/README.md b/README.md\nindex e69de29..39c9f36 100644\n--- a/README.md\n+++ b/README.md\n@@ -0,0 +1 @@\n+Content\n",
},
},
headers={
"Accept": "application/json",
"Content-Type": "application/json",
},
)


def test_propose_with_proposal_properties(fixture_tarfile, code_resource):
test_download_proxy(fixture_tarfile, code_resource)

Expand Down Expand Up @@ -710,6 +772,7 @@ def test_download_direct(fixture_git, code_resource):
assert path.exists(folder)
assert sorted(listdir(folder)) == [
".git",
"LICENSE",
"README.md",
]

Expand All @@ -719,7 +782,8 @@ def test_download_direct(fixture_git, code_resource):

# Saves the base commit
with open(proposal_base_commit_file, "r") as f:
assert f.read() == "cc3f46ae7fdf71747b66b3e4272c0e5fe290d116"
assert f.read() == "5575f40bc4ead411b690b6f2f09636e3468ef12e"
assert not path.exists(diff_base_commit_file)


@pytest.mark.asyncio
Expand Down Expand Up @@ -766,6 +830,7 @@ async def test_download_direct_async(fixture_git, async_code_resource):
assert path.exists(folder)
assert sorted(listdir(folder)) == [
".git",
"LICENSE",
"README.md",
]

Expand All @@ -775,7 +840,8 @@ async def test_download_direct_async(fixture_git, async_code_resource):

# Saves the base commit
with open(proposal_base_commit_file, "r") as f:
assert f.read() == "cc3f46ae7fdf71747b66b3e4272c0e5fe290d116"
assert f.read() == "5575f40bc4ead411b690b6f2f09636e3468ef12e"
assert not path.exists(diff_base_commit_file)


def test_propose_direct(fixture_git, code_resource):
Expand All @@ -802,7 +868,7 @@ def test_propose_direct(fixture_git, code_resource):
"proposal": {
"token": "ghijkl",
"diff": "diff --git a/README.md b/README.md\nindex e69de29..39c9f36 100644\n--- a/README.md\n+++ b/README.md\n@@ -0,0 +1 @@\n+Content\n",
"base_commit": "cc3f46ae7fdf71747b66b3e4272c0e5fe290d116",
"base_commit": "5575f40bc4ead411b690b6f2f09636e3468ef12e",
},
},
headers={
Expand Down Expand Up @@ -837,7 +903,7 @@ async def test_propose_direct_async(fixture_git, async_code_resource):
"proposal": {
"token": "ghijkl",
"diff": "diff --git a/README.md b/README.md\nindex e69de29..39c9f36 100644\n--- a/README.md\n+++ b/README.md\n@@ -0,0 +1 @@\n+Content\n",
"base_commit": "cc3f46ae7fdf71747b66b3e4272c0e5fe290d116",
"base_commit": "5575f40bc4ead411b690b6f2f09636e3468ef12e",
},
},
headers={
Expand Down
4 changes: 2 additions & 2 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.