Skip to content
Open
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
12 changes: 11 additions & 1 deletion src/specify_cli/workflows/overlays/_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,17 @@ def workflow_resolve(project_root: Path, workflow_id: str) -> dict[str, Any] | N
return None

console.print(f"Resolved workflow '{workflow_id}':")
console.print("Layers (highest precedence first):")
# The list is sorted by (priority, source) ascending, which puts the
# winning layer first only while priorities DIFFER. On a tie the sort is
# alphabetical by source while the merge gives the conflict to the LAST id
# (docs/reference/workflows.md:126), so the plain "highest precedence
# first" label stated the opposite of the outcome printed directly beneath
# it in the same output -- in the one command whose job is explaining which
# overlay won. Spell the tie-break out rather than reorder the list, which
# `test_workflow_resolve_equal_priority_layers_sort_by_source` pins.
console.print(
"Layers (highest precedence first; on equal priority the last ID wins):"
)
for layer in layers:
priority = (
"n/a" if layer.tier == "base" else str(normalize_priority(layer.priority))
Expand Down
53 changes: 53 additions & 0 deletions tests/workflows/test_overlay_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,59 @@ def test_workflow_resolve_escapes_rich_markup_in_step_id(
assert result.exit_code == 0, result.output
assert step_id in result.output

def test_workflow_resolve_header_states_the_equal_priority_tiebreak(
self, project_dir, monkeypatch
):
"""The layer header must not contradict the attribution beneath it.

The list is sorted by (priority, source) ascending, which puts the
winning layer first only while priorities DIFFER. On a tie the sort is
alphabetical while the merge gives the conflict to the LAST id, so a
bare "highest precedence first" label stated the opposite of the
outcome printed directly below it — in the one command whose job is
explaining which overlay won.
"""
monkeypatch.setattr("specify_cli._require_specify_project", lambda: project_dir)
_write_workflow(
project_dir,
"wf",
{
"schema_version": "1.0",
"workflow": {"id": "wf", "name": "WF", "version": "1.0.0"},
"steps": [{"id": "build", "type": "command", "command": "echo BASE"}],
},
)
for overlay_id in ("alpha", "beta"):
_write_overlay(
project_dir,
"wf",
overlay_id,
{
"id": overlay_id,
"extends": "wf",
"priority": 10,
"edits": [
{
"operation": "replace",
"anchor": "build",
"step": {
"id": "build",
"type": "command",
"command": f"echo FROM-{overlay_id.upper()}",
},
}
],
},
)

result = runner.invoke(app, ["workflow", "resolve", "wf"])

assert result.exit_code == 0, result.output
output = " ".join(result.output.split())
# alpha is listed first, but beta is the layer that actually wins.
assert "build: project:beta" in output, output
assert "on equal priority the last ID wins" in output, output

def test_workflow_resolve_equal_priority_layers_sort_by_source(self, project_dir, monkeypatch):
"""Equal-priority overlays are listed alphabetically by source."""
monkeypatch.setattr("specify_cli._require_specify_project", lambda: project_dir)
Expand Down