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
17 changes: 9 additions & 8 deletions src/deploy_tools/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,14 +128,15 @@ def _get_release_changes(
old_release = old_releases[name][version]

if old_release.module != new_release.module:
if old_release.module.allow_updates:
release_changes.to_update.append(new_release)
continue

raise ValidationError(
f"Module {name}/{version} modified without updating version."
)

if not old_release.module.allow_updates:
raise ValidationError(
f"Module {name}/{version} modified without updating version."
)
release_changes.to_update.append(new_release)

# Note that a content update and a lifecycle transition can happen in the
# same sync; evaluate the transition independently of whether content
# has changed.
if not old_release.deprecated and new_release.deprecated:
release_changes.to_deprecate.append(new_release)
elif old_release.deprecated and not new_release.deprecated:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# yaml-language-server: $schema=/workspaces/deploy-tools/src/deploy_tools/models/schemas/release.json

module:
name: example-module-updatable
version: "1.0"
description: Updatable shell-only module with a single shell application
allow_updates: true

applications:
- app_type: shell

name: test-updatable-echo
# Differs from the original updatable-live (echo live) so that deploying this over
# it updates the content in place. Module should be deprecated in the same sync.
script:
- echo deprecated

deprecated: true
3 changes: 3 additions & 0 deletions tests/configs/valid/updatable-deprecated/settings.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# yaml-language-server: $schema=/workspaces/deploy-tools/src/deploy_tools/models/schemas/deployment-settings.json

default_versions: {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# yaml-language-server: $schema=/workspaces/deploy-tools/src/deploy_tools/models/schemas/release.json

module:
name: example-module-updatable
version: "1.0"
description: Updatable shell-only module with a single shell application
allow_updates: true

applications:
- app_type: shell

name: test-updatable-echo
script:
- echo live
3 changes: 3 additions & 0 deletions tests/configs/valid/updatable-live/settings.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# yaml-language-server: $schema=/workspaces/deploy-tools/src/deploy_tools/models/schemas/deployment-settings.json

default_versions: {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# yaml-language-server: $schema=/workspaces/deploy-tools/src/deploy_tools/models/schemas/release.json

module:
name: example-module-updatable
version: "1.0"
description: Updatable shell-only module with a single shell application
allow_updates: true

applications:
- app_type: shell

name: test-updatable-echo
# Differs from updatable-deprecated (echo deprecated) so that deploying this over it
# updates the content in place. Module should be restored in the same sync
script:
- echo restored
3 changes: 3 additions & 0 deletions tests/configs/valid/updatable-restored/settings.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# yaml-language-server: $schema=/workspaces/deploy-tools/src/deploy_tools/models/schemas/deployment-settings.json

default_versions: {}
4 changes: 2 additions & 2 deletions tests/test_compare.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def test_compare_accepts_deprecated_modules(tmp_path: Path, configs: Path) -> No
# Deploy a module then deprecate it, so the area holds deprecated modulefile links;
# compare must accept them.
run_cli(
"sync", "--from-scratch", tmp_path, configs / "valid" / "multi-version-active"
"sync", "--from-scratch", tmp_path, configs / "valid" / "multi-version-live"
)
run_cli("sync", tmp_path, configs / "valid" / "multi-version-deprecated")
assert run_cli("compare", tmp_path) == ""
Expand Down Expand Up @@ -137,7 +137,7 @@ def test_compare_use_ref_detects_drift(tmp_path: Path, configs: Path) -> None:
# two syncs the area matches its own (HEAD) snapshot. The previous commit's
# snapshot, taken before the second sync deprecated a module version, doesn't match.
run_cli(
"sync", "--from-scratch", tmp_path, configs / "valid" / "multi-version-active"
"sync", "--from-scratch", tmp_path, configs / "valid" / "multi-version-live"
)
run_cli("sync", tmp_path, configs / "valid" / "multi-version-deprecated")

Expand Down
2 changes: 1 addition & 1 deletion tests/test_default_versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def test_excluded_versions_are_not_auto_selected() -> None:

def test_deprecated_versions_are_not_auto_selected() -> None:
# Deprecated releases are excluded from the deployed set, so a higher deprecated
# version must not become the default over a lower active one.
# version must not become the default over a lower live one.
deployment = _deployment(
_release("mod", "1.0"),
_release("mod", "2.0", deprecated=True),
Expand Down
34 changes: 31 additions & 3 deletions tests/test_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def test_deprecate_then_remove_multiple_versions_of_same_module(
# Regression test for issues with removing name folders for the same module name
# twice.
run_cli(
"sync", "--from-scratch", tmp_path, configs / "valid" / "multi-version-active"
"sync", "--from-scratch", tmp_path, configs / "valid" / "multi-version-live"
)

layout = Layout(tmp_path)
Expand All @@ -34,6 +34,34 @@ def test_deprecate_then_remove_multiple_versions_of_same_module(
assert run_cli("compare", tmp_path) == ""


def test_sync_updates_and_transitions_deprecation_together(
tmp_path: Path, configs: Path
) -> None:
# A single sync can change a module's content and its deprecation status at once.
# Applying the content update must not stop the deprecate or restore step from
# running, which would leave the modulefile link in its old location.
name = "example-module-updatable"
layout = Layout(tmp_path)
live_link = layout.get_modulefile_link(name, "1.0")
deprecated_link = layout.get_modulefile_link(name, "1.0", from_deprecated=True)
entrypoint = layout.get_entrypoints_folder(name, "1.0") / "test-updatable-echo"

run_cli("sync", "--from-scratch", tmp_path, configs / "valid" / "updatable-live")
assert live_link.is_symlink()

# Update the content and deprecate together.
run_cli("sync", tmp_path, configs / "valid" / "updatable-deprecated")
assert not live_link.exists()
assert deprecated_link.is_symlink()
assert "echo deprecated" in entrypoint.read_text()

# Update the content and restore together.
run_cli("sync", tmp_path, configs / "valid" / "updatable-restored")
assert live_link.is_symlink()
assert not deprecated_link.exists()
assert "echo restored" in entrypoint.read_text()


def test_sync_applies_explicit_default_version_over_auto_selection(
tmp_path: Path, configs: Path
) -> None:
Expand All @@ -56,12 +84,12 @@ def test_sync_rejects_deployment_area_without_git_repo(
# An area whose .git has been lost still has a snapshot but no history to commit
# against: a corrupt state surfaced as a clean error, not a GitPython traceback.
run_cli(
"sync", "--from-scratch", tmp_path, configs / "valid" / "multi-version-active"
"sync", "--from-scratch", tmp_path, configs / "valid" / "multi-version-live"
)
shutil.rmtree(tmp_path / ".git")

with pytest.raises(SyncError, match="not a git repository"):
run_cli("sync", tmp_path, configs / "valid" / "multi-version-active")
run_cli("sync", tmp_path, configs / "valid" / "multi-version-live")


def test_sync_from_scratch_rejects_existing_snapshot(
Expand Down
14 changes: 14 additions & 0 deletions tests/test_validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,20 @@ def test_validate_test_build_catches_invalid_script(
run_cli("validate", "--test-build", "--from-scratch", tmp_path, config)


def test_validate_previews_simultaneous_update_and_deprecation(
tmp_path: Path, configs: Path
) -> None:
# A single sync that both updates a module's content and deprecates it must preview
# both actions in the printed changes summary: the release is listed under
# "updated" and under "deprecated", not just one of them.
run_cli("sync", "--from-scratch", tmp_path, configs / "valid" / "updatable-live")

output = run_cli("validate", tmp_path, configs / "valid" / "updatable-deprecated")

assert "Modules to be updated:\nexample-module-updatable/1.0" in output
assert "Modules to be deprecated:\nexample-module-updatable/1.0" in output


def test_validate_reports_no_actions_when_unchanged(
tmp_path: Path, configs: Path
) -> None:
Expand Down
Loading