F5.1 · Inventario limpio, jerarquía manual y Hallazgos filtrados (#28)
Reset controlado de datos operativos e históricos, nueva jerarquía manual Departamento → Área → Yacimiento → Instalación → Subinstalación, filtros y administración inline de Hallazgos, y corrección de alta con Área sin Operadora.
This commit is contained in:
+64
-63
@@ -80,12 +80,9 @@ jobs:
|
||||
|
||||
docker compose --env-file .env.example up -d db
|
||||
|
||||
# The historical production reset is a one-shot operational migration,
|
||||
# not a bootstrap migration: it requires production data/configuration
|
||||
# that cannot exist at its timestamp in a database rebuilt from zero.
|
||||
# Prove the clean chain reaches that exact guard, then mark only that
|
||||
# one-shot migration as already applied and continue the reproducible
|
||||
# schema chain. The historical migration itself remains untouched.
|
||||
# Historical production reset is a one-shot migration that expects the
|
||||
# production admin. Prove the clean chain reaches that exact guard,
|
||||
# mark only that historical reset as applied, then continue the chain.
|
||||
bootstrap_log="$(mktemp)"
|
||||
set +e
|
||||
docker compose --env-file .env.example --profile tools run --build --rm migrate 2>&1 | tee "$bootstrap_log"
|
||||
@@ -104,17 +101,14 @@ jobs:
|
||||
docker compose --env-file .env.example exec -T db \
|
||||
psql -v ON_ERROR_STOP=1 -U dhv2_owner -d dhv2 <<'SQL'
|
||||
DO $$
|
||||
DECLARE
|
||||
reset_rows integer;
|
||||
DECLARE reset_rows integer;
|
||||
BEGIN
|
||||
SELECT COUNT(*) INTO reset_rows
|
||||
FROM typeorm_migrations
|
||||
WHERE name = 'ResetProductionOperationalData1788652800000';
|
||||
|
||||
IF reset_rows <> 0 THEN
|
||||
RAISE EXCEPTION 'CI one-shot bypass expected reset migration to be pending, found % rows', reset_rows;
|
||||
END IF;
|
||||
|
||||
INSERT INTO typeorm_migrations ("timestamp", name)
|
||||
VALUES (1788652800000, 'ResetProductionOperationalData1788652800000');
|
||||
END $$;
|
||||
@@ -122,17 +116,23 @@ jobs:
|
||||
|
||||
docker compose --env-file .env.example --profile tools run --rm migrate
|
||||
|
||||
# F5.1 intentionally ends with zero operational/domain instances. The
|
||||
# technical family and finding masters remain, but territory preload,
|
||||
# imports, applicability links and old audits are deliberately gone.
|
||||
docker compose --env-file .env.example exec -T db \
|
||||
psql -v ON_ERROR_STOP=1 -U dhv2_owner -d dhv2 <<'SQL'
|
||||
DO $$
|
||||
DECLARE
|
||||
f5_migrations integer;
|
||||
real_inventory integer;
|
||||
source_areas integer;
|
||||
source_yacimientos integer;
|
||||
domain_assets integer;
|
||||
audits integer;
|
||||
applicability integer;
|
||||
territory_sources integer;
|
||||
source_installations integer;
|
||||
source_subinstallations integer;
|
||||
source_findings integer;
|
||||
department_types integer;
|
||||
area_department_rules integer;
|
||||
BEGIN
|
||||
SELECT COUNT(*) INTO f5_migrations
|
||||
FROM typeorm_migrations
|
||||
@@ -141,28 +141,33 @@ jobs:
|
||||
'F5CanonicalInventoryHierarchy1790087150000',
|
||||
'F5AuthoritativeTerritory1790087200000',
|
||||
'F5OperationalContextCompatibility1790087250000',
|
||||
'F5AuthoritativeInventoryCatalog1790087300000'
|
||||
'F5AuthoritativeInventoryCatalog1790087300000',
|
||||
'F51CleanManualInventory1790087400000'
|
||||
);
|
||||
IF f5_migrations <> 5 THEN
|
||||
RAISE EXCEPTION 'Expected 5 F5 migrations, got %', f5_migrations;
|
||||
IF f5_migrations <> 6 THEN
|
||||
RAISE EXCEPTION 'Expected 6 F5/F5.1 migrations, got %', f5_migrations;
|
||||
END IF;
|
||||
|
||||
SELECT COUNT(*) INTO real_inventory
|
||||
FROM assets WHERE is_inventory_instance=true AND information_status<>'INACTIVE';
|
||||
IF real_inventory <> 0 THEN
|
||||
RAISE EXCEPTION 'Fresh F5 database must start with 0 real Inventory instances, got %', real_inventory;
|
||||
SELECT COUNT(*) INTO domain_assets FROM assets;
|
||||
IF domain_assets <> 0 THEN
|
||||
RAISE EXCEPTION 'F5.1 clean start must contain 0 Assets, got %', domain_assets;
|
||||
END IF;
|
||||
|
||||
SELECT COUNT(DISTINCT asset.id) FILTER (WHERE type.operational_role='AREA'),
|
||||
COUNT(DISTINCT asset.id) FILTER (WHERE lower(type.code)='yacimiento')
|
||||
INTO source_areas,source_yacimientos
|
||||
FROM source_documents document
|
||||
JOIN asset_source_documents link ON link.document_id=document.id
|
||||
JOIN assets asset ON asset.id=link.asset_id
|
||||
JOIN asset_types type ON type.id=asset.asset_type_id
|
||||
WHERE document.document_number='DH-F5-TERRITORY';
|
||||
IF source_areas <> 64 OR source_yacimientos <> 230 THEN
|
||||
RAISE EXCEPTION 'F5 territory preload mismatch: areas %, yacimientos %', source_areas,source_yacimientos;
|
||||
SELECT COUNT(*) INTO audits FROM audit_events;
|
||||
IF audits <> 0 THEN
|
||||
RAISE EXCEPTION 'F5.1 clean start must contain 0 audit events, got %', audits;
|
||||
END IF;
|
||||
|
||||
SELECT COUNT(*) INTO applicability FROM finding_catalog_item_inventory_families;
|
||||
IF applicability <> 0 THEN
|
||||
RAISE EXCEPTION 'F5.1 clean start must contain 0 finding applicability links, got %', applicability;
|
||||
END IF;
|
||||
|
||||
SELECT COUNT(*) INTO territory_sources
|
||||
FROM source_documents
|
||||
WHERE document_number='DH-F5-TERRITORY';
|
||||
IF territory_sources <> 0 THEN
|
||||
RAISE EXCEPTION 'F5.1 must remove the old territory source preload, got % rows', territory_sources;
|
||||
END IF;
|
||||
|
||||
SELECT COUNT(*) FILTER (WHERE level='INSTALLATION'),
|
||||
@@ -171,7 +176,7 @@ jobs:
|
||||
FROM inventory_families
|
||||
WHERE is_active=true AND source_reference LIKE 'F5:final_modelov2.xlsx%';
|
||||
IF source_installations <> 14 OR source_subinstallations <> 109 THEN
|
||||
RAISE EXCEPTION 'F5 family preload mismatch: installations %, subinstallations %', source_installations,source_subinstallations;
|
||||
RAISE EXCEPTION 'F5.1 must preserve technical family masters: installations %, subinstallations %', source_installations,source_subinstallations;
|
||||
END IF;
|
||||
|
||||
SELECT COUNT(*) INTO source_findings
|
||||
@@ -179,42 +184,38 @@ jobs:
|
||||
JOIN finding_categories category ON category.id=item.category_id
|
||||
WHERE lower(category.code)='f5model' AND item.is_active=true;
|
||||
IF source_findings <> 177 THEN
|
||||
RAISE EXCEPTION 'F5 finding preload mismatch: %', source_findings;
|
||||
RAISE EXCEPTION 'F5.1 must preserve finding master catalog, got %', source_findings;
|
||||
END IF;
|
||||
|
||||
SELECT COUNT(*) INTO department_types
|
||||
FROM asset_types
|
||||
WHERE lower(code)='departamento' AND can_be_root=true AND is_active=true;
|
||||
IF department_types <> 1 THEN
|
||||
RAISE EXCEPTION 'Expected one active root Departamento type, got %', department_types;
|
||||
END IF;
|
||||
|
||||
SELECT COUNT(*) INTO area_department_rules
|
||||
FROM asset_type_parent_rules rule
|
||||
JOIN asset_types child ON child.id=rule.child_type_id
|
||||
JOIN asset_types parent ON parent.id=rule.parent_type_id
|
||||
WHERE lower(child.code)='area' AND lower(parent.code)='departamento';
|
||||
IF area_department_rules <> 1 THEN
|
||||
RAISE EXCEPTION 'Expected Area → Departamento canonical rule, got %', area_department_rules;
|
||||
END IF;
|
||||
END $$;
|
||||
SQL
|
||||
|
||||
# Prove the five F5 migrations are actually reversible on a clean state.
|
||||
for _ in 1 2 3 4 5; do
|
||||
docker compose --env-file .env.example --profile tools run --rm migrate npm run migration:revert
|
||||
done
|
||||
|
||||
docker compose --env-file .env.example exec -T db \
|
||||
psql -v ON_ERROR_STOP=1 -U dhv2_owner -d dhv2 <<'SQL'
|
||||
DO $$
|
||||
DECLARE f5_migrations integer; instance_column integer;
|
||||
BEGIN
|
||||
SELECT COUNT(*) INTO f5_migrations
|
||||
FROM typeorm_migrations
|
||||
WHERE name LIKE 'F5%1790087%';
|
||||
IF f5_migrations <> 0 THEN
|
||||
RAISE EXCEPTION 'F5 rollback left % migration rows behind', f5_migrations;
|
||||
END IF;
|
||||
SELECT COUNT(*) INTO instance_column
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema='public' AND table_name='assets' AND column_name='is_inventory_instance';
|
||||
IF instance_column <> 0 THEN
|
||||
RAISE EXCEPTION 'F5 rollback left is_inventory_instance behind';
|
||||
END IF;
|
||||
END $$;
|
||||
SQL
|
||||
|
||||
# Reapply them once more. Each F5 migration performs its own source/cardinality checks.
|
||||
docker compose --env-file .env.example --profile tools run --rm migrate
|
||||
docker compose --env-file .env.example exec -T db \
|
||||
psql -v ON_ERROR_STOP=1 -U dhv2_owner -d dhv2 -Atc \
|
||||
"SELECT CASE WHEN COUNT(*)=5 THEN 'F5_REAPPLY_OK' ELSE 'F5_REAPPLY_FAILED:'||COUNT(*) END FROM typeorm_migrations WHERE name IN ('F5InventoryPhysicalInstance1790087100000','F5CanonicalInventoryHierarchy1790087150000','F5AuthoritativeTerritory1790087200000','F5OperationalContextCompatibility1790087250000','F5AuthoritativeInventoryCatalog1790087300000');" \
|
||||
| grep -Fx 'F5_REAPPLY_OK'
|
||||
# F5.1 is intentionally one-way: production rollback is the PRE database
|
||||
# backup, not migration:revert. Prove instead that the completed chain is
|
||||
# idempotent and has no pending migration on a second run.
|
||||
rerun_log="$(mktemp)"
|
||||
docker compose --env-file .env.example --profile tools run --rm migrate 2>&1 | tee "$rerun_log"
|
||||
grep -Eq 'No pending migrations|Applied migrations: 0' "$rerun_log" || {
|
||||
echo "ERROR: F5.1 migration chain is not idempotent." >&2
|
||||
cat "$rerun_log" >&2
|
||||
exit 1
|
||||
}
|
||||
rm -f "$rerun_log"
|
||||
- name: VPS-equivalent isolated API preflight
|
||||
run: |
|
||||
set -Eeuo pipefail
|
||||
|
||||
Reference in New Issue
Block a user