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
89 changes: 67 additions & 22 deletions scripts/evolve/daily_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,67 @@ def _remote_branch_exists(remote: str, branch: str, cwd: Path) -> bool:
return bool(output)


def _configure_bot_identity(clone: Path) -> None:
_run(
[
"git",
"config",
"user.name",
os.environ.get("WIKI_BOT_NAME", "rocmkernelwiki-evolution[bot]"),
],
cwd=clone,
)
_run(
[
"git",
"config",
"user.email",
os.environ.get(
"WIKI_BOT_EMAIL",
"rocmkernelwiki-evolution[bot]@users.noreply.github.com",
),
],
cwd=clone,
)


def _sync_with_base(
clone: Path,
*,
base: str,
branch: str,
source_branch: str,
) -> None:
if source_branch != branch:
_run(["git", "switch", "-c", branch], cwd=clone)
return
# A rolling branch owns the discovery watermarks, but executable controller
# code and tests must always come from the latest protected base branch.
_run(
[
"git",
"fetch",
"origin",
f"+{base}:refs/remotes/origin/{base}",
],
cwd=clone,
)
try:
_run(["git", "rebase", f"origin/{base}"], cwd=clone)
except RuntimeError:
# The clone is disposable, but abort explicitly so diagnostics see a
# clean branch and no caller can accidentally continue in a conflicted
# rebase state.
subprocess.run(
["git", "rebase", "--abort"],
cwd=clone,
capture_output=True,
text=True,
check=False,
)
raise


def run_daily(args: argparse.Namespace) -> dict[str, str]:
run_date = args.run_date or date.today().isoformat()
branch = args.branch or rolling_branch(run_date)
Expand All @@ -74,28 +135,12 @@ def run_daily(args: argparse.Namespace) -> dict[str, str]:
],
cwd=Path(directory),
)
if source_branch != branch:
_run(["git", "switch", "-c", branch], cwd=clone)
_run(
[
"git",
"config",
"user.name",
os.environ.get("WIKI_BOT_NAME", "rocmkernelwiki-evolution[bot]"),
],
cwd=clone,
)
_run(
[
"git",
"config",
"user.email",
os.environ.get(
"WIKI_BOT_EMAIL",
"rocmkernelwiki-evolution[bot]@users.noreply.github.com",
),
],
cwd=clone,
_configure_bot_identity(clone)
_sync_with_base(
clone,
base=args.base,
branch=branch,
source_branch=source_branch,
)

refresh = [
Expand Down
9 changes: 8 additions & 1 deletion scripts/evolve/draft_pr.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ def _run(
return result.stdout.strip()


def _push_branch(root: Path, branch: str) -> None:
_run(
["git", "push", "--force-with-lease", "-u", "origin", branch],
root=root,
)


def publish_draft(
*,
root: Path,
Expand Down Expand Up @@ -115,7 +122,7 @@ def publish_draft(
],
root=root,
)
_run(["git", "push", "--force-with-lease", "-u", "origin", branch], root=root)
_push_branch(root, branch)
existing = _run(
[
"gh",
Expand Down
171 changes: 171 additions & 0 deletions tests/test_evolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,177 @@ def test_final_summary_is_inside_the_enforced_budget():
]


def test_rolling_worker_rebases_state_onto_latest_main():
from evolve.daily_worker import _configure_bot_identity, _sync_with_base
from evolve.draft_pr import _push_branch

def git(cwd, *arguments):
return subprocess.run(
["git", *arguments],
cwd=cwd,
check=True,
capture_output=True,
text=True,
).stdout.strip()

with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
source = root / "source"
remote = root / "remote.git"
clone = root / "clone"
git(root, "init", "-q", "-b", "main", str(source))
(source / "base.txt").write_text("base\n", encoding="utf-8")
git(source, "add", "base.txt")
git(
source,
"-c",
"user.name=Test",
"-c",
"user.email=test@example.com",
"commit",
"-q",
"-m",
"base",
)
git(root, "init", "--bare", "-q", str(remote))
git(source, "remote", "add", "origin", str(remote))
git(source, "push", "-q", "-u", "origin", "main")

git(source, "switch", "-q", "-c", "bot/evolution")
(source / "state.txt").write_text("watermark\n", encoding="utf-8")
git(source, "add", "state.txt")
git(
source,
"-c",
"user.name=Bot",
"-c",
"user.email=bot@example.com",
"commit",
"-q",
"-m",
"state",
)
git(source, "push", "-q", "-u", "origin", "bot/evolution")

git(source, "switch", "-q", "main")
(source / "controller.txt").write_text("fixed controller\n", encoding="utf-8")
git(source, "add", "controller.txt")
git(
source,
"-c",
"user.name=Test",
"-c",
"user.email=test@example.com",
"commit",
"-q",
"-m",
"controller fix",
)
git(source, "push", "-q", "origin", "main")

git(root, "clone", "-q", "--branch", "bot/evolution", str(remote), str(clone))
_configure_bot_identity(clone)
_sync_with_base(
clone,
base="main",
branch="bot/evolution",
source_branch="bot/evolution",
)
assert git(clone, "branch", "--show-current") == "bot/evolution"
assert git(clone, "show", "HEAD:state.txt") == "watermark"
assert git(clone, "show", "HEAD:controller.txt") == "fixed controller"
subprocess.run(
["git", "merge-base", "--is-ancestor", "origin/main", "HEAD"],
cwd=clone,
check=True,
)
_push_branch(clone, "bot/evolution")
assert git(clone, "rev-parse", "HEAD") == git(
remote, "rev-parse", "refs/heads/bot/evolution"
)

new_clone = root / "new-clone"
git(root, "clone", "-q", "--branch", "main", str(remote), str(new_clone))
_configure_bot_identity(new_clone)
_sync_with_base(
new_clone,
base="main",
branch="bot/new-evolution",
source_branch="main",
)
assert git(new_clone, "branch", "--show-current") == "bot/new-evolution"
assert git(new_clone, "rev-parse", "HEAD") == git(
new_clone, "rev-parse", "origin/main"
)


def test_rolling_worker_aborts_conflicted_base_sync():
from evolve.daily_worker import _configure_bot_identity, _sync_with_base

def git(cwd, *arguments):
return subprocess.run(
["git", *arguments],
cwd=cwd,
check=True,
capture_output=True,
text=True,
).stdout.strip()

def commit(cwd, message):
git(cwd, "add", "conflict.txt")
git(
cwd,
"-c",
"user.name=Test",
"-c",
"user.email=test@example.com",
"commit",
"-q",
"-m",
message,
)

with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
source = root / "source"
remote = root / "remote.git"
clone = root / "clone"
git(root, "init", "-q", "-b", "main", str(source))
(source / "conflict.txt").write_text("base\n", encoding="utf-8")
commit(source, "base")
git(root, "init", "--bare", "-q", str(remote))
git(source, "remote", "add", "origin", str(remote))
git(source, "push", "-q", "-u", "origin", "main")

git(source, "switch", "-q", "-c", "bot/evolution")
(source / "conflict.txt").write_text("bot\n", encoding="utf-8")
commit(source, "bot state")
git(source, "push", "-q", "-u", "origin", "bot/evolution")
git(source, "switch", "-q", "main")
(source / "conflict.txt").write_text("main\n", encoding="utf-8")
commit(source, "main update")
git(source, "push", "-q", "origin", "main")

git(root, "clone", "-q", "--branch", "bot/evolution", str(remote), str(clone))
_configure_bot_identity(clone)
old_head = git(clone, "rev-parse", "HEAD")
try:
_sync_with_base(
clone,
base="main",
branch="bot/evolution",
source_branch="bot/evolution",
)
except RuntimeError:
pass
else:
raise AssertionError("conflicted rebase unexpectedly succeeded")
assert git(clone, "rev-parse", "HEAD") == old_head
assert git(clone, "status", "--porcelain=v1") == ""
assert not (clone / ".git" / "rebase-merge").exists()
assert not (clone / ".git" / "rebase-apply").exists()


def test_query_marks_upstream_pr_snippets_as_untrusted():
import query

Expand Down
Loading