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
13 changes: 12 additions & 1 deletion src/fips_agents_cli/commands/vendor.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,19 @@ def vendor(update: bool, tag: str | None):
if not is_already_vendored:
rewrite_pyproject_for_vendored(project_path)

# Read commit from VENDORED marker
vendored_commit = "unknown"
marker_path = project_path / "src" / "fipsagents" / "VENDORED"
if marker_path.exists():
for line in marker_path.read_text().splitlines():
if line.startswith("commit_short:"):
vendored_commit = line.split(":", 1)[1].strip()
break

# Success
console.print("\n[bold green]Vendoring complete![/bold green]\n")
console.print(
f"\n[bold green]Vendoring complete![/bold green] (commit {vendored_commit})\n"
)
console.print("Next steps:")
console.print(" 1. pip install -e . # Reinstall with vendored source")
console.print(" 2. make test # Verify everything works")
Expand Down
18 changes: 17 additions & 1 deletion src/fips_agents_cli/tools/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -782,17 +782,33 @@ def vendor_fipsagents_from_clone(
version = line.split("=")[1].strip().strip('"').strip("'")
break

# Read HEAD commit from clone
import git as gitmodule

commit_full = "unknown"
commit_short = "unknown"
try:
repo = gitmodule.Repo(str(clone_path))
commit_full = repo.head.commit.hexsha
commit_short = commit_full[:7]
except Exception:
pass

# Write VENDORED marker
marker = dest / "VENDORED"
marker.write_text(
f"version: {version}\n"
f"vendored: {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M UTC')}\n"
f"source: https://github.com/fips-agents/agent-template\n"
f"commit: {commit_full}\n"
f"commit_short: {commit_short}\n"
f"\n"
f"This fipsagents source is vendored into the project.\n"
f"To update: fips-agents vendor --update\n"
)
console.print(f"[green]✓[/green] Wrote VENDORED marker (version {version})")
console.print(
f"[green]✓[/green] Wrote VENDORED marker (version {version}, commit {commit_short})"
)


def rewrite_pyproject_for_vendored(project_path: Path) -> None:
Expand Down
296 changes: 296 additions & 0 deletions tests/test_vendor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
"""Tests for vendor commit tracking functionality."""

import git
import pytest

from fips_agents_cli.tools.project import vendor_fipsagents_from_clone


class TestVendorCommitTracking:
"""Tests for vendor commit tracking in VENDORED marker."""

def _create_monorepo_fixture(self, path):
"""
Create a real git repository with monorepo structure for testing.

This mimics the fips-agents/agent-template repository structure:
packages/fipsagents/src/fipsagents with a real git history.
"""
path.mkdir(parents=True, exist_ok=True)

# Create directory structure
pkg_root = path / "packages" / "fipsagents"
pkg_src = pkg_root / "src" / "fipsagents"
pkg_src.mkdir(parents=True)

baseagent = pkg_src / "baseagent"
baseagent.mkdir()

# Create pyproject.toml
(pkg_root / "pyproject.toml").write_text(
"[project]\n"
'name = "fipsagents"\n'
'version = "0.1.0"\n'
'dependencies = ["litellm>=1.83.0"]\n'
)

# Create __init__.py files
(pkg_src / "__init__.py").write_text('"""fipsagents package."""\n')
(baseagent / "__init__.py").write_text('"""BaseAgent module."""\n\n__version__ = "1.2.3"\n')

# Initialize git repository
repo = git.Repo.init(str(path))

# Configure git user (required for commits)
with repo.config_writer() as config:
config.set_value("user", "name", "Test User")
config.set_value("user", "email", "test@example.com")

# Add all files and create initial commit
repo.index.add(
[
"packages/fipsagents/pyproject.toml",
"packages/fipsagents/src/fipsagents/__init__.py",
"packages/fipsagents/src/fipsagents/baseagent/__init__.py",
]
)
repo.index.commit("Initial commit: Add fipsagents package")

return repo

def test_vendored_marker_contains_commit(self, temp_dir):
"""Test that VENDORED marker contains commit and commit_short fields with valid hex values."""
# Create monorepo fixture
clone_path = temp_dir / "agent-template"
_repo = self._create_monorepo_fixture(clone_path)

# Create project directory
project_path = temp_dir / "my-agent"
project_path.mkdir()
(project_path / "src").mkdir()

# Vendor the package
vendor_fipsagents_from_clone(clone_path, project_path)

# Read VENDORED marker
marker_path = project_path / "src" / "fipsagents" / "VENDORED"
assert marker_path.exists()
marker_content = marker_path.read_text()

# Verify commit fields are present
assert "commit:" in marker_content
assert "commit_short:" in marker_content

# Verify they're not "unknown"
assert "commit: unknown" not in marker_content
assert "commit_short: unknown" not in marker_content

# Extract commit hash and verify it's valid hex
for line in marker_content.splitlines():
if line.startswith("commit:"):
commit_hash = line.split(":", 1)[1].strip()
# Should be 40 character hex string
assert len(commit_hash) == 40
assert all(c in "0123456789abcdef" for c in commit_hash.lower())
if line.startswith("commit_short:"):
commit_short = line.split(":", 1)[1].strip()
# Should be 7 character hex string
assert len(commit_short) == 7
assert all(c in "0123456789abcdef" for c in commit_short.lower())

def test_vendored_marker_commit_matches_repo(self, temp_dir):
"""Test that the commit hash in VENDORED matches the actual HEAD commit of the source repo."""
# Create monorepo fixture
clone_path = temp_dir / "agent-template"
repo = self._create_monorepo_fixture(clone_path)

# Get actual HEAD commit
expected_commit = repo.head.commit.hexsha
expected_short = expected_commit[:7]

# Create project directory
project_path = temp_dir / "my-agent"
project_path.mkdir()
(project_path / "src").mkdir()

# Vendor the package
vendor_fipsagents_from_clone(clone_path, project_path)

# Read VENDORED marker
marker_path = project_path / "src" / "fipsagents" / "VENDORED"
marker_content = marker_path.read_text()

# Verify commit matches
assert f"commit: {expected_commit}" in marker_content
assert f"commit_short: {expected_short}" in marker_content

def test_vendored_marker_contains_version(self, temp_dir):
"""Test that VENDORED marker contains the correct version from __init__.py."""
# Create monorepo fixture
clone_path = temp_dir / "agent-template"
_repo = self._create_monorepo_fixture(clone_path)

# Create project directory
project_path = temp_dir / "my-agent"
project_path.mkdir()
(project_path / "src").mkdir()

# Vendor the package
vendor_fipsagents_from_clone(clone_path, project_path)

# Read VENDORED marker
marker_path = project_path / "src" / "fipsagents" / "VENDORED"
marker_content = marker_path.read_text()

# Verify version is present
assert "version: 1.2.3" in marker_content

def test_vendored_copies_source(self, temp_dir):
"""Test that vendoring actually copies the source files to the project."""
# Create monorepo fixture
clone_path = temp_dir / "agent-template"
_repo = self._create_monorepo_fixture(clone_path)

# Create project directory
project_path = temp_dir / "my-agent"
project_path.mkdir()
(project_path / "src").mkdir()

# Vendor the package
vendor_fipsagents_from_clone(clone_path, project_path)

# Verify source files were copied
dest = project_path / "src" / "fipsagents"
assert dest.exists()
assert dest.is_dir()
assert (dest / "__init__.py").exists()
assert (dest / "baseagent" / "__init__.py").exists()

# Verify content matches
baseagent_init = (dest / "baseagent" / "__init__.py").read_text()
assert '__version__ = "1.2.3"' in baseagent_init

def test_vendored_copies_upstream_toml(self, temp_dir):
"""Test that pyproject.toml is copied as UPSTREAM.toml for provenance."""
# Create monorepo fixture
clone_path = temp_dir / "agent-template"
_repo = self._create_monorepo_fixture(clone_path)

# Create project directory
project_path = temp_dir / "my-agent"
project_path.mkdir()
(project_path / "src").mkdir()

# Vendor the package
vendor_fipsagents_from_clone(clone_path, project_path)

# Verify UPSTREAM.toml was copied
upstream_toml = project_path / "src" / "fipsagents" / "UPSTREAM.toml"
assert upstream_toml.exists()

# Verify content matches original pyproject.toml
content = upstream_toml.read_text()
assert 'name = "fipsagents"' in content
assert 'version = "0.1.0"' in content

def test_vendored_replaces_existing_source(self, temp_dir):
"""Test that vendoring removes existing vendored source before copying."""
# Create monorepo fixture
clone_path = temp_dir / "agent-template"
_repo = self._create_monorepo_fixture(clone_path)

# Create project directory with existing vendored source
project_path = temp_dir / "my-agent"
project_path.mkdir()
(project_path / "src").mkdir()

existing_dest = project_path / "src" / "fipsagents"
existing_dest.mkdir()
(existing_dest / "old_file.py").write_text("# This should be removed\n")

# Vendor the package
vendor_fipsagents_from_clone(clone_path, project_path)

# Verify old file is gone and new files exist
assert not (existing_dest / "old_file.py").exists()
assert (existing_dest / "__init__.py").exists()
assert (existing_dest / "baseagent" / "__init__.py").exists()

def test_vendored_marker_contains_all_required_fields(self, temp_dir):
"""Test that VENDORED marker contains all required fields."""
# Create monorepo fixture
clone_path = temp_dir / "agent-template"
_repo = self._create_monorepo_fixture(clone_path)

# Create project directory
project_path = temp_dir / "my-agent"
project_path.mkdir()
(project_path / "src").mkdir()

# Vendor the package
vendor_fipsagents_from_clone(clone_path, project_path)

# Read VENDORED marker
marker_path = project_path / "src" / "fipsagents" / "VENDORED"
marker_content = marker_path.read_text()

# Verify all required fields are present
assert "version:" in marker_content
assert "vendored:" in marker_content
assert "source:" in marker_content
assert "commit:" in marker_content
assert "commit_short:" in marker_content
assert "https://github.com/fips-agents/agent-template" in marker_content

def test_multiple_commits_in_history(self, temp_dir):
"""Test that vendoring tracks the latest commit when repo has multiple commits."""
# Create monorepo fixture
clone_path = temp_dir / "agent-template"
repo = self._create_monorepo_fixture(clone_path)

# Add a second commit
readme_path = clone_path / "README.md"
readme_path.write_text("# Agent Template\n")
repo.index.add(["README.md"])
repo.index.commit("docs: Add README")

# Get the latest commit
expected_commit = repo.head.commit.hexsha
expected_short = expected_commit[:7]

# Create project directory
project_path = temp_dir / "my-agent"
project_path.mkdir()
(project_path / "src").mkdir()

# Vendor the package
vendor_fipsagents_from_clone(clone_path, project_path)

# Read VENDORED marker
marker_path = project_path / "src" / "fipsagents" / "VENDORED"
marker_content = marker_path.read_text()

# Verify it tracks the second (latest) commit, not the first
assert f"commit: {expected_commit}" in marker_content
assert f"commit_short: {expected_short}" in marker_content

def test_missing_package_source_raises_error(self, temp_dir):
"""Test that vendoring fails gracefully when package source is missing."""
# Create incomplete monorepo (missing fipsagents package)
clone_path = temp_dir / "agent-template"
clone_path.mkdir()

# Initialize git but don't create the expected package structure
repo = git.Repo.init(str(clone_path))
with repo.config_writer() as config:
config.set_value("user", "name", "Test User")
config.set_value("user", "email", "test@example.com")

# Create project directory
project_path = temp_dir / "my-agent"
project_path.mkdir()
(project_path / "src").mkdir()

# Vendoring should raise FileNotFoundError
with pytest.raises(FileNotFoundError, match="Expected fipsagents source at"):
vendor_fipsagents_from_clone(clone_path, project_path)
Loading