semantics
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
# [DEF:ScriptsPackage:Package]
|
||||
# @PURPOSE: Script entrypoint package root.
|
||||
# [/DEF:ScriptsPackage:Package]
|
||||
# #region ScriptsPackage [TYPE Package]
|
||||
# @BRIEF Script entrypoint package root.
|
||||
# #endregion ScriptsPackage
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
# [DEF:CleanReleaseCliScript:Module]
|
||||
# @COMPLEXITY: 3
|
||||
# @SEMANTICS: cli, clean-release, candidate, artifacts, manifest
|
||||
# @PURPOSE: Provide headless CLI commands for candidate registration, artifact import and manifest build.
|
||||
# @LAYER: Scripts
|
||||
# @RELATION: CALLS -> ComplianceOrchestrator
|
||||
# #region CleanReleaseCliScript [C:3] [TYPE Module] [SEMANTICS cli, clean-release, candidate, artifacts, manifest]
|
||||
# @BRIEF Provide headless CLI commands for candidate registration, artifact import and manifest build.
|
||||
# @LAYER Scripts
|
||||
# @RELATION CALLS -> [ComplianceOrchestrator]
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -27,8 +25,8 @@ from ..services.clean_release.publication_service import (
|
||||
)
|
||||
|
||||
|
||||
# [DEF:build_parser:Function]
|
||||
# @PURPOSE: Build argparse parser for clean release CLI.
|
||||
# #region build_parser [TYPE Function]
|
||||
# @BRIEF Build argparse parser for clean release CLI.
|
||||
def build_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(prog="clean-release-cli")
|
||||
subparsers = parser.add_subparsers(dest="command", required=True)
|
||||
@@ -99,13 +97,13 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
return parser
|
||||
|
||||
|
||||
# [/DEF:build_parser:Function]
|
||||
# #endregion build_parser
|
||||
|
||||
|
||||
# [DEF:run_candidate_register:Function]
|
||||
# @PURPOSE: Register candidate in repository via CLI command.
|
||||
# @PRE: Candidate ID must be unique.
|
||||
# @POST: Candidate is persisted in DRAFT status.
|
||||
# #region run_candidate_register [TYPE Function]
|
||||
# @BRIEF Register candidate in repository via CLI command.
|
||||
# @PRE Candidate ID must be unique.
|
||||
# @POST Candidate is persisted in DRAFT status.
|
||||
def run_candidate_register(args: argparse.Namespace) -> int:
|
||||
from ..dependencies import get_clean_release_repository
|
||||
|
||||
@@ -128,13 +126,13 @@ def run_candidate_register(args: argparse.Namespace) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# [/DEF:run_candidate_register:Function]
|
||||
# #endregion run_candidate_register
|
||||
|
||||
|
||||
# [DEF:run_artifact_import:Function]
|
||||
# @PURPOSE: Import single artifact for existing candidate.
|
||||
# @PRE: Candidate must exist.
|
||||
# @POST: Artifact is persisted for candidate.
|
||||
# #region run_artifact_import [TYPE Function]
|
||||
# @BRIEF Import single artifact for existing candidate.
|
||||
# @PRE Candidate must exist.
|
||||
# @POST Artifact is persisted for candidate.
|
||||
def run_artifact_import(args: argparse.Namespace) -> int:
|
||||
from ..dependencies import get_clean_release_repository
|
||||
|
||||
@@ -161,13 +159,13 @@ def run_artifact_import(args: argparse.Namespace) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# [/DEF:run_artifact_import:Function]
|
||||
# #endregion run_artifact_import
|
||||
|
||||
|
||||
# [DEF:run_manifest_build:Function]
|
||||
# @PURPOSE: Build immutable manifest snapshot for candidate.
|
||||
# @PRE: Candidate must exist.
|
||||
# @POST: New manifest version is persisted.
|
||||
# #region run_manifest_build [TYPE Function]
|
||||
# @BRIEF Build immutable manifest snapshot for candidate.
|
||||
# @PRE Candidate must exist.
|
||||
# @POST New manifest version is persisted.
|
||||
def run_manifest_build(args: argparse.Namespace) -> int:
|
||||
from ..dependencies import get_clean_release_repository
|
||||
from ..services.clean_release.manifest_service import build_manifest_snapshot
|
||||
@@ -195,13 +193,13 @@ def run_manifest_build(args: argparse.Namespace) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# [/DEF:run_manifest_build:Function]
|
||||
# #endregion run_manifest_build
|
||||
|
||||
|
||||
# [DEF:run_compliance_run:Function]
|
||||
# @PURPOSE: Execute compliance run for candidate with optional manifest fallback.
|
||||
# @PRE: Candidate exists and trusted snapshots are configured.
|
||||
# @POST: Returns run payload and exit code 0 on success.
|
||||
# #region run_compliance_run [TYPE Function]
|
||||
# @BRIEF Execute compliance run for candidate with optional manifest fallback.
|
||||
# @PRE Candidate exists and trusted snapshots are configured.
|
||||
# @POST Returns run payload and exit code 0 on success.
|
||||
def run_compliance_run(args: argparse.Namespace) -> int:
|
||||
from ..dependencies import get_clean_release_repository, get_config_manager
|
||||
|
||||
@@ -234,13 +232,13 @@ def run_compliance_run(args: argparse.Namespace) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# [/DEF:run_compliance_run:Function]
|
||||
# #endregion run_compliance_run
|
||||
|
||||
|
||||
# [DEF:run_compliance_status:Function]
|
||||
# @PURPOSE: Read run status by run id.
|
||||
# @PRE: Run exists.
|
||||
# @POST: Returns run status payload.
|
||||
# #region run_compliance_status [TYPE Function]
|
||||
# @BRIEF Read run status by run id.
|
||||
# @PRE Run exists.
|
||||
# @POST Returns run status payload.
|
||||
def run_compliance_status(args: argparse.Namespace) -> int:
|
||||
from ..dependencies import get_clean_release_repository
|
||||
|
||||
@@ -266,13 +264,13 @@ def run_compliance_status(args: argparse.Namespace) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# [/DEF:run_compliance_status:Function]
|
||||
# #endregion run_compliance_status
|
||||
|
||||
|
||||
# [DEF:_to_payload:Function]
|
||||
# @PURPOSE: Serialize domain models for CLI JSON output across SQLAlchemy/Pydantic variants.
|
||||
# @PRE: value is serializable model or primitive object.
|
||||
# @POST: Returns dictionary payload without mutating value.
|
||||
# #region _to_payload [TYPE Function]
|
||||
# @BRIEF Serialize domain models for CLI JSON output across SQLAlchemy/Pydantic variants.
|
||||
# @PRE value is serializable model or primitive object.
|
||||
# @POST Returns dictionary payload without mutating value.
|
||||
def _to_payload(value: Any) -> Dict[str, Any]:
|
||||
def _normalize(raw: Any) -> Any:
|
||||
if isinstance(raw, datetime):
|
||||
@@ -296,13 +294,13 @@ def _to_payload(value: Any) -> Dict[str, Any]:
|
||||
raise TypeError(f"unsupported payload type: {type(value)!r}")
|
||||
|
||||
|
||||
# [/DEF:_to_payload:Function]
|
||||
# #endregion _to_payload
|
||||
|
||||
|
||||
# [DEF:run_compliance_report:Function]
|
||||
# @PURPOSE: Read immutable report by run id.
|
||||
# @PRE: Run and report exist.
|
||||
# @POST: Returns report payload.
|
||||
# #region run_compliance_report [TYPE Function]
|
||||
# @BRIEF Read immutable report by run id.
|
||||
# @PRE Run and report exist.
|
||||
# @POST Returns report payload.
|
||||
def run_compliance_report(args: argparse.Namespace) -> int:
|
||||
from ..dependencies import get_clean_release_repository
|
||||
|
||||
@@ -323,13 +321,13 @@ def run_compliance_report(args: argparse.Namespace) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# [/DEF:run_compliance_report:Function]
|
||||
# #endregion run_compliance_report
|
||||
|
||||
|
||||
# [DEF:run_compliance_violations:Function]
|
||||
# @PURPOSE: Read run violations by run id.
|
||||
# @PRE: Run exists.
|
||||
# @POST: Returns violations payload.
|
||||
# #region run_compliance_violations [TYPE Function]
|
||||
# @BRIEF Read run violations by run id.
|
||||
# @PRE Run exists.
|
||||
# @POST Returns violations payload.
|
||||
def run_compliance_violations(args: argparse.Namespace) -> int:
|
||||
from ..dependencies import get_clean_release_repository
|
||||
|
||||
@@ -348,13 +346,13 @@ def run_compliance_violations(args: argparse.Namespace) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# [/DEF:run_compliance_violations:Function]
|
||||
# #endregion run_compliance_violations
|
||||
|
||||
|
||||
# [DEF:run_approve:Function]
|
||||
# @PURPOSE: Approve candidate based on immutable PASSED report.
|
||||
# @PRE: Candidate and report exist; report is PASSED.
|
||||
# @POST: Persists APPROVED decision and returns success payload.
|
||||
# #region run_approve [TYPE Function]
|
||||
# @BRIEF Approve candidate based on immutable PASSED report.
|
||||
# @PRE Candidate and report exist; report is PASSED.
|
||||
# @POST Persists APPROVED decision and returns success payload.
|
||||
def run_approve(args: argparse.Namespace) -> int:
|
||||
from ..dependencies import get_clean_release_repository
|
||||
|
||||
@@ -379,13 +377,13 @@ def run_approve(args: argparse.Namespace) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# [/DEF:run_approve:Function]
|
||||
# #endregion run_approve
|
||||
|
||||
|
||||
# [DEF:run_reject:Function]
|
||||
# @PURPOSE: Reject candidate without mutating compliance evidence.
|
||||
# @PRE: Candidate and report exist.
|
||||
# @POST: Persists REJECTED decision and returns success payload.
|
||||
# #region run_reject [TYPE Function]
|
||||
# @BRIEF Reject candidate without mutating compliance evidence.
|
||||
# @PRE Candidate and report exist.
|
||||
# @POST Persists REJECTED decision and returns success payload.
|
||||
def run_reject(args: argparse.Namespace) -> int:
|
||||
from ..dependencies import get_clean_release_repository
|
||||
|
||||
@@ -410,13 +408,13 @@ def run_reject(args: argparse.Namespace) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# [/DEF:run_reject:Function]
|
||||
# #endregion run_reject
|
||||
|
||||
|
||||
# [DEF:run_publish:Function]
|
||||
# @PURPOSE: Publish approved candidate to target channel.
|
||||
# @PRE: Candidate is approved and report belongs to candidate.
|
||||
# @POST: Appends ACTIVE publication record and returns payload.
|
||||
# #region run_publish [TYPE Function]
|
||||
# @BRIEF Publish approved candidate to target channel.
|
||||
# @PRE Candidate is approved and report belongs to candidate.
|
||||
# @POST Appends ACTIVE publication record and returns payload.
|
||||
def run_publish(args: argparse.Namespace) -> int:
|
||||
from ..dependencies import get_clean_release_repository
|
||||
|
||||
@@ -438,13 +436,13 @@ def run_publish(args: argparse.Namespace) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# [/DEF:run_publish:Function]
|
||||
# #endregion run_publish
|
||||
|
||||
|
||||
# [DEF:run_revoke:Function]
|
||||
# @PURPOSE: Revoke active publication record.
|
||||
# @PRE: Publication id exists and is ACTIVE.
|
||||
# @POST: Publication record status becomes REVOKED.
|
||||
# #region run_revoke [TYPE Function]
|
||||
# @BRIEF Revoke active publication record.
|
||||
# @PRE Publication id exists and is ACTIVE.
|
||||
# @POST Publication record status becomes REVOKED.
|
||||
def run_revoke(args: argparse.Namespace) -> int:
|
||||
from ..dependencies import get_clean_release_repository
|
||||
|
||||
@@ -464,11 +462,11 @@ def run_revoke(args: argparse.Namespace) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# [/DEF:run_revoke:Function]
|
||||
# #endregion run_revoke
|
||||
|
||||
|
||||
# [DEF:main:Function]
|
||||
# @PURPOSE: CLI entrypoint for clean release commands.
|
||||
# #region main [TYPE Function]
|
||||
# @BRIEF CLI entrypoint for clean release commands.
|
||||
def main(argv: Optional[List[str]] = None) -> int:
|
||||
parser = build_parser()
|
||||
args = parser.parse_args(argv)
|
||||
@@ -500,10 +498,10 @@ def main(argv: Optional[List[str]] = None) -> int:
|
||||
return 2
|
||||
|
||||
|
||||
# [/DEF:main:Function]
|
||||
# #endregion main
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
|
||||
# [/DEF:CleanReleaseCliScript:Module]
|
||||
# #endregion CleanReleaseCliScript
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
# [DEF:CleanReleaseTuiScript:Module]
|
||||
# @COMPLEXITY: 3
|
||||
# @SEMANTICS: clean-release, tui, ncurses, interactive-validator
|
||||
# @PURPOSE: Interactive terminal interface for Enterprise Clean Release compliance validation.
|
||||
# @LAYER: UI
|
||||
# @RELATION: DEPENDS_ON -> [ComplianceExecutionService]
|
||||
# @RELATION: DEPENDS_ON -> [CleanReleaseRepository]
|
||||
# @INVARIANT: TUI refuses startup in non-TTY environments; headless flow is CLI/API only.
|
||||
# #region CleanReleaseTuiScript [C:3] [TYPE Module] [SEMANTICS clean-release, tui, ncurses, interactive-validator]
|
||||
# @BRIEF Interactive terminal interface for Enterprise Clean Release compliance validation.
|
||||
# @LAYER UI
|
||||
# @INVARIANT TUI refuses startup in non-TTY environments; headless flow is CLI/API only.
|
||||
# @RELATION DEPENDS_ON -> [ComplianceExecutionService]
|
||||
# @RELATION DEPENDS_ON -> [CleanReleaseRepository]
|
||||
|
||||
import curses
|
||||
import json
|
||||
@@ -46,10 +44,10 @@ from src.services.clean_release.publication_service import publish_candidate
|
||||
from src.services.clean_release.repository import CleanReleaseRepository
|
||||
|
||||
|
||||
# [DEF:TuiFacadeAdapter:Class]
|
||||
# @PURPOSE: Thin TUI adapter that routes business mutations through application services.
|
||||
# @PRE: repository contains candidate and trusted policy/registry snapshots for execution.
|
||||
# @POST: Business actions return service results/errors without direct TUI-owned mutations.
|
||||
# #region TuiFacadeAdapter [TYPE Class]
|
||||
# @BRIEF Thin TUI adapter that routes business mutations through application services.
|
||||
# @PRE repository contains candidate and trusted policy/registry snapshots for execution.
|
||||
# @POST Business actions return service results/errors without direct TUI-owned mutations.
|
||||
class TuiFacadeAdapter:
|
||||
def __init__(self, repository: CleanReleaseRepository):
|
||||
self.repository = repository
|
||||
@@ -189,11 +187,11 @@ class TuiFacadeAdapter:
|
||||
}
|
||||
|
||||
|
||||
# [/DEF:TuiFacadeAdapter:Class]
|
||||
# #endregion TuiFacadeAdapter
|
||||
|
||||
|
||||
# [DEF:CleanReleaseTUI:Class]
|
||||
# @PURPOSE: Curses-based application for compliance monitoring.
|
||||
# #region CleanReleaseTUI [TYPE Class]
|
||||
# @BRIEF Curses-based application for compliance monitoring.
|
||||
# @UX_STATE: READY -> Waiting for operator to start checks (F5).
|
||||
# @UX_STATE: RUNNING -> Executing compliance stages with progress feedback.
|
||||
# @UX_STATE: COMPLIANT -> Release candidate passed all checks.
|
||||
@@ -508,10 +506,10 @@ class CleanReleaseTUI:
|
||||
self.stdscr.addstr(max_y - 1, 0, footer_text[:max_x])
|
||||
self.stdscr.attroff(curses.color_pair(1))
|
||||
|
||||
# [DEF:run_checks:Function]
|
||||
# @PURPOSE: Execute compliance run via facade adapter and update UI state.
|
||||
# @PRE: Candidate and policy snapshots are present in repository.
|
||||
# @POST: UI reflects final run/report/violation state from service result.
|
||||
# #region run_checks [TYPE Function]
|
||||
# @BRIEF Execute compliance run via facade adapter and update UI state.
|
||||
# @PRE Candidate and policy snapshots are present in repository.
|
||||
# @POST UI reflects final run/report/violation state from service result.
|
||||
def run_checks(self):
|
||||
self.status = "RUNNING"
|
||||
self.report_id = None
|
||||
@@ -552,7 +550,7 @@ class CleanReleaseTUI:
|
||||
self.refresh_overview()
|
||||
self.refresh_screen()
|
||||
|
||||
# [/DEF:run_checks:Function]
|
||||
# #endregion run_checks
|
||||
|
||||
def build_manifest(self):
|
||||
try:
|
||||
@@ -654,7 +652,7 @@ class CleanReleaseTUI:
|
||||
self.publish_latest()
|
||||
|
||||
|
||||
# [/DEF:CleanReleaseTUI:Class]
|
||||
# #endregion CleanReleaseTUI
|
||||
|
||||
|
||||
def tui_main(stdscr: curses.window):
|
||||
@@ -681,4 +679,4 @@ def main() -> int:
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
# [/DEF:CleanReleaseTuiScript:Module]
|
||||
# #endregion CleanReleaseTuiScript
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
# [DEF:CreateAdminScript:Module]
|
||||
# #region CreateAdminScript [C:3] [TYPE Module] [SEMANTICS admin, setup, user, auth, cli]
|
||||
# @BRIEF CLI tool for creating the initial admin user.
|
||||
# @LAYER Scripts
|
||||
# @INVARIANT Admin user must have the "Admin" role.
|
||||
# @RELATION USES -> [AuthSecurityModule]
|
||||
# @RELATION USES -> [DatabaseModule]
|
||||
# @RELATION USES -> [AuthModels]
|
||||
#
|
||||
# @COMPLEXITY: 3
|
||||
# @SEMANTICS: admin, setup, user, auth, cli
|
||||
# @PURPOSE: CLI tool for creating the initial admin user.
|
||||
# @LAYER: Scripts
|
||||
# @RELATION: USES -> [AuthSecurityModule]
|
||||
# @RELATION: USES -> [DatabaseModule]
|
||||
# @RELATION: USES -> [AuthModels]
|
||||
#
|
||||
# @INVARIANT: Admin user must have the "Admin" role.
|
||||
|
||||
# [SECTION: IMPORTS]
|
||||
import sys
|
||||
@@ -25,10 +23,10 @@ from src.core.logger import logger, belief_scope
|
||||
# [/SECTION]
|
||||
|
||||
|
||||
# [DEF:create_admin:Function]
|
||||
# @PURPOSE: Creates an admin user and necessary roles/permissions.
|
||||
# @PRE: username and password provided via CLI.
|
||||
# @POST: Admin user exists in auth.db.
|
||||
# #region create_admin [TYPE Function]
|
||||
# @BRIEF Creates an admin user and necessary roles/permissions.
|
||||
# @PRE username and password provided via CLI.
|
||||
# @POST Admin user exists in auth.db.
|
||||
#
|
||||
# @PARAM: username (str) - Admin username.
|
||||
# @PARAM: password (str) - Admin password.
|
||||
@@ -79,7 +77,7 @@ def create_admin(username, password, email=None):
|
||||
db.close()
|
||||
|
||||
|
||||
# [/DEF:create_admin:Function]
|
||||
# #endregion create_admin
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Create initial admin user")
|
||||
@@ -96,4 +94,4 @@ if __name__ == "__main__":
|
||||
except Exception:
|
||||
sys.exit(1)
|
||||
|
||||
# [/DEF:CreateAdminScript:Module]
|
||||
# #endregion CreateAdminScript
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
# [DEF:InitAuthDbScript:Module]
|
||||
# #region InitAuthDbScript [C:2] [TYPE Module] [SEMANTICS setup, database, auth, migration]
|
||||
# @BRIEF Initializes the auth database and creates the necessary tables.
|
||||
# @LAYER Scripts
|
||||
# @INVARIANT Safe to run multiple times (idempotent).
|
||||
# @RELATION CALLS -> [init_db]
|
||||
# @RELATION CALLS -> [ensure_encryption_key]
|
||||
# @RELATION CALLS -> [seed_permissions]
|
||||
#
|
||||
# @SEMANTICS: setup, database, auth, migration
|
||||
# @PURPOSE: Initializes the auth database and creates the necessary tables.
|
||||
# @COMPLEXITY: 2
|
||||
# @LAYER: Scripts
|
||||
# @RELATION: CALLS -> init_db
|
||||
# @RELATION: CALLS -> ensure_encryption_key
|
||||
# @RELATION: CALLS -> seed_permissions
|
||||
#
|
||||
# @INVARIANT: Safe to run multiple times (idempotent).
|
||||
|
||||
# [SECTION: IMPORTS]
|
||||
import sys
|
||||
@@ -24,13 +22,12 @@ from src.scripts.seed_permissions import seed_permissions
|
||||
# [/SECTION]
|
||||
|
||||
|
||||
# [DEF:run_init:Function]
|
||||
# @PURPOSE: Main entry point for the initialization script.
|
||||
# @COMPLEXITY: 3
|
||||
# @POST: auth.db is initialized with the correct schema and seeded permissions.
|
||||
# @RELATION: CALLS -> ensure_encryption_key
|
||||
# @RELATION: CALLS -> init_db
|
||||
# @RELATION: CALLS -> seed_permissions
|
||||
# #region run_init [C:3] [TYPE Function]
|
||||
# @BRIEF Main entry point for the initialization script.
|
||||
# @POST auth.db is initialized with the correct schema and seeded permissions.
|
||||
# @RELATION CALLS -> [ensure_encryption_key]
|
||||
# @RELATION CALLS -> [init_db]
|
||||
# @RELATION CALLS -> [seed_permissions]
|
||||
def run_init():
|
||||
with belief_scope("init_auth_db"):
|
||||
logger.info("Initializing authentication database...")
|
||||
@@ -47,9 +44,9 @@ def run_init():
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# [/DEF:run_init:Function]
|
||||
# #endregion run_init
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_init()
|
||||
|
||||
# [/DEF:InitAuthDbScript:Module]
|
||||
# #endregion InitAuthDbScript
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
# [DEF:MigrateSqliteToPostgresScript:Module]
|
||||
# #region MigrateSqliteToPostgresScript [C:3] [TYPE Module] [SEMANTICS migration, sqlite, postgresql, config, task_logs, task_records]
|
||||
# @BRIEF Migrates legacy config and task history from SQLite/file storage to PostgreSQL.
|
||||
# @LAYER Scripts
|
||||
# @INVARIANT Script is idempotent for task_records and app_configurations.
|
||||
# @RELATION READS_FROM -> [backend/tasks.db]
|
||||
# @RELATION READS_FROM -> [backend/config.json]
|
||||
# @RELATION WRITES_TO -> [postgresql.task_records]
|
||||
# @RELATION WRITES_TO -> [postgresql.task_logs]
|
||||
# @RELATION WRITES_TO -> [postgresql.app_configurations]
|
||||
#
|
||||
# @COMPLEXITY: 3
|
||||
# @SEMANTICS: migration, sqlite, postgresql, config, task_logs, task_records
|
||||
# @PURPOSE: Migrates legacy config and task history from SQLite/file storage to PostgreSQL.
|
||||
# @LAYER: Scripts
|
||||
# @RELATION: READS_FROM -> backend/tasks.db
|
||||
# @RELATION: READS_FROM -> backend/config.json
|
||||
# @RELATION: WRITES_TO -> postgresql.task_records
|
||||
# @RELATION: WRITES_TO -> postgresql.task_logs
|
||||
# @RELATION: WRITES_TO -> postgresql.app_configurations
|
||||
#
|
||||
# @INVARIANT: Script is idempotent for task_records and app_configurations.
|
||||
|
||||
# [SECTION: IMPORTS]
|
||||
import argparse
|
||||
@@ -30,7 +28,7 @@ log = MarkerLogger("MigrateSqliteToPostgres")
|
||||
# [/SECTION]
|
||||
|
||||
|
||||
# [DEF:Constants:Section]
|
||||
# #region Constants [TYPE Section]
|
||||
DEFAULT_TARGET_URL = os.getenv(
|
||||
"DATABASE_URL",
|
||||
os.getenv(
|
||||
@@ -38,14 +36,13 @@ DEFAULT_TARGET_URL = os.getenv(
|
||||
"postgresql+psycopg2://postgres:postgres@localhost:5432/ss_tools",
|
||||
),
|
||||
)
|
||||
# [/DEF:Constants:Section]
|
||||
# #endregion Constants
|
||||
|
||||
|
||||
# [DEF:_json_load_if_needed:Function]
|
||||
# @COMPLEXITY: 3
|
||||
# @PURPOSE: Parses JSON-like values from SQLite TEXT/JSON columns to Python objects.
|
||||
# @PRE: value is scalar JSON/text/list/dict or None.
|
||||
# @POST: Returns normalized Python object or original scalar value.
|
||||
# #region _json_load_if_needed [C:3] [TYPE Function]
|
||||
# @BRIEF Parses JSON-like values from SQLite TEXT/JSON columns to Python objects.
|
||||
# @PRE value is scalar JSON/text/list/dict or None.
|
||||
# @POST Returns normalized Python object or original scalar value.
|
||||
def _json_load_if_needed(value: Any) -> Any:
|
||||
with belief_scope("_json_load_if_needed"):
|
||||
if value is None:
|
||||
@@ -64,11 +61,11 @@ def _json_load_if_needed(value: Any) -> Any:
|
||||
return value
|
||||
|
||||
|
||||
# [/DEF:_json_load_if_needed:Function]
|
||||
# #endregion _json_load_if_needed
|
||||
|
||||
|
||||
# [DEF:_find_legacy_config_path:Function]
|
||||
# @PURPOSE: Resolves the existing legacy config.json path from candidates.
|
||||
# #region _find_legacy_config_path [TYPE Function]
|
||||
# @BRIEF Resolves the existing legacy config.json path from candidates.
|
||||
def _find_legacy_config_path(explicit_path: Optional[str]) -> Optional[Path]:
|
||||
with belief_scope("_find_legacy_config_path"):
|
||||
if explicit_path:
|
||||
@@ -85,11 +82,11 @@ def _find_legacy_config_path(explicit_path: Optional[str]) -> Optional[Path]:
|
||||
return None
|
||||
|
||||
|
||||
# [/DEF:_find_legacy_config_path:Function]
|
||||
# #endregion _find_legacy_config_path
|
||||
|
||||
|
||||
# [DEF:_connect_sqlite:Function]
|
||||
# @PURPOSE: Opens a SQLite connection with row factory.
|
||||
# #region _connect_sqlite [TYPE Function]
|
||||
# @BRIEF Opens a SQLite connection with row factory.
|
||||
def _connect_sqlite(path: Path) -> sqlite3.Connection:
|
||||
with belief_scope("_connect_sqlite"):
|
||||
conn = sqlite3.connect(str(path))
|
||||
@@ -97,11 +94,11 @@ def _connect_sqlite(path: Path) -> sqlite3.Connection:
|
||||
return conn
|
||||
|
||||
|
||||
# [/DEF:_connect_sqlite:Function]
|
||||
# #endregion _connect_sqlite
|
||||
|
||||
|
||||
# [DEF:_ensure_target_schema:Function]
|
||||
# @PURPOSE: Ensures required PostgreSQL tables exist before migration.
|
||||
# #region _ensure_target_schema [TYPE Function]
|
||||
# @BRIEF Ensures required PostgreSQL tables exist before migration.
|
||||
def _ensure_target_schema(engine) -> None:
|
||||
with belief_scope("_ensure_target_schema"):
|
||||
stmts: Iterable[str] = (
|
||||
@@ -164,11 +161,11 @@ def _ensure_target_schema(engine) -> None:
|
||||
conn.execute(text(stmt))
|
||||
|
||||
|
||||
# [/DEF:_ensure_target_schema:Function]
|
||||
# #endregion _ensure_target_schema
|
||||
|
||||
|
||||
# [DEF:_migrate_config:Function]
|
||||
# @PURPOSE: Migrates legacy config.json into app_configurations(global).
|
||||
# #region _migrate_config [TYPE Function]
|
||||
# @BRIEF Migrates legacy config.json into app_configurations(global).
|
||||
def _migrate_config(engine, legacy_config_path: Optional[Path]) -> int:
|
||||
with belief_scope("_migrate_config"):
|
||||
if legacy_config_path is None:
|
||||
@@ -195,11 +192,11 @@ def _migrate_config(engine, legacy_config_path: Optional[Path]) -> int:
|
||||
return 1
|
||||
|
||||
|
||||
# [/DEF:_migrate_config:Function]
|
||||
# #endregion _migrate_config
|
||||
|
||||
|
||||
# [DEF:_migrate_tasks_and_logs:Function]
|
||||
# @PURPOSE: Migrates task_records and task_logs from SQLite into PostgreSQL.
|
||||
# #region _migrate_tasks_and_logs [TYPE Function]
|
||||
# @BRIEF Migrates task_records and task_logs from SQLite into PostgreSQL.
|
||||
def _migrate_tasks_and_logs(engine, sqlite_conn: sqlite3.Connection) -> Dict[str, int]:
|
||||
with belief_scope("_migrate_tasks_and_logs"):
|
||||
stats = {
|
||||
@@ -324,11 +321,11 @@ def _migrate_tasks_and_logs(engine, sqlite_conn: sqlite3.Connection) -> Dict[str
|
||||
return stats
|
||||
|
||||
|
||||
# [/DEF:_migrate_tasks_and_logs:Function]
|
||||
# #endregion _migrate_tasks_and_logs
|
||||
|
||||
|
||||
# [DEF:run_migration:Function]
|
||||
# @PURPOSE: Orchestrates migration from SQLite/file to PostgreSQL.
|
||||
# #region run_migration [TYPE Function]
|
||||
# @BRIEF Orchestrates migration from SQLite/file to PostgreSQL.
|
||||
def run_migration(
|
||||
sqlite_path: Path, target_url: str, legacy_config_path: Optional[Path]
|
||||
) -> Dict[str, int]:
|
||||
@@ -349,11 +346,11 @@ def run_migration(
|
||||
sqlite_conn.close()
|
||||
|
||||
|
||||
# [/DEF:run_migration:Function]
|
||||
# #endregion run_migration
|
||||
|
||||
|
||||
# [DEF:main:Function]
|
||||
# @PURPOSE: CLI entrypoint.
|
||||
# #region main [TYPE Function]
|
||||
# @BRIEF CLI entrypoint.
|
||||
def main() -> int:
|
||||
with belief_scope("main"):
|
||||
parser = argparse.ArgumentParser(
|
||||
@@ -396,6 +393,6 @@ def main() -> int:
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
# [/DEF:main:Function]
|
||||
# #endregion main
|
||||
|
||||
# [/DEF:MigrateSqliteToPostgresScript:Module]
|
||||
# #endregion MigrateSqliteToPostgresScript
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
# [DEF:SeedPermissionsScript:Module]
|
||||
# #region SeedPermissionsScript [C:3] [TYPE Module] [SEMANTICS setup, database, auth, permissions, seeding]
|
||||
# @BRIEF Populates the auth database with initial system permissions.
|
||||
# @LAYER Scripts
|
||||
# @INVARIANT Safe to run multiple times (idempotent).
|
||||
# @RELATION DEPENDS_ON -> [AuthSessionLocal]
|
||||
# @RELATION DEPENDS_ON -> [Permission]
|
||||
# @RELATION DEPENDS_ON -> [Role]
|
||||
# @RELATION DEPENDS_ON -> [AuthRepository]
|
||||
#
|
||||
# @SEMANTICS: setup, database, auth, permissions, seeding
|
||||
# @PURPOSE: Populates the auth database with initial system permissions.
|
||||
# @COMPLEXITY: 3
|
||||
# @LAYER: Scripts
|
||||
# @RELATION: DEPENDS_ON -> AuthSessionLocal
|
||||
# @RELATION: DEPENDS_ON -> Permission
|
||||
# @RELATION: DEPENDS_ON -> Role
|
||||
# @RELATION: DEPENDS_ON -> AuthRepository
|
||||
#
|
||||
# @INVARIANT: Safe to run multiple times (idempotent).
|
||||
|
||||
# [SECTION: IMPORTS]
|
||||
import sys
|
||||
@@ -24,10 +22,9 @@ from src.core.auth.repository import AuthRepository
|
||||
from src.core.logger import logger, belief_scope
|
||||
# [/SECTION]
|
||||
|
||||
# [DEF:INITIAL_PERMISSIONS:Constant]
|
||||
# @PURPOSE: Canonical bootstrap permission tuples seeded into auth storage.
|
||||
# @COMPLEXITY: 3
|
||||
# @RELATION: DEPENDS_ON -> SeedPermissionsScript
|
||||
# #region INITIAL_PERMISSIONS [C:3] [TYPE Constant]
|
||||
# @BRIEF Canonical bootstrap permission tuples seeded into auth storage.
|
||||
# @RELATION DEPENDS_ON -> [SeedPermissionsScript]
|
||||
INITIAL_PERMISSIONS = [
|
||||
# Admin Permissions
|
||||
{"resource": "admin:users", "action": "READ"},
|
||||
@@ -59,18 +56,17 @@ INITIAL_PERMISSIONS = [
|
||||
{"resource": "dataset:execution", "action": "LAUNCH"},
|
||||
{"resource": "dataset:execution", "action": "LAUNCH_PROD"},
|
||||
]
|
||||
# [/DEF:INITIAL_PERMISSIONS:Constant]
|
||||
# #endregion INITIAL_PERMISSIONS
|
||||
|
||||
|
||||
# [DEF:seed_permissions:Function]
|
||||
# @PURPOSE: Inserts missing permissions into the database.
|
||||
# @COMPLEXITY: 3
|
||||
# @POST: All INITIAL_PERMISSIONS exist in the DB.
|
||||
# @RELATION: DEPENDS_ON -> AuthSessionLocal
|
||||
# @RELATION: DEPENDS_ON -> Permission
|
||||
# @RELATION: DEPENDS_ON -> Role
|
||||
# @RELATION: DEPENDS_ON -> AuthRepository
|
||||
# @RELATION: DEPENDS_ON -> INITIAL_PERMISSIONS
|
||||
# #region seed_permissions [C:3] [TYPE Function]
|
||||
# @BRIEF Inserts missing permissions into the database.
|
||||
# @POST All INITIAL_PERMISSIONS exist in the DB.
|
||||
# @RELATION DEPENDS_ON -> [AuthSessionLocal]
|
||||
# @RELATION DEPENDS_ON -> [Permission]
|
||||
# @RELATION DEPENDS_ON -> [Role]
|
||||
# @RELATION DEPENDS_ON -> [AuthRepository]
|
||||
# @RELATION DEPENDS_ON -> [INITIAL_PERMISSIONS]
|
||||
def seed_permissions():
|
||||
with belief_scope("seed_permissions"):
|
||||
db = AuthSessionLocal()
|
||||
@@ -140,9 +136,9 @@ def seed_permissions():
|
||||
db.close()
|
||||
|
||||
|
||||
# [/DEF:seed_permissions:Function]
|
||||
# #endregion seed_permissions
|
||||
|
||||
if __name__ == "__main__":
|
||||
seed_permissions()
|
||||
|
||||
# [/DEF:SeedPermissionsScript:Module]
|
||||
# #endregion SeedPermissionsScript
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
# [DEF:SeedSupersetLoadTestScript:Module]
|
||||
# #region SeedSupersetLoadTestScript [C:3] [TYPE Module] [SEMANTICS superset, load-test, charts, dashboards, seed, stress]
|
||||
# @BRIEF Creates randomized load-test data in Superset by cloning chart configurations and creating dashboards in target environments.
|
||||
# @LAYER Scripts
|
||||
# @INVARIANT Created chart and dashboard names are globally unique for one script run.
|
||||
# @RELATION USES -> [ConfigManager]
|
||||
# @RELATION USES -> [SupersetClient]
|
||||
#
|
||||
# @COMPLEXITY: 3
|
||||
# @SEMANTICS: superset, load-test, charts, dashboards, seed, stress
|
||||
# @PURPOSE: Creates randomized load-test data in Superset by cloning chart configurations and creating dashboards in target environments.
|
||||
# @LAYER: Scripts
|
||||
# @RELATION: USES -> [ConfigManager]
|
||||
# @RELATION: USES -> [SupersetClient]
|
||||
# @INVARIANT: Created chart and dashboard names are globally unique for one script run.
|
||||
|
||||
# [SECTION: IMPORTS]
|
||||
import argparse
|
||||
@@ -29,10 +27,10 @@ log = MarkerLogger("SeedSupersetLoadTest")
|
||||
# [/SECTION]
|
||||
|
||||
|
||||
# [DEF:_parse_args:Function]
|
||||
# @PURPOSE: Parses CLI arguments for load-test data generation.
|
||||
# @PRE: Script is called from CLI.
|
||||
# @POST: Returns validated argument namespace.
|
||||
# #region _parse_args [TYPE Function]
|
||||
# @BRIEF Parses CLI arguments for load-test data generation.
|
||||
# @PRE Script is called from CLI.
|
||||
# @POST Returns validated argument namespace.
|
||||
def _parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Seed Superset with load-test charts and dashboards"
|
||||
@@ -70,13 +68,13 @@ def _parse_args() -> argparse.Namespace:
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
# [/DEF:_parse_args:Function]
|
||||
# #endregion _parse_args
|
||||
|
||||
|
||||
# [DEF:_extract_result_payload:Function]
|
||||
# @PURPOSE: Normalizes Superset API payloads that may be wrapped in `result`.
|
||||
# @PRE: payload is a JSON-decoded API response.
|
||||
# @POST: Returns the unwrapped object when present.
|
||||
# #region _extract_result_payload [TYPE Function]
|
||||
# @BRIEF Normalizes Superset API payloads that may be wrapped in `result`.
|
||||
# @PRE payload is a JSON-decoded API response.
|
||||
# @POST Returns the unwrapped object when present.
|
||||
def _extract_result_payload(payload: Dict) -> Dict:
|
||||
result = payload.get("result")
|
||||
if isinstance(result, dict):
|
||||
@@ -84,13 +82,13 @@ def _extract_result_payload(payload: Dict) -> Dict:
|
||||
return payload
|
||||
|
||||
|
||||
# [/DEF:_extract_result_payload:Function]
|
||||
# #endregion _extract_result_payload
|
||||
|
||||
|
||||
# [DEF:_extract_created_id:Function]
|
||||
# @PURPOSE: Extracts object ID from create/update API response.
|
||||
# @PRE: payload is a JSON-decoded API response.
|
||||
# @POST: Returns integer object ID or None if missing.
|
||||
# #region _extract_created_id [TYPE Function]
|
||||
# @BRIEF Extracts object ID from create/update API response.
|
||||
# @PRE payload is a JSON-decoded API response.
|
||||
# @POST Returns integer object ID or None if missing.
|
||||
def _extract_created_id(payload: Dict) -> Optional[int]:
|
||||
direct_id = payload.get("id")
|
||||
if isinstance(direct_id, int):
|
||||
@@ -101,13 +99,13 @@ def _extract_created_id(payload: Dict) -> Optional[int]:
|
||||
return None
|
||||
|
||||
|
||||
# [/DEF:_extract_created_id:Function]
|
||||
# #endregion _extract_created_id
|
||||
|
||||
|
||||
# [DEF:_generate_unique_name:Function]
|
||||
# @PURPOSE: Generates globally unique random names for charts/dashboards.
|
||||
# @PRE: used_names is mutable set for collision tracking.
|
||||
# @POST: Returns a unique string and stores it in used_names.
|
||||
# #region _generate_unique_name [TYPE Function]
|
||||
# @BRIEF Generates globally unique random names for charts/dashboards.
|
||||
# @PRE used_names is mutable set for collision tracking.
|
||||
# @POST Returns a unique string and stores it in used_names.
|
||||
def _generate_unique_name(prefix: str, used_names: set[str], rng: random.Random) -> str:
|
||||
adjectives = [
|
||||
"amber",
|
||||
@@ -141,13 +139,13 @@ def _generate_unique_name(prefix: str, used_names: set[str], rng: random.Random)
|
||||
return candidate
|
||||
|
||||
|
||||
# [/DEF:_generate_unique_name:Function]
|
||||
# #endregion _generate_unique_name
|
||||
|
||||
|
||||
# [DEF:_resolve_target_envs:Function]
|
||||
# @PURPOSE: Resolves requested environment IDs from configuration.
|
||||
# @PRE: env_ids is non-empty.
|
||||
# @POST: Returns mapping env_id -> configured environment object.
|
||||
# #region _resolve_target_envs [TYPE Function]
|
||||
# @BRIEF Resolves requested environment IDs from configuration.
|
||||
# @PRE env_ids is non-empty.
|
||||
# @POST Returns mapping env_id -> configured environment object.
|
||||
def _resolve_target_envs(env_ids: List[str]) -> Dict[str, Environment]:
|
||||
config_manager = ConfigManager()
|
||||
configured = {env.id: env for env in config_manager.get_environments()}
|
||||
@@ -175,13 +173,13 @@ def _resolve_target_envs(env_ids: List[str]) -> Dict[str, Environment]:
|
||||
return resolved
|
||||
|
||||
|
||||
# [/DEF:_resolve_target_envs:Function]
|
||||
# #endregion _resolve_target_envs
|
||||
|
||||
|
||||
# [DEF:_build_chart_template_pool:Function]
|
||||
# @PURPOSE: Builds a pool of source chart templates to clone in one environment.
|
||||
# @PRE: Client is authenticated.
|
||||
# @POST: Returns non-empty list of chart payload templates.
|
||||
# #region _build_chart_template_pool [TYPE Function]
|
||||
# @BRIEF Builds a pool of source chart templates to clone in one environment.
|
||||
# @PRE Client is authenticated.
|
||||
# @POST Returns non-empty list of chart payload templates.
|
||||
def _build_chart_template_pool(
|
||||
client: SupersetClient, pool_size: int, rng: random.Random
|
||||
) -> List[Dict]:
|
||||
@@ -250,14 +248,14 @@ def _build_chart_template_pool(
|
||||
return templates
|
||||
|
||||
|
||||
# [/DEF:_build_chart_template_pool:Function]
|
||||
# #endregion _build_chart_template_pool
|
||||
|
||||
|
||||
# [DEF:seed_superset_load_data:Function]
|
||||
# @PURPOSE: Creates dashboards and cloned charts for load testing across target environments.
|
||||
# @PRE: Target environments must be reachable and authenticated.
|
||||
# @POST: Returns execution statistics dictionary.
|
||||
# @SIDE_EFFECT: Creates objects in Superset environments.
|
||||
# #region seed_superset_load_data [TYPE Function]
|
||||
# @BRIEF Creates dashboards and cloned charts for load testing across target environments.
|
||||
# @PRE Target environments must be reachable and authenticated.
|
||||
# @POST Returns execution statistics dictionary.
|
||||
# @SIDE_EFFECT Creates objects in Superset environments.
|
||||
def seed_superset_load_data(args: argparse.Namespace) -> Dict:
|
||||
rng = random.Random(args.seed)
|
||||
env_map = _resolve_target_envs(args.envs)
|
||||
@@ -371,13 +369,13 @@ def seed_superset_load_data(args: argparse.Namespace) -> Dict:
|
||||
}
|
||||
|
||||
|
||||
# [/DEF:seed_superset_load_data:Function]
|
||||
# #endregion seed_superset_load_data
|
||||
|
||||
|
||||
# [DEF:main:Function]
|
||||
# @PURPOSE: CLI entrypoint for Superset load-test data seeding.
|
||||
# @PRE: Command line arguments are valid.
|
||||
# @POST: Prints summary and exits with non-zero status on failure.
|
||||
# #region main [TYPE Function]
|
||||
# @BRIEF CLI entrypoint for Superset load-test data seeding.
|
||||
# @PRE Command line arguments are valid.
|
||||
# @POST Prints summary and exits with non-zero status on failure.
|
||||
def main() -> None:
|
||||
with belief_scope("seed_superset_load_test.main"):
|
||||
args = _parse_args()
|
||||
@@ -385,10 +383,10 @@ def main() -> None:
|
||||
log.reflect("Seed load test complete", payload={"summary": result})
|
||||
|
||||
|
||||
# [/DEF:main:Function]
|
||||
# #endregion main
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
# [/DEF:SeedSupersetLoadTestScript:Module]
|
||||
# #endregion SeedSupersetLoadTestScript
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# [DEF:test_dataset_dashboard_relations_script:Module]
|
||||
# @SEMANTICS: scripts, test, dataset, dashboard, superset, relations
|
||||
# @PURPOSE: Tests and inspects dataset-to-dashboard relationship responses from Superset API.
|
||||
# @COMPLEXITY: 2
|
||||
# #region test_dataset_dashboard_relations_script [C:2] [TYPE Module] [SEMANTICS scripts, test, dataset, dashboard, superset, relations]
|
||||
# @BRIEF Tests and inspects dataset-to-dashboard relationship responses from Superset API.
|
||||
"""
|
||||
Script to test dataset-to-dashboard relationships from Superset API.
|
||||
|
||||
@@ -169,4 +167,4 @@ def test_dashboard_dataset_relations():
|
||||
if __name__ == "__main__":
|
||||
test_dashboard_dataset_relations()
|
||||
|
||||
# [/DEF:test_dataset_dashboard_relations_script:Module]
|
||||
# #endregion test_dataset_dashboard_relations_script
|
||||
|
||||
Reference in New Issue
Block a user