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
11 changes: 9 additions & 2 deletions src/repo2rlenv/pipelines/recipes/swe_smith/grade.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,13 @@ def validate_submission(workspace: Path, contract: dict) -> None:
):
raise ValueError("Non-Python source assets must remain unchanged")
if relative not in backups:
os.chown(path, 0, 0)
# os.chown doesn't exist on Windows. This module only ever runs for
# real inside the Linux verifier container (every other path here
# is an absolute container path); the guard exists so the tests
# that call validate_submission() directly can run on a Windows
# host without touching real container-only ownership semantics.
if sys.platform != "win32":
os.chown(path, 0, 0)
path.chmod(0o644)
# Only the verifier's collected copy is cleaned. The learner workspace and
# archived submission retain their bytes. Validate every path first so a
Expand Down Expand Up @@ -126,7 +132,8 @@ def main() -> None:
return
with tempfile.TemporaryDirectory(prefix="r2e-grade-") as temporary:
working = Path(temporary)
os.chown(working, 1001, 1001)
if sys.platform != "win32": # os.chown doesn't exist on Windows; see above
os.chown(working, 1001, 1001)
working.chmod(0o700)
report = working / "results.xml"
command = [
Expand Down
5 changes: 4 additions & 1 deletion src/repo2rlenv/tasksmith/source_patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@ def reverse_crlf_patch(root: Path, patch: Path) -> bool:
adapted_path = directory / "source.diff"
adapted_path.write_bytes(adapted)
result = subprocess.run(
["git", "apply", "--reverse", str(adapted_path.resolve())],
# core.autocrlf=false: `staged` isn't a git repository, so `git apply`
# would otherwise fall back to the caller's global config and risk
# re-normalizing the exact CRLF bytes this fallback just reconstructed.
["git", "-c", "core.autocrlf=false", "apply", "--reverse", str(adapted_path.resolve())],
cwd=staged,
capture_output=True,
text=True,
Expand Down
14 changes: 11 additions & 3 deletions src/repo2rlenv/tasksmith/worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,14 @@ def reverse_source(source: dict, base: Path, output: Path) -> tuple[Path, tuple[
path.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(base / relative, path)
patch = output / "source.diff"
patch.write_text(source["source_diff"])
patch.write_bytes(source["source_diff"].encode())
try:
run(["git", "apply", "--reverse", str(patch)], cwd=defective)
# -c core.autocrlf=false: `defective` isn't a git repository, so without
# an explicit override `git apply` falls back to the caller's global
# config. On a machine with the (very common, Git-for-Windows-default)
# `core.autocrlf=true`, that silently rewrites line endings while
# applying, corrupting the exact-byte reversal this function promises.
run(["git", "-c", "core.autocrlf=false", "apply", "--reverse", str(patch)], cwd=defective)
except ValueError:
if not reverse_crlf_patch(defective, patch):
raise
Expand Down Expand Up @@ -277,7 +282,10 @@ def construct(source: dict, profile: Profile, design: Design, ready: dict, outpu
"Private supplements require an existing tests/ directory and an unused tasksmith_behavior.py path"
)
local = output / "tasksmith_behavior.py"
local.write_text(design.additional_tests)
# write_bytes, not write_text: this file is re-read as raw bytes below and
# shipped verbatim into the bundle; write_text's newline translation would
# rewrite LF to the host's line separator (CRLF on Windows) on the way in.
local.write_bytes(design.additional_tests.encode())
additions[extra] = local
options.test_selectors.append(extra)
if "tests" not in options.test_paths:
Expand Down
4 changes: 3 additions & 1 deletion tests/test_repository_source_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ def source_tree(tmp_path, monkeypatch):
path = base / name
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(content)
monkeypatch.setattr("os.chown", lambda *args: None)
# raising=False: os.chown doesn't exist on Windows, unlike POSIX where
# this test's whole point is to neutralize it.
monkeypatch.setattr("os.chown", lambda *args: None, raising=False)
return base


Expand Down
3 changes: 2 additions & 1 deletion tests/test_tasksmith_added_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ def test_added_modules_are_absent_and_new_helpers_can_be_submitted(tmp_path, mon
assert Task(task).config.artifacts[0].source == "/workspace/lib"
contract = json.loads((task / "tests/contract.json").read_text())
workspace = task / "environment/source"
monkeypatch.setattr("os.chown", lambda *args: None)
# raising=False: os.chown doesn't exist on Windows.
monkeypatch.setattr("os.chown", lambda *args: None, raising=False)
# Missing new APIs are left to behavioral assertions, not a collection exception.
validate_submission(workspace, contract)
(workspace / "lib/helper.py").write_text("value = 1\n")
Expand Down
3 changes: 2 additions & 1 deletion tests/test_tasksmith_verifier_selection.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,8 @@ def test_image(image, options, output, **kwargs):
monkeypatch.setattr(worker, "reverse_source", lambda *args: (defective, []))
monkeypatch.setattr(worker, "test_image", test_image)
monkeypatch.setattr(worker, "test_excerpts", lambda *args, **kwargs: [])
monkeypatch.setattr("os.chown", lambda *args: None)
# raising=False: os.chown doesn't exist on Windows.
monkeypatch.setattr("os.chown", lambda *args: None, raising=False)
output = tmp_path / "construct"
output.mkdir()
result = worker.construct(
Expand Down
19 changes: 17 additions & 2 deletions tests/test_verifier_editor_backups.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import hashlib
import os
import shutil
import sys

import pytest

Expand All @@ -14,7 +15,8 @@ def submission(tmp_path, monkeypatch):
workspace = tmp_path / "private"
(workspace / "src").mkdir(parents=True)
(workspace / "src/model.py").write_text("value = 1\n")
monkeypatch.setattr("os.chown", lambda *args: None)
# raising=False: os.chown doesn't exist on Windows.
monkeypatch.setattr("os.chown", lambda *args: None, raising=False)
contract = {"submitted_files": ["src/model.py"], "submitted_roots": ["src"]}
return workspace, contract

Expand Down Expand Up @@ -48,7 +50,20 @@ def test_unknown_non_python_additions_still_reject_without_cleaning(submission,
assert backup.read_text() == "previous\n"


@pytest.mark.parametrize("kind", ["symlink", "dangling_symlink", "oversized", "fifo"])
@pytest.mark.parametrize(
"kind",
[
"symlink",
"dangling_symlink",
"oversized",
pytest.param(
"fifo",
marks=pytest.mark.skipif(
sys.platform == "win32", reason="os.mkfifo doesn't exist on Windows"
),
),
],
)
def test_backup_paths_keep_regular_file_and_symlink_guards(submission, tmp_path, kind):
workspace, contract = submission
backup = workspace / "src/model.py.bak"
Expand Down