dploydb 0.1.0__tar.gz → 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. {dploydb-0.1.0 → dploydb-0.1.1}/CHANGELOG.md +12 -1
  2. {dploydb-0.1.0 → dploydb-0.1.1}/PKG-INFO +4 -4
  3. {dploydb-0.1.0 → dploydb-0.1.1}/README.md +3 -3
  4. {dploydb-0.1.0 → dploydb-0.1.1}/RELEASING.md +8 -0
  5. {dploydb-0.1.0 → dploydb-0.1.1}/docs/first-run.md +1 -1
  6. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/redaction.py +4 -1
  7. {dploydb-0.1.0 → dploydb-0.1.1}/pyproject.toml +1 -1
  8. {dploydb-0.1.0 → dploydb-0.1.1}/scripts/verify_clean_linux.py +7 -0
  9. {dploydb-0.1.0 → dploydb-0.1.1}/tests/integration/test_deploy_end_to_end.py +10 -2
  10. {dploydb-0.1.0 → dploydb-0.1.1}/tests/test_redaction.py +7 -1
  11. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_deploy.py +31 -0
  12. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_milestone8_docs.py +1 -1
  13. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_release_readiness.py +19 -4
  14. {dploydb-0.1.0 → dploydb-0.1.1}/uv.lock +1 -1
  15. {dploydb-0.1.0 → dploydb-0.1.1}/.gitignore +0 -0
  16. {dploydb-0.1.0 → dploydb-0.1.1}/CODE_OF_CONDUCT.md +0 -0
  17. {dploydb-0.1.0 → dploydb-0.1.1}/CONTRIBUTING.md +0 -0
  18. {dploydb-0.1.0 → dploydb-0.1.1}/LICENSE +0 -0
  19. {dploydb-0.1.0 → dploydb-0.1.1}/NOTICE +0 -0
  20. {dploydb-0.1.0 → dploydb-0.1.1}/SECURITY.md +0 -0
  21. {dploydb-0.1.0 → dploydb-0.1.1}/demo/.dockerignore +0 -0
  22. {dploydb-0.1.0 → dploydb-0.1.1}/demo/Dockerfile +0 -0
  23. {dploydb-0.1.0 → dploydb-0.1.1}/demo/__init__.py +0 -0
  24. {dploydb-0.1.0 → dploydb-0.1.1}/demo/compose.yaml +0 -0
  25. {dploydb-0.1.0 → dploydb-0.1.1}/demo/controller.py +0 -0
  26. {dploydb-0.1.0 → dploydb-0.1.1}/demo/dploydb.yaml +0 -0
  27. {dploydb-0.1.0 → dploydb-0.1.1}/demo/prepare.py +0 -0
  28. {dploydb-0.1.0 → dploydb-0.1.1}/demo/releases/broken-health/migration.sql +0 -0
  29. {dploydb-0.1.0 → dploydb-0.1.1}/demo/releases/broken-health/release.json +0 -0
  30. {dploydb-0.1.0 → dploydb-0.1.1}/demo/releases/broken-migration/migration.sql +0 -0
  31. {dploydb-0.1.0 → dploydb-0.1.1}/demo/releases/broken-migration/release.json +0 -0
  32. {dploydb-0.1.0 → dploydb-0.1.1}/demo/releases/final-health-failure/migration.sql +0 -0
  33. {dploydb-0.1.0 → dploydb-0.1.1}/demo/releases/final-health-failure/release.json +0 -0
  34. {dploydb-0.1.0 → dploydb-0.1.1}/demo/releases/v1/migration.sql +0 -0
  35. {dploydb-0.1.0 → dploydb-0.1.1}/demo/releases/v1/release.json +0 -0
  36. {dploydb-0.1.0 → dploydb-0.1.1}/demo/releases/v2/migration.sql +0 -0
  37. {dploydb-0.1.0 → dploydb-0.1.1}/demo/releases/v2/release.json +0 -0
  38. {dploydb-0.1.0 → dploydb-0.1.1}/demo/runtime/__init__.py +0 -0
  39. {dploydb-0.1.0 → dploydb-0.1.1}/demo/runtime/app.py +0 -0
  40. {dploydb-0.1.0 → dploydb-0.1.1}/demo/runtime/migration.py +0 -0
  41. {dploydb-0.1.0 → dploydb-0.1.1}/demo/runtime/production_migration_failure.py +0 -0
  42. {dploydb-0.1.0 → dploydb-0.1.1}/demo/runtime/release.py +0 -0
  43. {dploydb-0.1.0 → dploydb-0.1.1}/demo/runtime/traffic_hook.py +0 -0
  44. {dploydb-0.1.0 → dploydb-0.1.1}/docs/limitations.md +0 -0
  45. {dploydb-0.1.0 → dploydb-0.1.1}/docs/security.md +0 -0
  46. {dploydb-0.1.0 → dploydb-0.1.1}/docs/uninstall.md +0 -0
  47. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/__init__.py +0 -0
  48. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/__main__.py +0 -0
  49. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/backup.py +0 -0
  50. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/candidate.py +0 -0
  51. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/cli.py +0 -0
  52. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/config.py +0 -0
  53. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/cutover.py +0 -0
  54. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/deploy.py +0 -0
  55. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/deployment_dependencies.py +0 -0
  56. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/deployment_evidence.py +0 -0
  57. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/diagnostics.py +0 -0
  58. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/errors.py +0 -0
  59. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/health.py +0 -0
  60. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/locking.py +0 -0
  61. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/manual_restore.py +0 -0
  62. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/migration.py +0 -0
  63. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/models.py +0 -0
  64. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/recovery.py +0 -0
  65. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/releases.py +0 -0
  66. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/restore.py +0 -0
  67. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/retention.py +0 -0
  68. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/runners/__init__.py +0 -0
  69. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/runners/base.py +0 -0
  70. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/runners/docker_compose.py +0 -0
  71. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/runners/docker_compose_production.py +0 -0
  72. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/sqlite_checks.py +0 -0
  73. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/state.py +0 -0
  74. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/storage/__init__.py +0 -0
  75. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/storage/base.py +0 -0
  76. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/storage/local.py +0 -0
  77. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/storage/s3.py +0 -0
  78. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/subprocesses.py +0 -0
  79. {dploydb-0.1.0 → dploydb-0.1.1}/dploydb/traffic.py +0 -0
  80. {dploydb-0.1.0 → dploydb-0.1.1}/examples/nginx/README.md +0 -0
  81. {dploydb-0.1.0 → dploydb-0.1.1}/examples/nginx/dploydb-hook.py +0 -0
  82. {dploydb-0.1.0 → dploydb-0.1.1}/examples/nginx/dploydb.yaml +0 -0
  83. {dploydb-0.1.0 → dploydb-0.1.1}/examples/nginx/site.conf +0 -0
  84. {dploydb-0.1.0 → dploydb-0.1.1}/scripts/release_notes.py +0 -0
  85. {dploydb-0.1.0 → dploydb-0.1.1}/scripts/verify_distribution.py +0 -0
  86. {dploydb-0.1.0 → dploydb-0.1.1}/scripts/verify_pipx_install.py +0 -0
  87. {dploydb-0.1.0 → dploydb-0.1.1}/scripts/verify_registry.py +0 -0
  88. {dploydb-0.1.0 → dploydb-0.1.1}/scripts/verify_release_tag.py +0 -0
  89. {dploydb-0.1.0 → dploydb-0.1.1}/scripts/verify_s3_compatibility.py +0 -0
  90. {dploydb-0.1.0 → dploydb-0.1.1}/tests/fault_injection/test_lock_interruption.py +0 -0
  91. {dploydb-0.1.0 → dploydb-0.1.1}/tests/fault_injection/test_release_state_interruption.py +0 -0
  92. {dploydb-0.1.0 → dploydb-0.1.1}/tests/fault_injection/test_state_interruption.py +0 -0
  93. {dploydb-0.1.0 → dploydb-0.1.1}/tests/fault_injection/test_subprocess_tree_cleanup.py +0 -0
  94. {dploydb-0.1.0 → dploydb-0.1.1}/tests/integration/test_backup_cli.py +0 -0
  95. {dploydb-0.1.0 → dploydb-0.1.1}/tests/integration/test_backup_engine.py +0 -0
  96. {dploydb-0.1.0 → dploydb-0.1.1}/tests/integration/test_candidate_runner.py +0 -0
  97. {dploydb-0.1.0 → dploydb-0.1.1}/tests/integration/test_candidate_validation.py +0 -0
  98. {dploydb-0.1.0 → dploydb-0.1.1}/tests/integration/test_demo_docker.py +0 -0
  99. {dploydb-0.1.0 → dploydb-0.1.1}/tests/integration/test_demo_prepare.py +0 -0
  100. {dploydb-0.1.0 → dploydb-0.1.1}/tests/integration/test_demo_runtime.py +0 -0
  101. {dploydb-0.1.0 → dploydb-0.1.1}/tests/integration/test_migration_rehearsal.py +0 -0
  102. {dploydb-0.1.0 → dploydb-0.1.1}/tests/integration/test_milestone1_gate.py +0 -0
  103. {dploydb-0.1.0 → dploydb-0.1.1}/tests/integration/test_nginx_hook_example.py +0 -0
  104. {dploydb-0.1.0 → dploydb-0.1.1}/tests/integration/test_production_runner.py +0 -0
  105. {dploydb-0.1.0 → dploydb-0.1.1}/tests/test_cli.py +0 -0
  106. {dploydb-0.1.0 → dploydb-0.1.1}/tests/test_config.py +0 -0
  107. {dploydb-0.1.0 → dploydb-0.1.1}/tests/test_errors.py +0 -0
  108. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_backup.py +0 -0
  109. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_candidate.py +0 -0
  110. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_cutover.py +0 -0
  111. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_deploy_cli.py +0 -0
  112. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_diagnostics.py +0 -0
  113. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_docker_compose_production_runner.py +0 -0
  114. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_docker_compose_runner.py +0 -0
  115. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_health.py +0 -0
  116. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_locking.py +0 -0
  117. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_manual_restore.py +0 -0
  118. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_migration.py +0 -0
  119. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_milestone8_cli.py +0 -0
  120. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_recover_cli.py +0 -0
  121. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_recovery.py +0 -0
  122. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_release_cli.py +0 -0
  123. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_releases.py +0 -0
  124. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_restore.py +0 -0
  125. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_restore_cli.py +0 -0
  126. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_retention.py +0 -0
  127. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_s3_storage.py +0 -0
  128. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_sqlite_checks.py +0 -0
  129. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_state.py +0 -0
  130. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_subprocesses.py +0 -0
  131. {dploydb-0.1.0 → dploydb-0.1.1}/tests/unit/test_traffic.py +0 -0
@@ -6,6 +6,16 @@ All notable changes to DployDB are documented here. The project follows
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.1.1] - 2026-07-19
10
+
11
+ ### Fixed
12
+
13
+ - Treat the standard uppercase Linux `PWD` environment variable as a working
14
+ directory rather than a password field, preventing exact-value redaction from
15
+ invalidating absolute release-manifest paths during deployment.
16
+ - Exercise the installed clean-Linux deployment with `PWD` equal to the project
17
+ root and require the active release to retain an absolute operation-log path.
18
+
9
19
  ## [0.1.0] - 2026-07-19
10
20
 
11
21
  ### Added
@@ -32,5 +42,6 @@ All notable changes to DployDB are documented here. The project follows
32
42
  - During `0.x`, breaking public-contract changes may occur only in minor releases
33
43
  and will include explicit migration guidance.
34
44
 
35
- [Unreleased]: https://github.com/recursiveway/dployDB/compare/v0.1.0...HEAD
45
+ [Unreleased]: https://github.com/recursiveway/dployDB/compare/v0.1.1...HEAD
46
+ [0.1.1]: https://github.com/recursiveway/dployDB/releases/tag/v0.1.1
36
47
  [0.1.0]: https://github.com/recursiveway/dployDB/releases/tag/v0.1.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dploydb
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: Deployment safety for SQLite applications
5
5
  Project-URL: Homepage, https://github.com/recursiveway/dployDB
6
6
  Project-URL: Repository, https://github.com/recursiveway/dployDB
@@ -38,7 +38,7 @@ Description-Content-Type: text/markdown
38
38
  DployDB is being built as a deployment-safety tool for applications that use one SQLite database on one Linux server.
39
39
 
40
40
  > [!WARNING]
41
- > DployDB 0.1.0 is Alpha software. Its safety gates are real and extensively
41
+ > DployDB 0.1.1 is Alpha software. Its safety gates are real and extensively
42
42
  > tested, but public interfaces may still change in documented `0.x` minor
43
43
  > releases. Read the [supported limits](docs/limitations.md), keep independent
44
44
  > backups, and prove the complete failure/restore flow on a non-production host
@@ -59,7 +59,7 @@ docker compose version
59
59
 
60
60
  git clone https://github.com/recursiveway/dployDB.git
61
61
  cd dployDB
62
- pipx install dploydb==0.1.0
62
+ pipx install dploydb==0.1.1
63
63
  dploydb --no-color version
64
64
  ```
65
65
 
@@ -187,7 +187,7 @@ off-server backup, and protected retention are implemented.
187
187
  Install the published Alpha CLI:
188
188
 
189
189
  ```bash
190
- pipx install dploydb==0.1.0
190
+ pipx install dploydb==0.1.1
191
191
  dploydb version
192
192
  ```
193
193
 
@@ -8,7 +8,7 @@
8
8
  DployDB is being built as a deployment-safety tool for applications that use one SQLite database on one Linux server.
9
9
 
10
10
  > [!WARNING]
11
- > DployDB 0.1.0 is Alpha software. Its safety gates are real and extensively
11
+ > DployDB 0.1.1 is Alpha software. Its safety gates are real and extensively
12
12
  > tested, but public interfaces may still change in documented `0.x` minor
13
13
  > releases. Read the [supported limits](docs/limitations.md), keep independent
14
14
  > backups, and prove the complete failure/restore flow on a non-production host
@@ -29,7 +29,7 @@ docker compose version
29
29
 
30
30
  git clone https://github.com/recursiveway/dployDB.git
31
31
  cd dployDB
32
- pipx install dploydb==0.1.0
32
+ pipx install dploydb==0.1.1
33
33
  dploydb --no-color version
34
34
  ```
35
35
 
@@ -157,7 +157,7 @@ off-server backup, and protected retention are implemented.
157
157
  Install the published Alpha CLI:
158
158
 
159
159
  ```bash
160
- pipx install dploydb==0.1.0
160
+ pipx install dploydb==0.1.1
161
161
  dploydb version
162
162
  ```
163
163
 
@@ -66,6 +66,14 @@ different bytes. Keep the GitHub release as a draft, diagnose the failure, and
66
66
  rerun the immutable tag workflow when safe. PyPI files cannot be replaced or
67
67
  rolled back; a bad public release must be yanked and superseded by a new patch.
68
68
 
69
+ If a tag-triggered run fails before artifacts are built because the runner did
70
+ not materialize the annotated tag object, preserve the verified tag. Fix the
71
+ workflow through a protected pull request, merge it to `main`, and manually
72
+ dispatch `Publish release` from `main` with the existing canonical tag. The
73
+ recovery path explicitly fetches that remote tag object and checks out the
74
+ immutable tagged source; it must not be used to move or recreate a tag. The
75
+ normal protected TestPyPI and PyPI approvals still apply.
76
+
69
77
  ## One-time repository setup
70
78
 
71
79
  - Make the repository public, enable Issues, private vulnerability reporting,
@@ -25,7 +25,7 @@ docker compose version
25
25
  Install the exact published Alpha version into an isolated environment:
26
26
 
27
27
  ```bash
28
- pipx install dploydb==0.1.0
28
+ pipx install dploydb==0.1.1
29
29
  dploydb --no-color version
30
30
  ```
31
31
 
@@ -127,7 +127,10 @@ _URL_USERINFO = re.compile(
127
127
 
128
128
  def is_sensitive_key(key: str) -> bool:
129
129
  """Return whether a mapping key conventionally contains a secret value."""
130
- candidate = key.strip().strip("\"'").removeprefix("--")
130
+ normalized = key.strip().strip("\"'")
131
+ if normalized == "PWD":
132
+ return False
133
+ candidate = normalized.removeprefix("--")
131
134
  return _SENSITIVE_KEY.fullmatch(candidate) is not None
132
135
 
133
136
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "dploydb"
7
- version = "0.1.0"
7
+ version = "0.1.1"
8
8
  description = "Deployment safety for SQLite applications"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -198,6 +198,7 @@ def _demo_environment() -> dict[str, str]:
198
198
  "DPLOYDB_DEMO_UID": "0",
199
199
  "DPLOYDB_VERSION": "v1",
200
200
  "NO_COLOR": "1",
201
+ "PWD": "/workspace",
201
202
  "PYTHONPATH": "/workspace",
202
203
  }
203
204
 
@@ -288,6 +289,12 @@ def _run_readme_demo(container: str, executable: str) -> dict[str, Any]:
288
289
  release_payload = json.loads(releases.stdout)
289
290
  if release_payload.get("active_release_id") != deployment_payload.get("release_id"):
290
291
  _stop("clean Linux release history does not select the deployed release")
292
+ selected_releases = release_payload.get("releases")
293
+ if not isinstance(selected_releases, list) or len(selected_releases) != 1:
294
+ _stop(f"clean Linux release history was unexpected: {release_payload!r}")
295
+ operation_log = selected_releases[0].get("log_path")
296
+ if not isinstance(operation_log, str) or not operation_log.startswith("/workspace/"):
297
+ _stop(f"clean Linux release log path was not absolute: {operation_log!r}")
291
298
 
292
299
  database_check = _exec(
293
300
  container,
@@ -23,6 +23,8 @@ import yaml
23
23
 
24
24
  from dploydb.config import STARTER_CONFIGURATION
25
25
  from dploydb.models import new_operation_id
26
+ from dploydb.redaction import SecretRegistry
27
+ from dploydb.releases import ReleaseStore
26
28
  from dploydb.storage.local import LocalBackupStorage
27
29
 
28
30
  ROOT = Path(__file__).resolve().parents[2]
@@ -390,6 +392,8 @@ def test_real_docker_deploy_flows_and_pre_activation_traffic_isolation(
390
392
  time.sleep(0.025)
391
393
  assert any(record["observed"] == "old" for record in monitor.records)
392
394
 
395
+ deployment_environment = _environment(data, production_port, release)
396
+ deployment_environment["PWD"] = str(tmp_path)
393
397
  result = subprocess.run(
394
398
  [
395
399
  str(ROOT / ".venv" / "bin" / "dploydb"),
@@ -401,8 +405,8 @@ def test_real_docker_deploy_flows_and_pre_activation_traffic_isolation(
401
405
  "--json",
402
406
  "--non-interactive",
403
407
  ],
404
- cwd=ROOT,
405
- env=_environment(data, production_port, release),
408
+ cwd=tmp_path,
409
+ env=deployment_environment,
406
410
  capture_output=True,
407
411
  text=True,
408
412
  timeout=300,
@@ -413,6 +417,10 @@ def test_real_docker_deploy_flows_and_pre_activation_traffic_isolation(
413
417
  assert payload["outcome"] == expected_outcome
414
418
  assert payload["traffic_activated"] is (scenario == "success")
415
419
  assert payload["recovery_required"] is False
420
+ release_store = ReleaseStore(tmp_path / "state", secrets=SecretRegistry())
421
+ persisted_release, _pointers = release_store.lookup_history_release(payload["release_id"])
422
+ assert persisted_release.operation_log_path.is_absolute()
423
+ assert persisted_release.operation_log_path.is_relative_to(tmp_path)
416
424
 
417
425
  if scenario == "success":
418
426
  _wait_for_release(production_port, "v2")
@@ -46,6 +46,9 @@ def test_empty_secret_values_are_ignored_without_corrupting_output() -> None:
46
46
  "key",
47
47
  (
48
48
  "password",
49
+ "pwd",
50
+ "DATABASE_PWD",
51
+ "--pwd",
49
52
  "DATABASE_PASSWORD",
50
53
  "api-key",
51
54
  "S3_SECRET_ACCESS_KEY",
@@ -61,7 +64,10 @@ def test_sensitive_keys_are_recognized(key: str) -> None:
61
64
  assert is_sensitive_key(key)
62
65
 
63
66
 
64
- @pytest.mark.parametrize("key", ("project", "candidate_port", "secretary", "tokenizer"))
67
+ @pytest.mark.parametrize(
68
+ "key",
69
+ ("PWD", "project", "candidate_port", "secretary", "tokenizer"),
70
+ )
65
71
  def test_non_sensitive_keys_are_not_over_redacted(key: str) -> None:
66
72
  assert not is_sensitive_key(key)
67
73
 
@@ -72,6 +72,7 @@ from dploydb.subprocesses import (
72
72
  CapturedOutput,
73
73
  CommandOutcome,
74
74
  CommandResult,
75
+ SubprocessRunner,
75
76
  TerminationReason,
76
77
  )
77
78
  from dploydb.traffic import TrafficAction, TrafficHookResult
@@ -868,6 +869,36 @@ def test_successful_coordinator_activates_checked_release_in_exact_order(
868
869
  assert "traffic_activation_started" in {event.stage for event in events}
869
870
 
870
871
 
872
+ def test_standard_pwd_environment_preserves_absolute_release_paths(tmp_path: Path) -> None:
873
+ config_path, loaded = loaded_project(tmp_path)
874
+ command = SubprocessRunner(secrets=loaded.secrets).run(
875
+ [sys.executable, "-c", "pass"],
876
+ timeout_seconds=5,
877
+ environment={"PWD": str(tmp_path)},
878
+ working_directory=tmp_path,
879
+ )
880
+ assert command.succeeded
881
+ assert command.working_directory == str(tmp_path)
882
+
883
+ selected = harness(loaded, config_path)
884
+ result = run_deploy(loaded, config_path, selected)
885
+
886
+ persisted = ReleaseStore(
887
+ loaded.config.state_directory,
888
+ secrets=loaded.secrets,
889
+ ).read_manifest(result.release.release_id)
890
+ assert result.active is True
891
+ assert persisted.status is DeploymentState.ACTIVE
892
+ assert persisted.operation_log_path.is_absolute()
893
+ assert persisted.operation_log_path.is_relative_to(tmp_path)
894
+ assert persisted.previous_application is not None
895
+ assert persisted.previous_application.database_directory.is_absolute()
896
+ assert persisted.previous_application.database_directory.is_relative_to(tmp_path)
897
+ assert persisted.new_application is not None
898
+ assert persisted.new_application.database_directory.is_absolute()
899
+ assert persisted.new_application.database_directory.is_relative_to(tmp_path)
900
+
901
+
871
902
  def test_required_remote_final_backup_is_verified_before_production_migration(
872
903
  tmp_path: Path,
873
904
  ) -> None:
@@ -38,7 +38,7 @@ def test_readme_quick_start_uses_the_installed_cli_and_parses_real_json() -> Non
38
38
  readme = (ROOT / "README.md").read_text(encoding="utf-8")
39
39
 
40
40
  for required in (
41
- "pipx install dploydb==0.1.0",
41
+ "pipx install dploydb==0.1.1",
42
42
  "git clone https://github.com/recursiveway/dployDB.git",
43
43
  "python3 -m demo.prepare",
44
44
  "dploydb --no-color doctor --deep",
@@ -1,4 +1,4 @@
1
- """Release-readiness contracts for DployDB 0.1.0 Alpha."""
1
+ """Release-readiness contracts for DployDB 0.1.1 Alpha."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
@@ -29,7 +29,7 @@ def test_public_package_metadata_declares_alpha_license_owner_and_urls() -> None
29
29
 
30
30
  assert metadata["build-system"]["requires"] == ["hatchling>=1.27"]
31
31
  assert project["name"] == "dploydb"
32
- assert project["version"] == "0.1.0"
32
+ assert project["version"] == "0.1.1"
33
33
  assert project["license"] == "Apache-2.0"
34
34
  assert project["license-files"] == ["LICENSE", "NOTICE"]
35
35
  assert project["authors"] == [{"name": "RecursiveWay"}]
@@ -80,8 +80,8 @@ def test_license_notice_and_public_policies_are_present() -> None:
80
80
  def test_readme_is_explicitly_alpha_and_publicly_installable() -> None:
81
81
  readme = (ROOT / "README.md").read_text(encoding="utf-8")
82
82
 
83
- assert "DployDB 0.1.0 is Alpha software" in readme
84
- assert "pipx install dploydb==0.1.0" in readme
83
+ assert "DployDB 0.1.1 is Alpha software" in readme
84
+ assert "pipx install dploydb==0.1.1" in readme
85
85
  assert "Apache License 2.0" in readme
86
86
  assert "1.0.0" in readme
87
87
  assert "SECURITY.md" in readme
@@ -110,6 +110,21 @@ def test_workflows_pin_actions_and_limit_publish_permissions() -> None:
110
110
  assert "--draft=false --prerelease=true" in release
111
111
 
112
112
 
113
+ def test_release_workflow_recovers_an_existing_immutable_tag_from_main() -> None:
114
+ release = WORKFLOWS[1].read_text(encoding="utf-8")
115
+
116
+ assert "workflow_dispatch:" in release
117
+ assert "Existing verified release tag to recover" in release
118
+ assert "release tag is not canonical" in release
119
+ assert 'test "$GITHUB_REF" = "refs/heads/main"' in release
120
+ assert '"refs/tags/${TAG}:refs/tags/${TAG}"' in release
121
+ assert 'test "$object_type" = "tag"' in release
122
+ assert 'test "$remote_target" = "$(git rev-parse "${TAG}^{}")"' in release
123
+ assert ".verification.verified" in release
124
+ assert release.count("ref: refs/tags/${{ env.RELEASE_TAG }}") == 4
125
+ assert "${{ github.ref_name }}" not in release
126
+
127
+
113
128
  def _write_wheel(path: Path, version: str) -> None:
114
129
  with zipfile.ZipFile(path, "w") as archive:
115
130
  archive.writestr(
@@ -298,7 +298,7 @@ wheels = [
298
298
 
299
299
  [[package]]
300
300
  name = "dploydb"
301
- version = "0.1.0"
301
+ version = "0.1.1"
302
302
  source = { editable = "." }
303
303
  dependencies = [
304
304
  { name = "boto3" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes