@mriekki/project-forge 2.1.0 → 3.0.0
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.
- package/assets/components.json +3 -3
- package/assets/deployment/distribution.json +1 -1
- package/assets/generated/ASSEMBLY.json +40 -40
- package/assets/generated/core/core/agents/reviewer.agent.md +1 -1
- package/assets/generated/core/core/instructions/project-forge-workflow.instructions.md +5 -1
- package/assets/generated/core/core/process/DEPLOYMENT_ENABLEMENT.md +1 -1
- package/assets/generated/core/core/process/GUIDED_NEXT_STEPS.md +22 -6
- package/assets/generated/core/core/process/INTERACTIVE_SESSION_GUIDE.md +31 -1
- package/assets/generated/core/core/process/PROJECT_DOCUMENTATION_FRAMEWORK.md +3 -3
- package/assets/generated/core/core/process/PROJECT_FORGE_ENGINEERING_GUIDE.md +14 -14
- package/assets/generated/core/core/skills/cleanup-branches/SKILL.md +6 -10
- package/assets/generated/core/core/skills/code-walkthrough/SKILL.md +12 -8
- package/assets/generated/core/core/skills/core-sync/SKILL.md +17 -11
- package/assets/generated/core/core/skills/define-feature/SKILL.md +9 -18
- package/assets/generated/core/core/skills/deliver/SKILL.md +12 -8
- package/assets/generated/core/core/skills/deploy/SKILL.md +3 -3
- package/assets/generated/core/core/skills/deployment-readiness/SKILL.md +1 -1
- package/assets/generated/core/core/skills/grill-with-docs/SKILL.md +6 -8
- package/assets/generated/core/core/skills/next-step/SKILL.md +10 -11
- package/assets/generated/core/core/skills/progress/SKILL.md +1 -1
- package/assets/generated/core/core/skills/project-forge-update/SKILL.md +10 -6
- package/assets/generated/core/core/skills/pull-latest/SKILL.md +16 -10
- package/assets/generated/core/core/skills/refine-requirements/SKILL.md +37 -16
- package/assets/generated/core/core/skills/setup/SKILL.md +25 -22
- package/assets/generated/core/core/skills/start-work/SKILL.md +11 -9
- package/assets/generated/core/core/skills/tear-down/SKILL.md +4 -4
- package/assets/generated/deployment/docs/DEVELOPER_ENVIRONMENTS.md +11 -11
- package/assets/generated/deployment/docs/ENVIRONMENT_PROOF.md +2 -2
- package/assets/generated/deployment/docs/WORKSPACE_INSTANCES.md +1 -1
- package/assets/generated/deployment/docs/images/developer-environment-journey.svg +2 -2
- package/assets/generated/deployment/docs/images/{poc-environment-model.svg → project-environment-model.svg} +4 -4
- package/assets/generated/deployment/src/project_forge_deployment/cli.py +1 -1
- package/assets/generated/deployment/src/project_forge_deployment/entrypoint.py +1 -1
- package/assets/generated/deployment/src/project_forge_deployment/environment_probe.py +2 -2
- package/assets/generated/deployment/src/project_forge_deployment/route_registry.py +2 -2
- package/assets/generated/deployment/src/project_forge_deployment/teardown.py +7 -7
- package/assets/generated/deployment/src/project_forge_deployment/teardown_cli.py +6 -6
- package/assets/generated/deployment/src/project_forge_deployment/teardown_remote.py +6 -6
- package/package.json +1 -1
|
@@ -111,16 +111,16 @@ Explain why the level was chosen.
|
|
|
111
111
|
|
|
112
112
|
## 4. Optional learning checkpoint
|
|
113
113
|
|
|
114
|
-
For STANDARD/ELEVATED work, once requirements are ready, coaching may be offered but is never required
|
|
114
|
+
For STANDARD/ELEVATED work, once requirements are ready, coaching may be offered but is never required. When the human needs to choose, render a **single-select structured choice** through the host UI when available; in VS Code use the native question surface such as `vscode_askQuestions`.
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
1. Continue without coaching — recommended default
|
|
118
|
-
2. /coach for a short pre-build concept review
|
|
119
|
-
3. /grill-me for challenge rather than teaching
|
|
120
|
-
4. Stop
|
|
121
|
-
```
|
|
116
|
+
Relevant choices are:
|
|
122
117
|
|
|
123
|
-
|
|
118
|
+
- `Continue without coaching` — `RECOMMENDED` default
|
|
119
|
+
- `/coach` for a short pre-build concept review
|
|
120
|
+
- `/grill-me` for challenge rather than teaching
|
|
121
|
+
- `Stop`
|
|
122
|
+
|
|
123
|
+
Use a numbered text list only when structured choice UI is unavailable. For LIGHT work, do not interrupt with coaching unless requested.
|
|
124
124
|
|
|
125
125
|
## 5. Planning rules
|
|
126
126
|
|
|
@@ -185,6 +185,8 @@ Return:
|
|
|
185
185
|
- requirement readiness,
|
|
186
186
|
- branch state / expected short-lived branch when implementation is about to begin,
|
|
187
187
|
- RECOMMENDED NEXT STEP,
|
|
188
|
-
- a small
|
|
188
|
+
- a small set of relevant action choices.
|
|
189
|
+
|
|
190
|
+
Render mutually exclusive next actions through the host's **single-select structured choice UI** when available, following `INTERACTIVE_SESSION_GUIDE.md`. In VS Code, use the native question surface such as `vscode_askQuestions`. Make the recommended action visible when evidence supports one, include `Other / specify` or free-text only when the action set may be incomplete, and use a numbered text menu only as the no-structured-UI fallback.
|
|
189
191
|
|
|
190
192
|
Do not skip an unresolved requirement/plan gate merely because implementation is technically possible. Once implementation is selected/approved, do not insert a separate ceremonial branch-creation gate when the branch setup is safe and deterministic.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: tear-down
|
|
3
|
-
description: "Guided destructive cleanup for local state, one exact repository-defined named environment, or all explicitly configured
|
|
3
|
+
description: "Guided destructive cleanup for local state, one exact repository-defined named environment, or all explicitly configured Project environments. Preserves ownership boundaries and resolved Production-data authorization."
|
|
4
4
|
argument-hint: "[local|<environment>|all]"
|
|
5
5
|
user-invocable: true
|
|
6
6
|
disable-model-invocation: true
|
|
@@ -8,7 +8,7 @@ disable-model-invocation: true
|
|
|
8
8
|
|
|
9
9
|
# Tear Down
|
|
10
10
|
|
|
11
|
-
`/tear-down` is the human-facing destructive cleanup workflow for repository-owned
|
|
11
|
+
`/tear-down` is the human-facing destructive cleanup workflow for repository-owned Project state.
|
|
12
12
|
|
|
13
13
|
Logical environment names are data-driven and are not limited to Staging/Production:
|
|
14
14
|
|
|
@@ -47,7 +47,7 @@ all
|
|
|
47
47
|
|
|
48
48
|
If no scope is named, present only scopes the repository actually supports; never silently default to a Production environment or `all`.
|
|
49
49
|
|
|
50
|
-
`all` means only all explicitly configured scopes for this
|
|
50
|
+
`all` means only all explicitly configured scopes for this Project; it never means machine-wide cleanup.
|
|
51
51
|
|
|
52
52
|
Selecting scope does not authorize deletion. Physical colocation never merges ownership: deleting `demo` must not remove `staging` or `prod-east` merely because they share hardware.
|
|
53
53
|
|
|
@@ -99,7 +99,7 @@ Do not promise deletion of named/shared Docker volumes unless the installed dete
|
|
|
99
99
|
Every modifying tear-down requires the installed normal authorization, conceptually:
|
|
100
100
|
|
|
101
101
|
```text
|
|
102
|
-
[
|
|
102
|
+
[PROJECT TEAR DOWN]
|
|
103
103
|
```
|
|
104
104
|
|
|
105
105
|
whenever **any selected environment is Production-class**, require the stronger Production-data deletion authorization, conceptually:
|
|
@@ -13,7 +13,7 @@ The happy path intentionally hides Bootstrap/package/control-plane plumbing. Dev
|
|
|
13
13
|
The normal progression is:
|
|
14
14
|
|
|
15
15
|
```text
|
|
16
|
-
generated
|
|
16
|
+
generated Project repository
|
|
17
17
|
→ clone/open workspace
|
|
18
18
|
→ /setup
|
|
19
19
|
→ LOCAL READY
|
|
@@ -42,7 +42,7 @@ The normal conversational entry point is:
|
|
|
42
42
|
|
|
43
43
|
`/setup` explains the current state/options and invokes the correct repository-local setup launcher. Direct script invocation remains the non-AI/automation fallback.
|
|
44
44
|
|
|
45
|
-
The first local success target is `LOCAL WORKSPACE READY`. Remote access must not block it unless the
|
|
45
|
+
The first local success target is `LOCAL WORKSPACE READY`. Remote access must not block it unless the Project explicitly requires a remote dependency for local development.
|
|
46
46
|
|
|
47
47
|
## Progress and next action
|
|
48
48
|
|
|
@@ -156,11 +156,11 @@ A changed previously pinned host key remains a blocker and requires explicit tru
|
|
|
156
156
|
|
|
157
157
|
## Docker and host preparation
|
|
158
158
|
|
|
159
|
-
Docker is a workstation/target capability, not something every
|
|
159
|
+
Docker is a workstation/target capability, not something every Project should reinstall.
|
|
160
160
|
|
|
161
161
|
### Developer workstation
|
|
162
162
|
|
|
163
|
-
The first
|
|
163
|
+
The first Project on a computer may install/verify required local tools and Docker through the generated setup contract. Later Projects reuse the ready workstation.
|
|
164
164
|
|
|
165
165
|
### Remote Ubuntu target
|
|
166
166
|
|
|
@@ -209,17 +209,17 @@ A credential needed to acquire a private image is entered only through the local
|
|
|
209
209
|
|
|
210
210
|
If the exact immutable image is already cached locally, registry authentication is not required merely to transfer it to the remote target.
|
|
211
211
|
|
|
212
|
-
##
|
|
212
|
+
## Project environment model
|
|
213
213
|
|
|
214
|
-

|
|
215
215
|
|
|
216
|
-
The
|
|
216
|
+
The Project owns project-specific facts such as environment identities/roles, placement, target binding, application image, runtime/health contract, persistence, routes/exposure, and Production classification/authorization.
|
|
217
217
|
|
|
218
|
-
The reusable workstation/target layer owns machine mechanics. A
|
|
218
|
+
The reusable workstation/target layer owns machine mechanics. A Project references targets; it does not own or duplicate a developer's private credentials.
|
|
219
219
|
|
|
220
220
|
## New workstation
|
|
221
221
|
|
|
222
|
-
A developer normally starts from any generated
|
|
222
|
+
A developer normally starts from any generated Project:
|
|
223
223
|
|
|
224
224
|
```text
|
|
225
225
|
clone repository
|
|
@@ -233,7 +233,7 @@ When GitHub is reachable, personal profile sync supplies portable target definit
|
|
|
233
233
|
|
|
234
234
|
## New remote box
|
|
235
235
|
|
|
236
|
-
Adding a new remote target is a workstation-driven onboarding operation, not a
|
|
236
|
+
Adding a new remote target is a workstation-driven onboarding operation, not a Project implementation step:
|
|
237
237
|
|
|
238
238
|
```text
|
|
239
239
|
add/import portable target definition
|
|
@@ -243,7 +243,7 @@ add/import portable target definition
|
|
|
243
243
|
→ TARGET READY
|
|
244
244
|
```
|
|
245
245
|
|
|
246
|
-
Once ready, multiple
|
|
246
|
+
Once ready, multiple Projects can bind logical environments to that target without repeating host bootstrap.
|
|
247
247
|
|
|
248
248
|
## Portable export/import fallback
|
|
249
249
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Environment Proof Deployment
|
|
2
2
|
|
|
3
|
-
A Project Forge environment proof verifies that one developer workstation can exercise the real remote deployment path for one exact named logical environment before the
|
|
3
|
+
A Project Forge environment proof verifies that one developer workstation can exercise the real remote deployment path for one exact named logical environment before the Project application exists or before its first real deployment.
|
|
4
4
|
|
|
5
5
|
It is intentionally **not** the project application.
|
|
6
6
|
|
|
@@ -84,7 +84,7 @@ The proof attempts cleanup even when deployment raises after mutation begins.
|
|
|
84
84
|
|
|
85
85
|
Successful completion requires terminal cleanup evidence (`removed` or already `absent`) for the reserved probe runtime. If deployment succeeds but cleanup cannot be verified, the command fails rather than reporting a clean proof.
|
|
86
86
|
|
|
87
|
-
The probe must not delete another
|
|
87
|
+
The probe must not delete another Project, another application, another logical environment, or shared host platform state.
|
|
88
88
|
|
|
89
89
|
## Relationship to `/setup` and `/deploy`
|
|
90
90
|
|
|
@@ -63,7 +63,7 @@ Local mutable application data is not synchronized between workstations by Proje
|
|
|
63
63
|
|
|
64
64
|
## Setup on another workstation
|
|
65
65
|
|
|
66
|
-
The normal flow for an existing generated
|
|
66
|
+
The normal flow for an existing generated Project is:
|
|
67
67
|
|
|
68
68
|
```text
|
|
69
69
|
clone repository
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="520" viewBox="0 0 1200 520" role="img" aria-labelledby="title desc">
|
|
2
2
|
<title id="title">Project Forge developer environment journey</title>
|
|
3
|
-
<desc id="desc">Generated
|
|
3
|
+
<desc id="desc">Generated Project repository progresses through clone and setup to local ready, then optionally to staging ready and production ready.</desc>
|
|
4
4
|
<defs>
|
|
5
5
|
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
|
|
6
6
|
<stop offset="0" stop-color="#0b1220"/>
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
<g filter="url(#shadow)" font-family="Inter,Segoe UI,Arial,sans-serif">
|
|
21
21
|
<rect x="70" y="180" width="205" height="128" rx="18" fill="#1e2a40" stroke="#405274" stroke-width="2"/>
|
|
22
22
|
<text x="172" y="222" text-anchor="middle" font-size="15" font-weight="700" fill="#91a4c4">PROJECT</text>
|
|
23
|
-
<text x="172" y="254" text-anchor="middle" font-size="22" font-weight="700" fill="#ffffff">Generated
|
|
23
|
+
<text x="172" y="254" text-anchor="middle" font-size="22" font-weight="700" fill="#ffffff">Generated Project</text>
|
|
24
24
|
<text x="172" y="282" text-anchor="middle" font-size="15" fill="#b8c5d8">clone + open workspace</text>
|
|
25
25
|
|
|
26
26
|
<rect x="360" y="180" width="205" height="128" rx="18" fill="#1f3440" stroke="#3b7584" stroke-width="2"/>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="720" viewBox="0 0 1200 720" role="img" aria-labelledby="title desc">
|
|
2
2
|
<title id="title">Project Forge environment model</title>
|
|
3
|
-
<desc id="desc">A
|
|
3
|
+
<desc id="desc">A Project uses local workstation runtime and optional remote logical staging and production environments bound to reusable remote targets and shared host platform infrastructure.</desc>
|
|
4
4
|
<defs>
|
|
5
5
|
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
|
|
6
6
|
<stop offset="0" stop-color="#0b1220"/>
|
|
@@ -14,12 +14,12 @@
|
|
|
14
14
|
</marker>
|
|
15
15
|
</defs>
|
|
16
16
|
<rect width="1200" height="720" rx="24" fill="url(#bg)"/>
|
|
17
|
-
<text x="70" y="70" font-family="Inter,Segoe UI,Arial,sans-serif" font-size="30" font-weight="700" fill="#f7f9fc">
|
|
18
|
-
<text x="70" y="102" font-family="Inter,Segoe UI,Arial,sans-serif" font-size="17" fill="#aebbd0">The
|
|
17
|
+
<text x="70" y="70" font-family="Inter,Segoe UI,Arial,sans-serif" font-size="30" font-weight="700" fill="#f7f9fc">Project Environment Model</text>
|
|
18
|
+
<text x="70" y="102" font-family="Inter,Segoe UI,Arial,sans-serif" font-size="17" fill="#aebbd0">The Project owns application/environment facts; reusable workstation and target layers own machine mechanics.</text>
|
|
19
19
|
|
|
20
20
|
<g filter="url(#shadow)" font-family="Inter,Segoe UI,Arial,sans-serif">
|
|
21
21
|
<rect x="390" y="135" width="420" height="106" rx="20" fill="#25294a" stroke="#7277c9" stroke-width="2"/>
|
|
22
|
-
<text x="600" y="176" text-anchor="middle" font-size="15" font-weight="700" fill="#aaaef1">GENERATED
|
|
22
|
+
<text x="600" y="176" text-anchor="middle" font-size="15" font-weight="700" fill="#aaaef1">GENERATED Project</text>
|
|
23
23
|
<text x="600" y="209" text-anchor="middle" font-size="21" font-weight="700" fill="#ffffff">Environment bindings + app runtime facts</text>
|
|
24
24
|
|
|
25
25
|
<rect x="95" y="330" width="300" height="190" rx="20" fill="#173c35" stroke="#3a9d82" stroke-width="2"/>
|
|
@@ -283,7 +283,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
283
283
|
route_inventory_parser.add_argument("--json", action="store_true")
|
|
284
284
|
|
|
285
285
|
route_register = route_sub.add_parser(
|
|
286
|
-
"register", help="Create or update one
|
|
286
|
+
"register", help="Create or update one Project/environment-owned Caddy + mDNS route"
|
|
287
287
|
)
|
|
288
288
|
route_register.add_argument("name")
|
|
289
289
|
route_register.add_argument("--owner-repo", required=True)
|
|
@@ -33,5 +33,5 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
33
33
|
print("Command group: local Loopback-only workstation application runtime")
|
|
34
34
|
print("Command group: registry Read-only exact-image registry access verification")
|
|
35
35
|
print("Command group: sqlite SQLite persistence import/backup/migration/rollback checks")
|
|
36
|
-
print("Command group: tear-down Scoped destructive
|
|
36
|
+
print("Command group: tear-down Scoped destructive Project environment/local cleanup")
|
|
37
37
|
return core_main(args)
|
|
@@ -14,7 +14,7 @@ from .app_runtime_workstation import apply_app_deploy_from_workstation
|
|
|
14
14
|
from .box_platform import platform_status
|
|
15
15
|
from .errors import DeploymentKitError
|
|
16
16
|
from .registry_access import normalize_registry_image
|
|
17
|
-
from .teardown import
|
|
17
|
+
from .teardown import tear_down_project
|
|
18
18
|
|
|
19
19
|
ENVIRONMENT_PROOF_WRITE_PHRASE = "[ENVIRONMENT PROOF WRITE]"
|
|
20
20
|
PRODUCTION_ENVIRONMENT_PROOF_WRITE_PHRASE = "[PRODUCTION ENVIRONMENT PROOF]"
|
|
@@ -171,7 +171,7 @@ def run_environment_proof(plan: dict[str, Any]) -> dict[str, Any]:
|
|
|
171
171
|
deploy_error = error
|
|
172
172
|
finally:
|
|
173
173
|
try:
|
|
174
|
-
cleanup =
|
|
174
|
+
cleanup = tear_down_project(
|
|
175
175
|
{
|
|
176
176
|
"ownerRepo": plan["ownerRepo"],
|
|
177
177
|
"appSlug": plan["probeAppSlug"],
|
|
@@ -270,7 +270,7 @@ def plan_route_registration(
|
|
|
270
270
|
owner_fields = ("ownerRepo", "environment", "appSlug")
|
|
271
271
|
if any(existing[field] != spec[field] for field in owner_fields):
|
|
272
272
|
raise DeploymentKitError(
|
|
273
|
-
f"Route ID {spec['routeId']} is already owned by another
|
|
273
|
+
f"Route ID {spec['routeId']} is already owned by another Project/environment; refusing takeover."
|
|
274
274
|
)
|
|
275
275
|
requested = set(spec["hostnames"])
|
|
276
276
|
for route in inventory["managedRoutes"]:
|
|
@@ -312,7 +312,7 @@ def plan_route_removal(
|
|
|
312
312
|
if existing is None:
|
|
313
313
|
raise DeploymentKitError(f"Managed route does not exist: {route_id}")
|
|
314
314
|
if existing["ownerRepo"] != owner_repo or existing["environment"] != environment or existing["appSlug"] != app_slug:
|
|
315
|
-
raise DeploymentKitError("Route removal ownership does not match the registered
|
|
315
|
+
raise DeploymentKitError("Route removal ownership does not match the registered Project/environment.")
|
|
316
316
|
return {
|
|
317
317
|
"targetName": name,
|
|
318
318
|
"action": "remove",
|
|
@@ -15,7 +15,7 @@ from .route_registry import apply_route_change, plan_route_removal, route_invent
|
|
|
15
15
|
from .ssh import ssh_args, verify_access
|
|
16
16
|
from .teardown_remote import REMOTE_SCRIPT
|
|
17
17
|
|
|
18
|
-
TEAR_DOWN_WRITE_PHRASE = "[
|
|
18
|
+
TEAR_DOWN_WRITE_PHRASE = "[PROJECT TEAR DOWN]"
|
|
19
19
|
PRODUCTION_DATA_DELETE_PHRASE = "[PRODUCTION DATA DELETE]"
|
|
20
20
|
RESERVED_SCOPES = ("local", "all")
|
|
21
21
|
|
|
@@ -167,13 +167,13 @@ def _tear_down_remote_environment(
|
|
|
167
167
|
stderr = completed.stderr.decode("utf-8", errors="replace").strip()
|
|
168
168
|
if completed.returncode != 0:
|
|
169
169
|
raise DeploymentKitError(
|
|
170
|
-
"Remote
|
|
170
|
+
"Remote Project tear-down failed: " + (stderr or stdout or f"exit {completed.returncode}")
|
|
171
171
|
)
|
|
172
172
|
parts = stdout.split("\t")
|
|
173
173
|
if len(parts) not in {4, 5} or parts[0] != "TEAR_DOWN" or parts[1] not in {"absent", "removed"}:
|
|
174
|
-
raise DeploymentKitError("Remote
|
|
174
|
+
raise DeploymentKitError("Remote Project tear-down returned malformed evidence.")
|
|
175
175
|
if parts[2] != app_slug or parts[3] != environment:
|
|
176
|
-
raise DeploymentKitError("Remote
|
|
176
|
+
raise DeploymentKitError("Remote Project tear-down returned mismatched app/environment evidence.")
|
|
177
177
|
result: dict[str, Any] = {"state": parts[1], "mutated": parts[1] == "removed"}
|
|
178
178
|
if len(parts) == 5:
|
|
179
179
|
result["containerName"] = parts[4]
|
|
@@ -219,7 +219,7 @@ def tear_down_workstation_package(package: str) -> dict[str, Any]:
|
|
|
219
219
|
removed = _run_local([docker, "rm", "-f", "-v", *containers])
|
|
220
220
|
if removed.returncode != 0:
|
|
221
221
|
raise DeploymentKitError(
|
|
222
|
-
"Local
|
|
222
|
+
"Local Project container removal failed: " + (removed.stderr or removed.stdout).strip()
|
|
223
223
|
)
|
|
224
224
|
|
|
225
225
|
images_result = _run_local([docker, "images", "--format", "{{.Repository}}:{{.Tag}}"])
|
|
@@ -238,7 +238,7 @@ def tear_down_workstation_package(package: str) -> dict[str, Any]:
|
|
|
238
238
|
removed = _run_local([docker, "image", "rm", "-f", *image_refs])
|
|
239
239
|
if removed.returncode != 0:
|
|
240
240
|
raise DeploymentKitError(
|
|
241
|
-
"Local
|
|
241
|
+
"Local Project image removal failed: " + (removed.stderr or removed.stdout).strip()
|
|
242
242
|
)
|
|
243
243
|
return {
|
|
244
244
|
"state": "removed" if containers or image_refs else "absent",
|
|
@@ -248,7 +248,7 @@ def tear_down_workstation_package(package: str) -> dict[str, Any]:
|
|
|
248
248
|
}
|
|
249
249
|
|
|
250
250
|
|
|
251
|
-
def
|
|
251
|
+
def tear_down_project(spec: dict[str, Any]) -> dict[str, Any]:
|
|
252
252
|
normalized = normalize_teardown_spec(
|
|
253
253
|
owner_repo=spec.get("ownerRepo"),
|
|
254
254
|
app_slug=spec.get("appSlug"),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""CLI for scoped destructive
|
|
1
|
+
"""CLI for scoped destructive Project tear-down."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
@@ -12,7 +12,7 @@ from .teardown import (
|
|
|
12
12
|
normalize_teardown_spec,
|
|
13
13
|
parse_environment_targets,
|
|
14
14
|
render_teardown_summary,
|
|
15
|
-
|
|
15
|
+
tear_down_project,
|
|
16
16
|
)
|
|
17
17
|
|
|
18
18
|
|
|
@@ -83,7 +83,7 @@ def _preview(spec: dict[str, object]) -> dict[str, object]:
|
|
|
83
83
|
"deletesPersistentData": bool(spec["environmentTargets"]),
|
|
84
84
|
"deletesProductionData": production,
|
|
85
85
|
"preservesSharedPlatform": True,
|
|
86
|
-
"
|
|
86
|
+
"preservesUnrelatedProjects": True,
|
|
87
87
|
"deletesGhcrPackage": False,
|
|
88
88
|
"requiredAuthorization": TEAR_DOWN_WRITE_PHRASE,
|
|
89
89
|
"requiredProductionDataAuthorization": PRODUCTION_DATA_DELETE_PHRASE if production else None,
|
|
@@ -111,7 +111,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
111
111
|
and args.production_data_authorization != PRODUCTION_DATA_DELETE_PHRASE
|
|
112
112
|
)
|
|
113
113
|
if missing_general or missing_production:
|
|
114
|
-
print("
|
|
114
|
+
print("PROJECT TEAR-DOWN PREVIEW")
|
|
115
115
|
print(render_teardown_summary(preview))
|
|
116
116
|
if missing_general:
|
|
117
117
|
print(
|
|
@@ -126,8 +126,8 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
126
126
|
)
|
|
127
127
|
return 2
|
|
128
128
|
|
|
129
|
-
result =
|
|
130
|
-
print("
|
|
129
|
+
result = tear_down_project(spec)
|
|
130
|
+
print("PROJECT TEAR-DOWN COMPLETE")
|
|
131
131
|
print(render_teardown_summary(result))
|
|
132
132
|
return 0
|
|
133
133
|
except DeploymentKitError as exc:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Remote destructive tear-down for one Kit-managed
|
|
1
|
+
"""Remote destructive tear-down for one Kit-managed Project environment."""
|
|
2
2
|
|
|
3
3
|
REMOTE_SCRIPT = r'''#!/usr/bin/env bash
|
|
4
4
|
set -Eeuo pipefail
|
|
@@ -37,7 +37,7 @@ if [[ ! -e "$ROOT" ]]; then
|
|
|
37
37
|
fi
|
|
38
38
|
|
|
39
39
|
[[ -d "$ROOT" && ! -L "$ROOT" ]] || {
|
|
40
|
-
echo "
|
|
40
|
+
echo "Project environment root is not a safe managed directory: $ROOT" >&2
|
|
41
41
|
exit 21
|
|
42
42
|
}
|
|
43
43
|
|
|
@@ -46,16 +46,16 @@ HAS_PERSISTENCE=false
|
|
|
46
46
|
[[ -f "$STATE" && ! -L "$STATE" ]] && HAS_STATE=true
|
|
47
47
|
[[ -f "$PERSISTENCE" && ! -L "$PERSISTENCE" ]] && HAS_PERSISTENCE=true
|
|
48
48
|
[[ "$HAS_STATE" == true || "$HAS_PERSISTENCE" == true ]] || {
|
|
49
|
-
echo "
|
|
49
|
+
echo "Project environment root has no managed ownership marker: $ROOT" >&2
|
|
50
50
|
exit 21
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
if [[ "$HAS_STATE" == true ]] && ! assert_owned_marker "$STATE"; then
|
|
54
|
-
echo "
|
|
54
|
+
echo "Project deployment ownership mismatch for $ROOT" >&2
|
|
55
55
|
exit 22
|
|
56
56
|
fi
|
|
57
57
|
if [[ "$HAS_PERSISTENCE" == true ]] && ! assert_owned_marker "$PERSISTENCE"; then
|
|
58
|
-
echo "
|
|
58
|
+
echo "Project persistence ownership mismatch for $ROOT" >&2
|
|
59
59
|
exit 22
|
|
60
60
|
fi
|
|
61
61
|
|
|
@@ -87,7 +87,7 @@ if [[ "$HAS_STATE" == true ]]; then
|
|
|
87
87
|
fi
|
|
88
88
|
fi
|
|
89
89
|
|
|
90
|
-
# The verified environment root is owned by this
|
|
90
|
+
# The verified environment root is owned by this Project/environment. Tear-down
|
|
91
91
|
# removes releases, evidence, persistent data, and backups together.
|
|
92
92
|
rm -rf --one-file-system -- "$ROOT"
|
|
93
93
|
|