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
10 changes: 9 additions & 1 deletion diffgraph/schema/diffgraph-v2.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -395,14 +395,22 @@
"UNKNOWN",
"not_a_git_repository",
"git_diff_failed",
"git_untracked_failed",
"invalid_base_ref",
"invalid_head_ref",
"merge_base_failed",
"malformed_merge_base",
"malformed_git_output",
"undecodable_path",
"missing_object_id",
"unsupported_worktree_entry",
"worktree_read_failed",
"hash_object_failed",
"malformed_hash_object_output",
"unmerged_index_entry"
"unmerged_index_entry",
"pathspec_outside_repository",
"gitlink_head_failed",
"malformed_gitlink_head"
],
"description": "Machine-readable warning code. Consumers can surface these to the user."
},
Expand Down
39 changes: 26 additions & 13 deletions diffgraph/structural.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,31 @@
QUERY_VERSION = "python-structure-v2"
_PARSER_STATE = threading.local()

# Resolver failures are part of the public artifact contract. Keep their
# machine-readable identity instead of collapsing an actionable Git failure
# into UNKNOWN, so clients can offer the right remediation without parsing
# the human-oriented detail string.
RESOLUTION_WARNING_CODES = frozenset({
"not_a_git_repository",
"git_diff_failed",
"git_untracked_failed",
"invalid_base_ref",
"invalid_head_ref",
"merge_base_failed",
"malformed_merge_base",
"malformed_git_output",
"undecodable_path",
"missing_object_id",
"unsupported_worktree_entry",
"worktree_read_failed",
"hash_object_failed",
"malformed_hash_object_output",
"unmerged_index_entry",
"pathspec_outside_repository",
"gitlink_head_failed",
"malformed_gitlink_head",
})


class StructuralDependencyError(ImportError):
"""A required local structural parser dependency is unavailable."""
Expand Down Expand Up @@ -370,19 +395,7 @@ def _warning(code: str, path: Optional[str], detail: str) -> Dict[str, str]:


def _resolution_warning(item: ResolutionWarning) -> Dict[str, str]:
known_codes = {
"not_a_git_repository",
"git_diff_failed",
"malformed_git_output",
"undecodable_path",
"missing_object_id",
"unsupported_worktree_entry",
"worktree_read_failed",
"hash_object_failed",
"malformed_hash_object_output",
"unmerged_index_entry",
}
code = item.code if item.code in known_codes else "UNKNOWN"
code = item.code if item.code in RESOLUTION_WARNING_CODES else "UNKNOWN"
return _warning(code, item.path, "{}: {}".format(item.code, item.message))


Expand Down
29 changes: 27 additions & 2 deletions tests/test_structural.py
Original file line number Diff line number Diff line change
Expand Up @@ -778,7 +778,7 @@ def test_cli_structural_json_rejects_four_dot_range(tmp_path, monkeypatch):
assert "explicit non-empty BASE..HEAD or BASE...HEAD refs" in result.output


def test_cli_structural_json_preserves_invalid_ref_warning(tmp_path, monkeypatch):
def test_cli_structural_json_preserves_invalid_ref_warning_code(tmp_path, monkeypatch):
from click.testing import CliRunner
from diffgraph.cli import main

Expand All @@ -795,10 +795,35 @@ def test_cli_structural_json_preserves_invalid_ref_warning(tmp_path, monkeypatch
artifact = json.loads(result.output)
assert artifact["files"] == []
warning = artifact["metadata"]["warnings"][0]
assert warning["code"] == "UNKNOWN"
assert warning["code"] == "invalid_base_ref"
assert warning["detail"].startswith("invalid_base_ref:")


def test_cli_structural_json_preserves_scoped_pathspec_warning_code(tmp_path, monkeypatch):
"""Pathspec failures remain actionable structured artifact warnings."""
from click.testing import CliRunner
from diffgraph.cli import main

root = repo(tmp_path)
write(root, "app.py", "def value():\n return 1\n")
commit(root)
monkeypatch.chdir(root)

outside = tmp_path / "outside.py"
result = CliRunner().invoke(
main, ["--structural-json", "-", "diff", "--", str(outside)]
)

assert result.exit_code == 0, result.output
artifact = json.loads(result.output)
assert artifact["files"] == []
assert artifact["metadata"]["warnings"] == [{
"code": "pathspec_outside_repository",
"file": str(outside),
"detail": "pathspec_outside_repository: Absolute pathspec is outside the repository and was not resolved",
}]


def test_cli_structural_json_requires_separator_before_pathspecs(tmp_path, monkeypatch):
from click.testing import CliRunner
from diffgraph.cli import main
Expand Down
Loading