@mriekki/lean 0.1.0 → 0.2.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/README.md +71 -67
- package/assets/components.json +33 -33
- package/assets/core/distribution.json +52 -51
- package/assets/deployment/distribution.json +34 -34
- package/assets/generated/ASSEMBLY.json +38 -33
- package/assets/generated/core/core/skills/core-sync/SKILL.md +78 -120
- package/assets/generated/core/core/skills/deploy/SKILL.md +74 -91
- package/assets/generated/core/core/skills/lean-update/SKILL.md +189 -0
- package/assets/generated/core/core/skills/upstream-fix/SKILL.md +91 -76
- package/assets/generated/deployment/distribution/lean-poc-deploy.py +8 -8
- package/assets/generated/deployment/src/lean_poc_deployment/__init__.py +3 -3
- package/assets/generated/deployment/src/lean_poc_deployment/__main__.py +3 -3
- package/assets/generated/deployment/src/lean_poc_deployment/app_cli.py +140 -140
- package/assets/generated/deployment/src/lean_poc_deployment/app_runtime.py +455 -455
- package/assets/generated/deployment/src/lean_poc_deployment/app_runtime_remote.py +415 -415
- package/assets/generated/deployment/src/lean_poc_deployment/box_platform.py +208 -208
- package/assets/generated/deployment/src/lean_poc_deployment/box_platform_assets.py +100 -100
- package/assets/generated/deployment/src/lean_poc_deployment/box_platform_remote.py +271 -271
- package/assets/generated/deployment/src/lean_poc_deployment/cli.py +437 -437
- package/assets/generated/deployment/src/lean_poc_deployment/doctor.py +121 -121
- package/assets/generated/deployment/src/lean_poc_deployment/entrypoint.py +25 -25
- package/assets/generated/deployment/src/lean_poc_deployment/errors.py +5 -5
- package/assets/generated/deployment/src/lean_poc_deployment/keys.py +171 -171
- package/assets/generated/deployment/src/lean_poc_deployment/profiles.py +204 -204
- package/assets/generated/deployment/src/lean_poc_deployment/route_registry.py +367 -367
- package/assets/generated/deployment/src/lean_poc_deployment/route_registry_remote.py +443 -443
- package/assets/generated/deployment/src/lean_poc_deployment/security.py +290 -290
- package/assets/generated/deployment/src/lean_poc_deployment/sqlite_adapter.py +334 -334
- package/assets/generated/deployment/src/lean_poc_deployment/sqlite_adapter_remote.py +339 -339
- package/assets/generated/deployment/src/lean_poc_deployment/sqlite_cli.py +85 -85
- package/assets/generated/deployment/src/lean_poc_deployment/sqlite_import_recovery.py +437 -437
- package/assets/generated/deployment/src/lean_poc_deployment/ssh.py +118 -118
- package/assets/generated/deployment/src/lean_poc_deployment/teardown.py +286 -286
- package/assets/generated/deployment/src/lean_poc_deployment/teardown_cli.py +120 -120
- package/assets/generated/deployment/src/lean_poc_deployment/teardown_remote.py +100 -100
- package/assets/generated/deployment/src/lean_poc_deployment/trust.py +278 -278
- package/bin/lean.mjs +9 -9
- package/package.json +33 -33
- package/src/cli.mjs +131 -131
- package/src/engine.mjs +243 -243
- package/src/package-data.mjs +18 -18
|
@@ -1,86 +1,86 @@
|
|
|
1
|
-
"""CLI for the SQLite persistence adapter."""
|
|
2
|
-
from __future__ import annotations
|
|
3
|
-
import argparse, json, sys
|
|
4
|
-
from typing import Any
|
|
5
|
-
from .errors import DeploymentKitError
|
|
6
|
-
from .sqlite_adapter import authorization_phrase, apply_sqlite_operation, normalize_sqlite_request, plan_sqlite_operation, sqlite_inventory
|
|
7
|
-
from .sqlite_import_recovery import (
|
|
8
|
-
apply_sqlite_import_recovery,
|
|
9
|
-
authorization_phrase as recovery_authorization_phrase,
|
|
10
|
-
plan_sqlite_import_recovery,
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def _print(value: Any, *, as_json: bool) -> None:
|
|
15
|
-
if as_json:
|
|
16
|
-
print(json.dumps(value, indent=2, sort_keys=True)); return
|
|
17
|
-
if isinstance(value, dict):
|
|
18
|
-
for key, item in value.items():
|
|
19
|
-
print(f"{key}: {json.dumps(item, sort_keys=True) if isinstance(item,(dict,list)) else item}")
|
|
20
|
-
else: print(value)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def _parse_env(items: list[str]) -> list[tuple[str,str]]:
|
|
24
|
-
out=[]
|
|
25
|
-
for item in items:
|
|
26
|
-
if '=' not in item: raise DeploymentKitError("--maintenance-env entries must use NAME=value form.")
|
|
27
|
-
out.append(tuple(item.split('=',1)))
|
|
28
|
-
return out
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def _confirm(args: argparse.Namespace, phrase: str, action: str) -> None:
|
|
32
|
-
if args.yes: return
|
|
33
|
-
if input(f"Type {phrase} to {action}: ").strip() != phrase:
|
|
34
|
-
raise DeploymentKitError(f"{action.capitalize()} was not authorized.")
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def _common(parser: argparse.ArgumentParser) -> None:
|
|
38
|
-
parser.add_argument('name')
|
|
39
|
-
parser.add_argument('--owner-repo', required=True)
|
|
40
|
-
parser.add_argument('--environment', required=True)
|
|
41
|
-
parser.add_argument('--app-slug', required=True)
|
|
42
|
-
parser.add_argument('--image', required=True)
|
|
43
|
-
parser.add_argument('--maintenance-env', action='append', default=[], help='Non-secret image maintenance environment value in NAME=value form; repeat as needed')
|
|
44
|
-
parser.add_argument('--json', action='store_true')
|
|
45
|
-
parser.add_argument('--yes', action='store_true')
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def _import_args(parser: argparse.ArgumentParser) -> None:
|
|
49
|
-
parser.add_argument('--run-as', required=True)
|
|
50
|
-
parser.add_argument('--database-file', required=True)
|
|
51
|
-
parser.add_argument('--source-database', required=True)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def build_parser() -> argparse.ArgumentParser:
|
|
55
|
-
parser=argparse.ArgumentParser(prog='lean-poc-deploy sqlite')
|
|
56
|
-
sub=parser.add_subparsers(dest='command',required=True)
|
|
57
|
-
inv=sub.add_parser('inventory',help='Read SQLite persistence reservations without writes'); inv.add_argument('name'); inv.add_argument('--json',action='store_true')
|
|
58
|
-
imp=sub.add_parser('import',help='Import an existing SQLite DB via the exact image online-backup contract'); _common(imp); _import_args(imp)
|
|
59
|
-
recover=sub.add_parser('recover-import',help='Recover a verified failed import without overwriting or recopying the database'); _common(recover); _import_args(recover)
|
|
60
|
-
backup=sub.add_parser('backup',help='Create a verified SQLite backup with the exact image'); _common(backup)
|
|
61
|
-
migrate=sub.add_parser('migrate',help='Create required backup and run exact-image SQLite migration'); _common(migrate)
|
|
62
|
-
migrate.add_argument('--first-install',action='store_true'); migrate.add_argument('--run-as'); migrate.add_argument('--database-file')
|
|
63
|
-
check=sub.add_parser('rollback-check',help='Verify that an exact rollback image accepts the current SQLite state'); _common(check)
|
|
64
|
-
return parser
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
def main(argv: list[str]|None=None) -> int:
|
|
68
|
-
args=build_parser().parse_args(argv)
|
|
69
|
-
try:
|
|
70
|
-
if args.command=='inventory': _print(sqlite_inventory(args.name),as_json=args.json); return 0
|
|
71
|
-
request=normalize_sqlite_request(owner_repo=args.owner_repo,environment=args.environment,app_slug=args.app_slug,image=args.image,
|
|
72
|
-
maintenance_environment=_parse_env(args.maintenance_env),run_as=getattr(args,'run_as',None),database_file=getattr(args,'database_file',None),
|
|
73
|
-
source_database=getattr(args,'source_database',None),first_install=getattr(args,'first_install',False))
|
|
74
|
-
if args.command=='recover-import':
|
|
75
|
-
plan=plan_sqlite_import_recovery(args.name,request)
|
|
76
|
-
_print({k:v for k,v in plan.items() if k!='request'},as_json=False)
|
|
77
|
-
phrase=recovery_authorization_phrase(plan)
|
|
78
|
-
_confirm(args,phrase,f"recover SQLite import for {plan['appSlug']} ({plan['environment']}) on target {args.name}")
|
|
79
|
-
_print(apply_sqlite_import_recovery(plan),as_json=args.json); return 0
|
|
80
|
-
plan=plan_sqlite_operation(args.name,args.command,request)
|
|
81
|
-
_print({k:v for k,v in plan.items() if k!='spec'},as_json=False)
|
|
82
|
-
phrase=authorization_phrase(plan)
|
|
83
|
-
_confirm(args,phrase,f"run SQLite {args.command} for {plan['appSlug']} ({plan['environment']}) on target {args.name}")
|
|
84
|
-
_print(apply_sqlite_operation(plan),as_json=args.json); return 0
|
|
85
|
-
except DeploymentKitError as error:
|
|
1
|
+
"""CLI for the SQLite persistence adapter."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
import argparse, json, sys
|
|
4
|
+
from typing import Any
|
|
5
|
+
from .errors import DeploymentKitError
|
|
6
|
+
from .sqlite_adapter import authorization_phrase, apply_sqlite_operation, normalize_sqlite_request, plan_sqlite_operation, sqlite_inventory
|
|
7
|
+
from .sqlite_import_recovery import (
|
|
8
|
+
apply_sqlite_import_recovery,
|
|
9
|
+
authorization_phrase as recovery_authorization_phrase,
|
|
10
|
+
plan_sqlite_import_recovery,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _print(value: Any, *, as_json: bool) -> None:
|
|
15
|
+
if as_json:
|
|
16
|
+
print(json.dumps(value, indent=2, sort_keys=True)); return
|
|
17
|
+
if isinstance(value, dict):
|
|
18
|
+
for key, item in value.items():
|
|
19
|
+
print(f"{key}: {json.dumps(item, sort_keys=True) if isinstance(item,(dict,list)) else item}")
|
|
20
|
+
else: print(value)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _parse_env(items: list[str]) -> list[tuple[str,str]]:
|
|
24
|
+
out=[]
|
|
25
|
+
for item in items:
|
|
26
|
+
if '=' not in item: raise DeploymentKitError("--maintenance-env entries must use NAME=value form.")
|
|
27
|
+
out.append(tuple(item.split('=',1)))
|
|
28
|
+
return out
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _confirm(args: argparse.Namespace, phrase: str, action: str) -> None:
|
|
32
|
+
if args.yes: return
|
|
33
|
+
if input(f"Type {phrase} to {action}: ").strip() != phrase:
|
|
34
|
+
raise DeploymentKitError(f"{action.capitalize()} was not authorized.")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _common(parser: argparse.ArgumentParser) -> None:
|
|
38
|
+
parser.add_argument('name')
|
|
39
|
+
parser.add_argument('--owner-repo', required=True)
|
|
40
|
+
parser.add_argument('--environment', required=True)
|
|
41
|
+
parser.add_argument('--app-slug', required=True)
|
|
42
|
+
parser.add_argument('--image', required=True)
|
|
43
|
+
parser.add_argument('--maintenance-env', action='append', default=[], help='Non-secret image maintenance environment value in NAME=value form; repeat as needed')
|
|
44
|
+
parser.add_argument('--json', action='store_true')
|
|
45
|
+
parser.add_argument('--yes', action='store_true')
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _import_args(parser: argparse.ArgumentParser) -> None:
|
|
49
|
+
parser.add_argument('--run-as', required=True)
|
|
50
|
+
parser.add_argument('--database-file', required=True)
|
|
51
|
+
parser.add_argument('--source-database', required=True)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
55
|
+
parser=argparse.ArgumentParser(prog='lean-poc-deploy sqlite')
|
|
56
|
+
sub=parser.add_subparsers(dest='command',required=True)
|
|
57
|
+
inv=sub.add_parser('inventory',help='Read SQLite persistence reservations without writes'); inv.add_argument('name'); inv.add_argument('--json',action='store_true')
|
|
58
|
+
imp=sub.add_parser('import',help='Import an existing SQLite DB via the exact image online-backup contract'); _common(imp); _import_args(imp)
|
|
59
|
+
recover=sub.add_parser('recover-import',help='Recover a verified failed import without overwriting or recopying the database'); _common(recover); _import_args(recover)
|
|
60
|
+
backup=sub.add_parser('backup',help='Create a verified SQLite backup with the exact image'); _common(backup)
|
|
61
|
+
migrate=sub.add_parser('migrate',help='Create required backup and run exact-image SQLite migration'); _common(migrate)
|
|
62
|
+
migrate.add_argument('--first-install',action='store_true'); migrate.add_argument('--run-as'); migrate.add_argument('--database-file')
|
|
63
|
+
check=sub.add_parser('rollback-check',help='Verify that an exact rollback image accepts the current SQLite state'); _common(check)
|
|
64
|
+
return parser
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def main(argv: list[str]|None=None) -> int:
|
|
68
|
+
args=build_parser().parse_args(argv)
|
|
69
|
+
try:
|
|
70
|
+
if args.command=='inventory': _print(sqlite_inventory(args.name),as_json=args.json); return 0
|
|
71
|
+
request=normalize_sqlite_request(owner_repo=args.owner_repo,environment=args.environment,app_slug=args.app_slug,image=args.image,
|
|
72
|
+
maintenance_environment=_parse_env(args.maintenance_env),run_as=getattr(args,'run_as',None),database_file=getattr(args,'database_file',None),
|
|
73
|
+
source_database=getattr(args,'source_database',None),first_install=getattr(args,'first_install',False))
|
|
74
|
+
if args.command=='recover-import':
|
|
75
|
+
plan=plan_sqlite_import_recovery(args.name,request)
|
|
76
|
+
_print({k:v for k,v in plan.items() if k!='request'},as_json=False)
|
|
77
|
+
phrase=recovery_authorization_phrase(plan)
|
|
78
|
+
_confirm(args,phrase,f"recover SQLite import for {plan['appSlug']} ({plan['environment']}) on target {args.name}")
|
|
79
|
+
_print(apply_sqlite_import_recovery(plan),as_json=args.json); return 0
|
|
80
|
+
plan=plan_sqlite_operation(args.name,args.command,request)
|
|
81
|
+
_print({k:v for k,v in plan.items() if k!='spec'},as_json=False)
|
|
82
|
+
phrase=authorization_phrase(plan)
|
|
83
|
+
_confirm(args,phrase,f"run SQLite {args.command} for {plan['appSlug']} ({plan['environment']}) on target {args.name}")
|
|
84
|
+
_print(apply_sqlite_operation(plan),as_json=args.json); return 0
|
|
85
|
+
except DeploymentKitError as error:
|
|
86
86
|
print(f"ERROR: {error}",file=sys.stderr); return 2
|