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
19 changes: 10 additions & 9 deletions src/specify_cli/bundler/services/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,16 @@ def install_bundle(
if installer.is_installed(project_root, component):
installer.remove(project_root, component)
result.uninstalled.append(component)

record = InstalledBundleRecord.create(
bundle_id=plan.bundle_id,
version=plan.version,
components=contributed,
# Preserve the original install time across refresh/update so
# ``bundle list`` keeps reporting when the bundle was first installed.
installed_at=existing.installed_at if existing is not None else None,
)
save_records(project_root, upsert_record(records, record))
except BundlerError:
_rollback(project_root, installer, done)
raise
Expand All @@ -165,15 +175,6 @@ def install_bundle(
"No changes were recorded."
) from exc

record = InstalledBundleRecord.create(
bundle_id=plan.bundle_id,
version=plan.version,
components=contributed,
# Preserve the original install time across refresh/update so
# ``bundle list`` keeps reporting when the bundle was first installed.
installed_at=existing.installed_at if existing is not None else None,
)
save_records(project_root, upsert_record(records, record))
return result


Expand Down
19 changes: 19 additions & 0 deletions tests/integration/test_bundler_install_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,25 @@ def test_partial_failure_rolls_back_and_records_nothing(tmp_path: Path):
assert load_records(tmp_path) == []


def test_record_save_failure_rolls_back_new_components(tmp_path: Path, monkeypatch):
make_project(tmp_path)
manifest = BundleManifest.from_dict(valid_manifest_dict())
installer = FakeInstaller()

def fail_save(*_args, **_kwargs):
raise OSError("disk full")

monkeypatch.setattr(
"specify_cli.bundler.services.installer.save_records", fail_save
)

with pytest.raises(BundlerError, match="disk full"):
install_bundle(tmp_path, _plan(manifest), installer, manifest=manifest)

assert installer.installed == set()
assert load_records(tmp_path) == []


def test_remove_is_non_collateral(tmp_path: Path):
make_project(tmp_path)
installer = FakeInstaller()
Expand Down