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
192 changes: 152 additions & 40 deletions adopt-project.py

Large diffs are not rendered by default.

21 changes: 12 additions & 9 deletions scripts/shape_materialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -867,10 +867,11 @@ def _append_eol_block(target: Path, result: "Materialized",
A SYMLINK IS NOT WRITTEN THROUGH: what it leads to may be another file of
the source's, or nothing in this repository, and git follows no
`.gitattributes` that is a link anyway: a checkout reads only the link's
own text, the path it leads to, which names no attribute (#654). It is
left as it is, and the follow-up says so and lists the lines a regular
file would need. A link is one on the disk OR in git's index
(`_tracked_as_link`)."""
own text, the path it leads to, as one line of rules, which names an
attribute only where that text holds a space, a tab or a carriage
return (#654, #906). It is left as it is, and the follow-up says so and
lists the lines a regular file would need. A link is one on the disk OR
in git's index (`_tracked_as_link`)."""
if (ATTRIBUTES, f"{collision_dir}/{ATTRIBUTES}") not in result.collisions:
return
path = target / ATTRIBUTES
Expand Down Expand Up @@ -987,8 +988,9 @@ def collision_follow_up(intended: str, actual: str,
def _attributes_link_follow_up(intended: str, actual: str,
beside: dict | None) -> str:
"""The follow-up for a source `.gitattributes` that is a symlink: no
block was appended, nothing git reads gives the root's pinned copies a
line ending, and these are the lines a regular file needs.
block was appended, for a link to a path nothing git reads gives the
root's pinned copies a line ending, and these are the lines a regular
file needs.

The copies are the pinned ones that kept their own names, in the pin's
order: every one `beside` does not send under the collision directory,
Expand All @@ -1001,9 +1003,10 @@ def _attributes_link_follow_up(intended: str, actual: str,
f"never writes through a link) and wrote the shape's copy beside it "
f"as {actual}, which governs only its own directory. Git follows no "
f"{intended} that is a symlink: a checkout reads only the link's own "
"text, the path it leads to, and a path names no attribute, so no "
"line-ending rule reaches the pinned copies outside it: a Windows "
"clone writes them with CRLF and "
"text, the path it leads to, as one line of rules, which names an "
"attribute only where that text holds a space, a tab or a carriage "
"return, so, for a link to a path, no line-ending rule reaches the "
"pinned copies outside it: a Windows clone writes them with CRLF and "
"reports shape-copy-drift on each one. To give every clone one, "
f"replace the link with a regular {intended} holding the lines of "
"the file it leads to that should still apply, then these, one per "
Expand Down
8 changes: 5 additions & 3 deletions tests/test_adopt_attributes_eol_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,9 +395,11 @@ def test_the_follow_up_for_a_linked_attributes_file_lists_the_lines_it_needs(
assert said.startswith(f"{ATTRIBUTES} is a symlink, so the split "
"appended nothing to it"), said
assert (f"Git follows no {ATTRIBUTES} that is a symlink: a checkout "
"reads only the link's own text, the path it leads to, and a "
"path names no attribute, so no line-ending rule reaches the "
"pinned copies outside it") in said
"reads only the link's own text, the path it leads to, as one "
"line of rules, which names an attribute only where that text "
"holds a space, a tab or a carriage return, so, for a link to a "
"path, no line-ending rule reaches the pinned copies outside "
"it") in said
assert f"replace the link with a regular {ATTRIBUTES}" in said
assert ", ".join(f"`{line}`" for line in needed) in said
assert needed[0] == EOL_BLOCK_HEAD
Expand Down
18 changes: 17 additions & 1 deletion tests/test_adopt_contracts_dir.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,19 @@
"code leg's tooling takes the path from the environment instead of "
"assuming it sits beside it")

#: The same line for a source that keeps a root `Makefile` of its own (#906):
#: the shape's, with the block, is written beside it as `shape/Makefile`, so
#: the block waits there until the two are merged, as #634 words it for the
#: pinned `AGENTS-shape.md`, and the root's Makefile is the source's, which
#: the run adds nothing to.
OWN_MAKEFILE_FOLLOW_UP = (
"the shape's Makefile wires `CONTRACTS_DIR` to the `contracts/` of the "
"spec leg, where the plan's `legs.spec_path` mounts it, and exports it, "
"so the code leg's tooling takes the path from the environment instead "
"of assuming it sits beside it; but the source keeps a Makefile of its "
"own at the root, so that block waits in `shape/Makefile` until the two "
"are merged, and the run adds none of it to the root's Makefile")

#: A source with nothing for the plan to ask about, no Makefile of its own
#: (so the shape's, with the block, is the root's), and one code file that
#: names `contracts/`, which the rules send to the spec leg.
Expand Down Expand Up @@ -436,8 +449,11 @@ def test_the_path_the_split_message_prints_leads_to_the_contracts(
marker = "the spec leg's `contracts/` is `"
printed = message.split(marker, 1)[1].split("`", 1)[0]
assert printed == "../../legs/spec/contracts/"
# The plan holds the read-across line, so the spec leg's own path comes
# first (#906), and then the path to its `contracts/`.
assert (f"mounted back here at {SPEC_PATH}/ and {CODE_PATH}/, so from "
f"the code leg {marker}{printed}`.") in message
f"the code leg the spec leg is `../../legs/spec/` and "
f"{marker}{printed}`.") in message
code_leg = adopted_by_hand["clone"].joinpath(*CODE_PATH.split("/"))
target = code_leg.joinpath(*printed.rstrip("/").split("/"), POLICY)
assert target.resolve() == adopted_by_hand["clone"].joinpath(
Expand Down
19 changes: 14 additions & 5 deletions tests/test_adopt_leg_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,18 @@ def test_a_hand_edited_mount_leaves_no_plan_time_path_in_a_follow_up(
EDITED_ROWS = [pytest.param(*row.values[1:], id=row.id) for row in ACROSS_ROWS]


def clause(across: str) -> str:
"""What the two sentences that print the mounts end with."""
return (", so from the code leg the spec leg's `contracts/` is "
f"`{across}`")
def clause(across: str, reads_across: bool = True) -> str:
"""What the two sentences that print the mounts end with. Where the plan
holds the read-across follow-up, as every plan here does, it is the spec
leg's own path first and then the path to its `contracts/` (#906); a
split message rendered with no follow-ups has the `contracts/` path
alone."""
contracts = f"the spec leg's `contracts/` is `{across}`"
if not reads_across:
return f", so from the code leg {contracts}"
spec_leg = across.removesuffix("contracts/")
return (f", so from the code leg the spec leg is `{spec_leg}` and "
f"{contracts}")


def split_line(output: str) -> str:
Expand Down Expand Up @@ -359,7 +367,8 @@ def test_the_split_message_prints_the_path_to_the_contracts_beside_the_mounts(
which are the ones `execute` made (the pull request's body is this text)."""
message = split_message_at(adopter, *edited)
assert (f"mounted back here at {edited[0]}/ and {edited[1]}/"
f"{clause(across)}.") in message.splitlines()[2]
f"{clause(across, reads_across=False)}.") \
in message.splitlines()[2]
assert posixpath.normpath(posixpath.join(edited[1], across)) == \
f"{edited[0]}/contracts"

Expand Down
Loading
Loading