ghcr-cleanup-manager 1.1.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/CHANGELOG.md +356 -0
- package/LICENSE +21 -0
- package/README.md +262 -0
- package/dist/cleanup-summary/_cleanup-summary-markdown.d.ts +5 -0
- package/dist/cleanup-summary/_cleanup-summary-markdown.js +224 -0
- package/dist/cleanup-summary/_cleanup-summary.d.ts +56 -0
- package/dist/cleanup-summary/_cleanup-summary.js +59 -0
- package/dist/cleanup-summary/index.d.ts +2 -0
- package/dist/cleanup-summary/index.js +2 -0
- package/dist/cli/_args.d.ts +7 -0
- package/dist/cli/_args.js +41 -0
- package/dist/cli/_cleanup-command.d.ts +1 -0
- package/dist/cli/_cleanup-command.js +142 -0
- package/dist/cli/_db-merge-command.d.ts +1 -0
- package/dist/cli/_db-merge-command.js +41 -0
- package/dist/cli/_github-output.d.ts +10 -0
- package/dist/cli/_github-output.js +13 -0
- package/dist/cli/_json-output.d.ts +1 -0
- package/dist/cli/_json-output.js +11 -0
- package/dist/cli/_logger.d.ts +10 -0
- package/dist/cli/_logger.js +26 -0
- package/dist/cli/_older-than.d.ts +5 -0
- package/dist/cli/_older-than.js +42 -0
- package/dist/cli/_planner-options.d.ts +20 -0
- package/dist/cli/_planner-options.js +83 -0
- package/dist/cli/_scan-command.d.ts +1 -0
- package/dist/cli/_scan-command.js +39 -0
- package/dist/cli/_tag-selector-resolver.d.ts +3 -0
- package/dist/cli/_tag-selector-resolver.js +132 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +52 -0
- package/dist/config/_service-constants.d.ts +3 -0
- package/dist/config/_service-constants.js +3 -0
- package/dist/config/index.d.ts +9 -0
- package/dist/config/index.js +9 -0
- package/dist/core/_digest-tag.d.ts +2 -0
- package/dist/core/_digest-tag.js +12 -0
- package/dist/core/_github-package-owner.d.ts +11 -0
- package/dist/core/_github-package-owner.js +45 -0
- package/dist/core/_http-error.d.ts +6 -0
- package/dist/core/_http-error.js +33 -0
- package/dist/core/_types.d.ts +54 -0
- package/dist/core/_types.js +8 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.js +4 -0
- package/dist/db/_cleanup-run-writer.d.ts +10 -0
- package/dist/db/_cleanup-run-writer.js +113 -0
- package/dist/db/_db-merge-cleanup-copy.d.ts +7 -0
- package/dist/db/_db-merge-cleanup-copy.js +172 -0
- package/dist/db/_db-merge-history.d.ts +2 -0
- package/dist/db/_db-merge-history.js +15 -0
- package/dist/db/_db-merge-repository.d.ts +8 -0
- package/dist/db/_db-merge-repository.js +95 -0
- package/dist/db/_db-merge-scan-copy.d.ts +10 -0
- package/dist/db/_db-merge-scan-copy.js +70 -0
- package/dist/db/_db-merge-types.d.ts +44 -0
- package/dist/db/_db-merge-types.js +1 -0
- package/dist/db/_github-actions-run-url.d.ts +1 -0
- package/dist/db/_github-actions-run-url.js +9 -0
- package/dist/db/_manifest-reachability.d.ts +2 -0
- package/dist/db/_manifest-reachability.js +158 -0
- package/dist/db/_scan-writer.d.ts +20 -0
- package/dist/db/_scan-writer.js +184 -0
- package/dist/db/_schema.d.ts +2 -0
- package/dist/db/_schema.js +19 -0
- package/dist/db/_snapshot-repository.d.ts +24 -0
- package/dist/db/_snapshot-repository.js +86 -0
- package/dist/db/_sql-placeholders.d.ts +2 -0
- package/dist/db/_sql-placeholders.js +16 -0
- package/dist/db/index.d.ts +10 -0
- package/dist/db/index.js +13 -0
- package/dist/db/planner/_planner-direct-target-root-options.d.ts +11 -0
- package/dist/db/planner/_planner-direct-target-root-options.js +1 -0
- package/dist/db/planner/_planner-direct-target-root-tag-filters.d.ts +7 -0
- package/dist/db/planner/_planner-direct-target-root-tag-filters.js +41 -0
- package/dist/db/planner/_planner-direct-target-roots-combined-sql.d.ts +7 -0
- package/dist/db/planner/_planner-direct-target-roots-combined-sql.js +189 -0
- package/dist/db/planner/_planner-direct-target-roots-combined.d.ts +4 -0
- package/dist/db/planner/_planner-direct-target-roots-combined.js +10 -0
- package/dist/db/planner/_planner-direct-target-roots-tagged.d.ts +4 -0
- package/dist/db/planner/_planner-direct-target-roots-tagged.js +117 -0
- package/dist/db/planner/_planner-direct-target-roots.d.ts +8 -0
- package/dist/db/planner/_planner-direct-target-roots.js +17 -0
- package/dist/db/planner/_planner-direct-target-tags.d.ts +6 -0
- package/dist/db/planner/_planner-direct-target-tags.js +54 -0
- package/dist/db/planner/_planner-latest-scan.d.ts +10 -0
- package/dist/db/planner/_planner-latest-scan.js +20 -0
- package/dist/db/planner/_planner-output.d.ts +5 -0
- package/dist/db/planner/_planner-output.js +111 -0
- package/dist/db/planner/_planner-plan-artifacts-blocked-roots-sql.d.ts +1 -0
- package/dist/db/planner/_planner-plan-artifacts-blocked-roots-sql.js +110 -0
- package/dist/db/planner/_planner-plan-artifacts-closure-sql.d.ts +1 -0
- package/dist/db/planner/_planner-plan-artifacts-closure-sql.js +188 -0
- package/dist/db/planner/_planner-plan-artifacts-supported-untag-only-sql.d.ts +1 -0
- package/dist/db/planner/_planner-plan-artifacts-supported-untag-only-sql.js +86 -0
- package/dist/db/planner/_planner-plan-artifacts.d.ts +7 -0
- package/dist/db/planner/_planner-plan-artifacts.js +109 -0
- package/dist/db/planner/_planner-repository.d.ts +44 -0
- package/dist/db/planner/_planner-repository.js +105 -0
- package/dist/db/planner/_planner-sql.d.ts +12 -0
- package/dist/db/planner/_planner-sql.js +46 -0
- package/dist/db/planner/_planner-tag-selectors.d.ts +8 -0
- package/dist/db/planner/_planner-tag-selectors.js +57 -0
- package/dist/db/planner/_planner-types.d.ts +144 -0
- package/dist/db/planner/_planner-types.js +49 -0
- package/dist/db/planner/index.d.ts +3 -0
- package/dist/db/planner/index.js +2 -0
- package/dist/execute/_http.d.ts +7 -0
- package/dist/execute/_http.js +48 -0
- package/dist/execute/_manifest-detach.d.ts +4 -0
- package/dist/execute/_manifest-detach.js +31 -0
- package/dist/execute/_package-version-delete-client.d.ts +4 -0
- package/dist/execute/_package-version-delete-client.js +34 -0
- package/dist/execute/_package-version-page-client.d.ts +14 -0
- package/dist/execute/_package-version-page-client.js +64 -0
- package/dist/execute/_package-version-tag-source-client.d.ts +12 -0
- package/dist/execute/_package-version-tag-source-client.js +65 -0
- package/dist/execute/_plan-executor.d.ts +3 -0
- package/dist/execute/_plan-executor.js +69 -0
- package/dist/execute/_registry-manifest-client.d.ts +12 -0
- package/dist/execute/_registry-manifest-client.js +79 -0
- package/dist/execute/_registry-token-client.d.ts +4 -0
- package/dist/execute/_registry-token-client.js +37 -0
- package/dist/execute/_types.d.ts +34 -0
- package/dist/execute/_types.js +1 -0
- package/dist/execute/_untag-client.d.ts +2 -0
- package/dist/execute/_untag-client.js +30 -0
- package/dist/execute/index.d.ts +5 -0
- package/dist/execute/index.js +3 -0
- package/dist/ingest/github/_manifest-client.d.ts +14 -0
- package/dist/ingest/github/_manifest-client.js +108 -0
- package/dist/ingest/github/_manifest-ingest.d.ts +3 -0
- package/dist/ingest/github/_manifest-ingest.js +90 -0
- package/dist/ingest/github/_manifest-kind.d.ts +26 -0
- package/dist/ingest/github/_manifest-kind.js +65 -0
- package/dist/ingest/github/_package-metadata-load.d.ts +5 -0
- package/dist/ingest/github/_package-metadata-load.js +45 -0
- package/dist/ingest/github/_package-version-page-load.d.ts +13 -0
- package/dist/ingest/github/_package-version-page-load.js +55 -0
- package/dist/ingest/github/_packages-client.d.ts +10 -0
- package/dist/ingest/github/_packages-client.js +76 -0
- package/dist/ingest/github/_parallel-paginated-ingest.d.ts +12 -0
- package/dist/ingest/github/_parallel-paginated-ingest.js +49 -0
- package/dist/ingest/github/_registry-token-client.d.ts +6 -0
- package/dist/ingest/github/_registry-token-client.js +67 -0
- package/dist/ingest/github/_shared.d.ts +28 -0
- package/dist/ingest/github/_shared.js +70 -0
- package/dist/ingest/github/index.d.ts +11 -0
- package/dist/ingest/github/index.js +29 -0
- package/package.json +78 -0
- package/resources/sql/schema/001_schema.sql +222 -0
- package/resources/sql/views/001_v_latest_scan_per_package.sql +27 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
export const _LIST_BLOCKED_ROOTS_SQL = `
|
|
2
|
+
WITH selected_root_graphs AS (
|
|
3
|
+
SELECT
|
|
4
|
+
dtr.root_version_id,
|
|
5
|
+
dtr.root_digest,
|
|
6
|
+
dtr.root_manifest_kind,
|
|
7
|
+
manifest_graphs.graph_id
|
|
8
|
+
FROM temp_direct_target_roots dtr
|
|
9
|
+
CROSS JOIN manifest_graphs
|
|
10
|
+
WHERE manifest_graphs.scan_id = ?
|
|
11
|
+
AND manifest_graphs.digest = dtr.root_digest
|
|
12
|
+
),
|
|
13
|
+
selected_graphs AS (
|
|
14
|
+
SELECT DISTINCT
|
|
15
|
+
selected_root_graphs.graph_id
|
|
16
|
+
FROM selected_root_graphs
|
|
17
|
+
),
|
|
18
|
+
retained_tagged_manifests AS (
|
|
19
|
+
SELECT DISTINCT
|
|
20
|
+
manifest_graphs.graph_id,
|
|
21
|
+
m.version_id AS tagged_version_id,
|
|
22
|
+
m.digest AS tagged_digest
|
|
23
|
+
FROM selected_graphs
|
|
24
|
+
CROSS JOIN manifest_graphs
|
|
25
|
+
CROSS JOIN manifests m
|
|
26
|
+
JOIN tags t
|
|
27
|
+
ON t.scan_id = m.scan_id
|
|
28
|
+
AND t.version_id = m.version_id
|
|
29
|
+
AND t.is_digest_tag = 0
|
|
30
|
+
WHERE manifest_graphs.scan_id = m.scan_id
|
|
31
|
+
AND selected_graphs.graph_id = manifest_graphs.graph_id
|
|
32
|
+
AND manifest_graphs.digest = m.digest
|
|
33
|
+
AND m.scan_id = ?
|
|
34
|
+
AND NOT EXISTS (
|
|
35
|
+
SELECT 1
|
|
36
|
+
FROM temp_direct_target_roots dtr
|
|
37
|
+
WHERE dtr.root_digest = m.digest
|
|
38
|
+
)
|
|
39
|
+
),
|
|
40
|
+
protected_manifests AS (
|
|
41
|
+
SELECT
|
|
42
|
+
retained.graph_id,
|
|
43
|
+
retained.tagged_version_id AS blocking_version_id,
|
|
44
|
+
retained.tagged_digest AS blocking_digest,
|
|
45
|
+
retained.tagged_digest AS digest,
|
|
46
|
+
0 AS min_distance
|
|
47
|
+
FROM retained_tagged_manifests retained
|
|
48
|
+
|
|
49
|
+
UNION
|
|
50
|
+
|
|
51
|
+
SELECT
|
|
52
|
+
retained.graph_id,
|
|
53
|
+
retained.tagged_version_id AS blocking_version_id,
|
|
54
|
+
retained.tagged_digest AS blocking_digest,
|
|
55
|
+
retained_overlap.descendant_digest AS digest,
|
|
56
|
+
retained_overlap.min_distance
|
|
57
|
+
FROM retained_tagged_manifests retained
|
|
58
|
+
JOIN manifest_reachability retained_overlap
|
|
59
|
+
ON retained_overlap.scan_id = ?
|
|
60
|
+
AND retained_overlap.ancestor_digest = retained.tagged_digest
|
|
61
|
+
AND retained_overlap.min_distance > 0
|
|
62
|
+
),
|
|
63
|
+
ranked_blocks AS (
|
|
64
|
+
SELECT
|
|
65
|
+
root_graph.root_version_id AS blocked_version_id,
|
|
66
|
+
root_graph.root_digest AS blocked_digest,
|
|
67
|
+
retained.tagged_version_id AS blocking_version_id,
|
|
68
|
+
retained.tagged_digest AS blocking_digest,
|
|
69
|
+
COALESCE(retained_overlap.descendant_digest, helper_overlap.digest) AS overlap_digest,
|
|
70
|
+
root_graph.root_manifest_kind AS overlap_manifest_kind,
|
|
71
|
+
'overlap-with-retained-root' AS block_reason,
|
|
72
|
+
ROW_NUMBER() OVER (
|
|
73
|
+
PARTITION BY root_graph.root_digest, retained.tagged_digest
|
|
74
|
+
ORDER BY
|
|
75
|
+
COALESCE(retained_overlap.min_distance, helper_overlap.min_distance),
|
|
76
|
+
COALESCE(retained_overlap.descendant_digest, helper_overlap.digest),
|
|
77
|
+
root_graph.root_digest
|
|
78
|
+
) AS rn
|
|
79
|
+
FROM selected_root_graphs root_graph
|
|
80
|
+
JOIN retained_tagged_manifests retained
|
|
81
|
+
ON retained.graph_id = root_graph.graph_id
|
|
82
|
+
AND retained.tagged_digest <> root_graph.root_digest
|
|
83
|
+
LEFT JOIN manifest_reachability retained_overlap
|
|
84
|
+
ON retained_overlap.scan_id = ?
|
|
85
|
+
AND retained_overlap.ancestor_digest = retained.tagged_digest
|
|
86
|
+
AND retained_overlap.descendant_digest = root_graph.root_digest
|
|
87
|
+
AND retained_overlap.min_distance > 0
|
|
88
|
+
LEFT JOIN manifest_edges helper_edge INDEXED BY idx_manifest_edges_scan_parent
|
|
89
|
+
ON helper_edge.scan_id = ?
|
|
90
|
+
AND helper_edge.parent_digest = root_graph.root_digest
|
|
91
|
+
AND helper_edge.edge_kind = 'digest-tag-referrer'
|
|
92
|
+
LEFT JOIN protected_manifests helper_overlap
|
|
93
|
+
ON helper_overlap.graph_id = root_graph.graph_id
|
|
94
|
+
AND helper_overlap.blocking_digest = retained.tagged_digest
|
|
95
|
+
AND helper_overlap.digest = helper_edge.child_digest
|
|
96
|
+
WHERE retained_overlap.descendant_digest IS NOT NULL
|
|
97
|
+
OR helper_overlap.digest IS NOT NULL
|
|
98
|
+
)
|
|
99
|
+
SELECT
|
|
100
|
+
blocked_version_id,
|
|
101
|
+
blocked_digest,
|
|
102
|
+
blocking_version_id,
|
|
103
|
+
blocking_digest,
|
|
104
|
+
overlap_digest,
|
|
105
|
+
overlap_manifest_kind,
|
|
106
|
+
block_reason
|
|
107
|
+
FROM ranked_blocks
|
|
108
|
+
WHERE rn = 1
|
|
109
|
+
ORDER BY blocked_digest, blocking_digest, overlap_digest
|
|
110
|
+
`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const _LIST_CLOSURE_MANIFESTS_SQL = "\n WITH selected_graphs AS (\n SELECT DISTINCT\n manifest_graphs.graph_id\n FROM temp_direct_target_roots dtr\n CROSS JOIN manifest_graphs\n WHERE manifest_graphs.scan_id = ?\n AND manifest_graphs.digest = dtr.root_digest\n ),\n retained_tagged_manifests AS (\n SELECT DISTINCT\n m.digest\n FROM selected_graphs\n CROSS JOIN manifest_graphs\n CROSS JOIN manifests m\n JOIN tags t\n ON t.scan_id = m.scan_id\n AND t.version_id = m.version_id\n AND t.is_digest_tag = 0\n WHERE manifest_graphs.scan_id = m.scan_id\n AND selected_graphs.graph_id = manifest_graphs.graph_id\n AND manifest_graphs.digest = m.digest\n AND m.scan_id = ?\n AND NOT EXISTS (\n SELECT 1\n FROM temp_direct_target_roots dtr\n WHERE dtr.root_digest = m.digest\n )\n ),\n retained_manifests AS (\n SELECT\n retained.digest\n FROM retained_tagged_manifests retained\n\n UNION\n\n SELECT\n mr.descendant_digest AS digest\n FROM retained_tagged_manifests retained\n CROSS JOIN manifest_reachability mr\n WHERE mr.scan_id = ?\n AND mr.ancestor_digest = retained.digest\n AND mr.min_distance > 0\n ),\n direct_target_closure AS (\n SELECT\n dtr.root_digest AS source_digest,\n dtr.root_digest AS member_digest,\n 0 AS hops_from_root\n FROM temp_direct_target_roots dtr\n\n UNION ALL\n\n SELECT\n dtr.root_digest AS source_digest,\n mr.descendant_digest AS member_digest,\n mr.min_distance AS hops_from_root\n FROM temp_direct_target_roots dtr\n CROSS JOIN manifest_reachability mr\n WHERE mr.scan_id = ?\n AND mr.ancestor_digest = dtr.root_digest\n AND mr.min_distance > 0\n ),\n closure_seed AS (\n SELECT\n dtc.source_digest,\n dtc.member_digest,\n dtc.hops_from_root\n FROM direct_target_closure dtc\n WHERE dtc.hops_from_root = 0\n OR NOT EXISTS (\n SELECT 1\n FROM retained_manifests retained\n WHERE retained.digest = dtc.member_digest\n )\n ),\n undirected_edges AS (\n SELECT\n me.parent_digest AS source_digest,\n me.child_digest AS target_digest\n FROM selected_graphs\n CROSS JOIN manifest_graphs parent_graph\n CROSS JOIN manifest_edges me INDEXED BY idx_manifest_edges_scan_parent\n WHERE parent_graph.scan_id = me.scan_id\n AND selected_graphs.graph_id = parent_graph.graph_id\n AND parent_graph.digest = me.parent_digest\n AND me.scan_id = ?\n AND NOT EXISTS (\n SELECT 1\n FROM retained_manifests retained\n WHERE retained.digest = me.parent_digest\n OR retained.digest = me.child_digest\n )\n\n UNION\n\n SELECT\n me.child_digest AS source_digest,\n me.parent_digest AS target_digest\n FROM selected_graphs\n CROSS JOIN manifest_graphs child_graph\n CROSS JOIN manifest_edges me INDEXED BY idx_manifest_edges_scan_child\n WHERE child_graph.scan_id = me.scan_id\n AND selected_graphs.graph_id = child_graph.graph_id\n AND child_graph.digest = me.child_digest\n AND me.scan_id = ?\n AND NOT EXISTS (\n SELECT 1\n FROM retained_manifests retained\n WHERE retained.digest = me.parent_digest\n OR retained.digest = me.child_digest\n )\n ),\n delete_component_members AS (\n SELECT\n seed.source_digest,\n seed.member_digest\n FROM closure_seed seed\n\n UNION\n\n SELECT\n walk.source_digest,\n edge.target_digest AS member_digest\n FROM delete_component_members walk\n JOIN undirected_edges edge\n ON edge.source_digest = walk.member_digest\n WHERE NOT EXISTS (\n SELECT 1\n FROM retained_manifests retained\n WHERE retained.digest = edge.target_digest\n )\n ),\n source_seed_hops AS (\n SELECT\n seed.source_digest,\n MAX(seed.hops_from_root) AS max_seed_hops\n FROM closure_seed seed\n GROUP BY seed.source_digest\n ),\n descendant_hops AS (\n SELECT\n dtc.source_digest,\n dtc.member_digest,\n MIN(dtc.hops_from_root) AS min_hops_from_root\n FROM direct_target_closure dtc\n WHERE dtc.hops_from_root > 0\n GROUP BY dtc.source_digest, dtc.member_digest\n )\n SELECT\n dtr.root_version_id AS source_version_id,\n walk.source_digest,\n MIN(member_manifest.version_id) AS member_version_id,\n walk.member_digest,\n MIN(member_manifest.manifest_kind) AS member_manifest_kind,\n CASE\n WHEN walk.member_digest = walk.source_digest\n THEN 0\n WHEN descendant_hops.min_hops_from_root IS NOT NULL\n THEN descendant_hops.min_hops_from_root\n ELSE source_seed_hops.max_seed_hops + 1\n END AS hops_from_root,\n CASE\n WHEN walk.member_digest = walk.source_digest\n THEN 'root'\n WHEN descendant_hops.min_hops_from_root IS NOT NULL\n THEN 'descendant'\n ELSE 'connected'\n END AS member_role\n FROM delete_component_members walk\n JOIN temp_direct_target_roots dtr\n ON dtr.root_digest = walk.source_digest\n JOIN manifests member_manifest\n ON member_manifest.scan_id = ?\n AND member_manifest.digest = walk.member_digest\n JOIN source_seed_hops\n ON source_seed_hops.source_digest = walk.source_digest\n LEFT JOIN descendant_hops\n ON descendant_hops.source_digest = walk.source_digest\n AND descendant_hops.member_digest = walk.member_digest\n GROUP BY\n dtr.root_version_id,\n walk.source_digest,\n walk.member_digest,\n descendant_hops.min_hops_from_root,\n source_seed_hops.max_seed_hops\n ORDER BY walk.source_digest, hops_from_root, walk.member_digest\n";
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
export const _LIST_CLOSURE_MANIFESTS_SQL = `
|
|
2
|
+
WITH selected_graphs AS (
|
|
3
|
+
SELECT DISTINCT
|
|
4
|
+
manifest_graphs.graph_id
|
|
5
|
+
FROM temp_direct_target_roots dtr
|
|
6
|
+
CROSS JOIN manifest_graphs
|
|
7
|
+
WHERE manifest_graphs.scan_id = ?
|
|
8
|
+
AND manifest_graphs.digest = dtr.root_digest
|
|
9
|
+
),
|
|
10
|
+
retained_tagged_manifests AS (
|
|
11
|
+
SELECT DISTINCT
|
|
12
|
+
m.digest
|
|
13
|
+
FROM selected_graphs
|
|
14
|
+
CROSS JOIN manifest_graphs
|
|
15
|
+
CROSS JOIN manifests m
|
|
16
|
+
JOIN tags t
|
|
17
|
+
ON t.scan_id = m.scan_id
|
|
18
|
+
AND t.version_id = m.version_id
|
|
19
|
+
AND t.is_digest_tag = 0
|
|
20
|
+
WHERE manifest_graphs.scan_id = m.scan_id
|
|
21
|
+
AND selected_graphs.graph_id = manifest_graphs.graph_id
|
|
22
|
+
AND manifest_graphs.digest = m.digest
|
|
23
|
+
AND m.scan_id = ?
|
|
24
|
+
AND NOT EXISTS (
|
|
25
|
+
SELECT 1
|
|
26
|
+
FROM temp_direct_target_roots dtr
|
|
27
|
+
WHERE dtr.root_digest = m.digest
|
|
28
|
+
)
|
|
29
|
+
),
|
|
30
|
+
retained_manifests AS (
|
|
31
|
+
SELECT
|
|
32
|
+
retained.digest
|
|
33
|
+
FROM retained_tagged_manifests retained
|
|
34
|
+
|
|
35
|
+
UNION
|
|
36
|
+
|
|
37
|
+
SELECT
|
|
38
|
+
mr.descendant_digest AS digest
|
|
39
|
+
FROM retained_tagged_manifests retained
|
|
40
|
+
CROSS JOIN manifest_reachability mr
|
|
41
|
+
WHERE mr.scan_id = ?
|
|
42
|
+
AND mr.ancestor_digest = retained.digest
|
|
43
|
+
AND mr.min_distance > 0
|
|
44
|
+
),
|
|
45
|
+
direct_target_closure AS (
|
|
46
|
+
SELECT
|
|
47
|
+
dtr.root_digest AS source_digest,
|
|
48
|
+
dtr.root_digest AS member_digest,
|
|
49
|
+
0 AS hops_from_root
|
|
50
|
+
FROM temp_direct_target_roots dtr
|
|
51
|
+
|
|
52
|
+
UNION ALL
|
|
53
|
+
|
|
54
|
+
SELECT
|
|
55
|
+
dtr.root_digest AS source_digest,
|
|
56
|
+
mr.descendant_digest AS member_digest,
|
|
57
|
+
mr.min_distance AS hops_from_root
|
|
58
|
+
FROM temp_direct_target_roots dtr
|
|
59
|
+
CROSS JOIN manifest_reachability mr
|
|
60
|
+
WHERE mr.scan_id = ?
|
|
61
|
+
AND mr.ancestor_digest = dtr.root_digest
|
|
62
|
+
AND mr.min_distance > 0
|
|
63
|
+
),
|
|
64
|
+
closure_seed AS (
|
|
65
|
+
SELECT
|
|
66
|
+
dtc.source_digest,
|
|
67
|
+
dtc.member_digest,
|
|
68
|
+
dtc.hops_from_root
|
|
69
|
+
FROM direct_target_closure dtc
|
|
70
|
+
WHERE dtc.hops_from_root = 0
|
|
71
|
+
OR NOT EXISTS (
|
|
72
|
+
SELECT 1
|
|
73
|
+
FROM retained_manifests retained
|
|
74
|
+
WHERE retained.digest = dtc.member_digest
|
|
75
|
+
)
|
|
76
|
+
),
|
|
77
|
+
undirected_edges AS (
|
|
78
|
+
SELECT
|
|
79
|
+
me.parent_digest AS source_digest,
|
|
80
|
+
me.child_digest AS target_digest
|
|
81
|
+
FROM selected_graphs
|
|
82
|
+
CROSS JOIN manifest_graphs parent_graph
|
|
83
|
+
CROSS JOIN manifest_edges me INDEXED BY idx_manifest_edges_scan_parent
|
|
84
|
+
WHERE parent_graph.scan_id = me.scan_id
|
|
85
|
+
AND selected_graphs.graph_id = parent_graph.graph_id
|
|
86
|
+
AND parent_graph.digest = me.parent_digest
|
|
87
|
+
AND me.scan_id = ?
|
|
88
|
+
AND NOT EXISTS (
|
|
89
|
+
SELECT 1
|
|
90
|
+
FROM retained_manifests retained
|
|
91
|
+
WHERE retained.digest = me.parent_digest
|
|
92
|
+
OR retained.digest = me.child_digest
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
UNION
|
|
96
|
+
|
|
97
|
+
SELECT
|
|
98
|
+
me.child_digest AS source_digest,
|
|
99
|
+
me.parent_digest AS target_digest
|
|
100
|
+
FROM selected_graphs
|
|
101
|
+
CROSS JOIN manifest_graphs child_graph
|
|
102
|
+
CROSS JOIN manifest_edges me INDEXED BY idx_manifest_edges_scan_child
|
|
103
|
+
WHERE child_graph.scan_id = me.scan_id
|
|
104
|
+
AND selected_graphs.graph_id = child_graph.graph_id
|
|
105
|
+
AND child_graph.digest = me.child_digest
|
|
106
|
+
AND me.scan_id = ?
|
|
107
|
+
AND NOT EXISTS (
|
|
108
|
+
SELECT 1
|
|
109
|
+
FROM retained_manifests retained
|
|
110
|
+
WHERE retained.digest = me.parent_digest
|
|
111
|
+
OR retained.digest = me.child_digest
|
|
112
|
+
)
|
|
113
|
+
),
|
|
114
|
+
delete_component_members AS (
|
|
115
|
+
SELECT
|
|
116
|
+
seed.source_digest,
|
|
117
|
+
seed.member_digest
|
|
118
|
+
FROM closure_seed seed
|
|
119
|
+
|
|
120
|
+
UNION
|
|
121
|
+
|
|
122
|
+
SELECT
|
|
123
|
+
walk.source_digest,
|
|
124
|
+
edge.target_digest AS member_digest
|
|
125
|
+
FROM delete_component_members walk
|
|
126
|
+
JOIN undirected_edges edge
|
|
127
|
+
ON edge.source_digest = walk.member_digest
|
|
128
|
+
WHERE NOT EXISTS (
|
|
129
|
+
SELECT 1
|
|
130
|
+
FROM retained_manifests retained
|
|
131
|
+
WHERE retained.digest = edge.target_digest
|
|
132
|
+
)
|
|
133
|
+
),
|
|
134
|
+
source_seed_hops AS (
|
|
135
|
+
SELECT
|
|
136
|
+
seed.source_digest,
|
|
137
|
+
MAX(seed.hops_from_root) AS max_seed_hops
|
|
138
|
+
FROM closure_seed seed
|
|
139
|
+
GROUP BY seed.source_digest
|
|
140
|
+
),
|
|
141
|
+
descendant_hops AS (
|
|
142
|
+
SELECT
|
|
143
|
+
dtc.source_digest,
|
|
144
|
+
dtc.member_digest,
|
|
145
|
+
MIN(dtc.hops_from_root) AS min_hops_from_root
|
|
146
|
+
FROM direct_target_closure dtc
|
|
147
|
+
WHERE dtc.hops_from_root > 0
|
|
148
|
+
GROUP BY dtc.source_digest, dtc.member_digest
|
|
149
|
+
)
|
|
150
|
+
SELECT
|
|
151
|
+
dtr.root_version_id AS source_version_id,
|
|
152
|
+
walk.source_digest,
|
|
153
|
+
MIN(member_manifest.version_id) AS member_version_id,
|
|
154
|
+
walk.member_digest,
|
|
155
|
+
MIN(member_manifest.manifest_kind) AS member_manifest_kind,
|
|
156
|
+
CASE
|
|
157
|
+
WHEN walk.member_digest = walk.source_digest
|
|
158
|
+
THEN 0
|
|
159
|
+
WHEN descendant_hops.min_hops_from_root IS NOT NULL
|
|
160
|
+
THEN descendant_hops.min_hops_from_root
|
|
161
|
+
ELSE source_seed_hops.max_seed_hops + 1
|
|
162
|
+
END AS hops_from_root,
|
|
163
|
+
CASE
|
|
164
|
+
WHEN walk.member_digest = walk.source_digest
|
|
165
|
+
THEN 'root'
|
|
166
|
+
WHEN descendant_hops.min_hops_from_root IS NOT NULL
|
|
167
|
+
THEN 'descendant'
|
|
168
|
+
ELSE 'connected'
|
|
169
|
+
END AS member_role
|
|
170
|
+
FROM delete_component_members walk
|
|
171
|
+
JOIN temp_direct_target_roots dtr
|
|
172
|
+
ON dtr.root_digest = walk.source_digest
|
|
173
|
+
JOIN manifests member_manifest
|
|
174
|
+
ON member_manifest.scan_id = ?
|
|
175
|
+
AND member_manifest.digest = walk.member_digest
|
|
176
|
+
JOIN source_seed_hops
|
|
177
|
+
ON source_seed_hops.source_digest = walk.source_digest
|
|
178
|
+
LEFT JOIN descendant_hops
|
|
179
|
+
ON descendant_hops.source_digest = walk.source_digest
|
|
180
|
+
AND descendant_hops.member_digest = walk.member_digest
|
|
181
|
+
GROUP BY
|
|
182
|
+
dtr.root_version_id,
|
|
183
|
+
walk.source_digest,
|
|
184
|
+
walk.member_digest,
|
|
185
|
+
descendant_hops.min_hops_from_root,
|
|
186
|
+
source_seed_hops.max_seed_hops
|
|
187
|
+
ORDER BY walk.source_digest, hops_from_root, walk.member_digest
|
|
188
|
+
`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const _LIST_SUPPORTED_UNTAG_ONLY_ROOT_DIGESTS_SQL = "\n WITH selected_graphs AS (\n SELECT DISTINCT\n manifest_graphs.graph_id\n FROM temp_direct_target_roots dtr\n CROSS JOIN manifest_graphs\n WHERE manifest_graphs.scan_id = ?\n AND manifest_graphs.digest = dtr.root_digest\n ),\n retained_tagged_manifests AS (\n SELECT DISTINCT\n m.digest\n FROM selected_graphs\n CROSS JOIN manifest_graphs\n CROSS JOIN manifests m\n JOIN tags t\n ON t.scan_id = m.scan_id\n AND t.version_id = m.version_id\n AND t.is_digest_tag = 0\n WHERE manifest_graphs.scan_id = m.scan_id\n AND selected_graphs.graph_id = manifest_graphs.graph_id\n AND manifest_graphs.digest = m.digest\n AND m.scan_id = ?\n AND NOT EXISTS (\n SELECT 1\n FROM temp_direct_target_roots dtr\n WHERE dtr.root_digest = m.digest\n )\n ),\n retained_manifests AS (\n SELECT\n retained.digest\n FROM retained_tagged_manifests retained\n\n UNION\n\n SELECT\n mr.descendant_digest AS digest\n FROM retained_tagged_manifests retained\n CROSS JOIN manifest_reachability mr\n WHERE mr.scan_id = ?\n AND mr.ancestor_digest = retained.digest\n AND mr.min_distance > 0\n )\n SELECT DISTINCT\n dtr.root_digest\n FROM temp_direct_target_roots dtr\n WHERE dtr.root_manifest_kind = 'index_manifest'\n AND EXISTS (\n SELECT 1\n FROM manifest_edges me\n JOIN manifests child\n ON child.scan_id = ?\n AND child.digest = me.child_digest\n WHERE me.scan_id = ?\n AND me.parent_digest = dtr.root_digest\n AND me.edge_kind = 'referrer'\n AND child.manifest_kind = 'signature_manifest'\n )\n AND EXISTS (\n SELECT 1\n FROM manifest_edges me\n JOIN manifests child\n ON child.scan_id = ?\n AND child.digest = me.child_digest\n WHERE me.scan_id = ?\n AND me.parent_digest = dtr.root_digest\n AND me.edge_kind = 'image-child'\n AND child.manifest_kind <> 'signature_manifest'\n )\n AND NOT EXISTS (\n SELECT 1\n FROM manifest_edges me\n JOIN manifests child\n ON child.scan_id = ?\n AND child.digest = me.child_digest\n WHERE me.scan_id = ?\n AND me.parent_digest = dtr.root_digest\n AND me.edge_kind = 'image-child'\n AND child.manifest_kind <> 'signature_manifest'\n AND child.digest NOT IN (\n SELECT digest\n FROM retained_manifests\n )\n )\n";
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export const _LIST_SUPPORTED_UNTAG_ONLY_ROOT_DIGESTS_SQL = `
|
|
2
|
+
WITH selected_graphs AS (
|
|
3
|
+
SELECT DISTINCT
|
|
4
|
+
manifest_graphs.graph_id
|
|
5
|
+
FROM temp_direct_target_roots dtr
|
|
6
|
+
CROSS JOIN manifest_graphs
|
|
7
|
+
WHERE manifest_graphs.scan_id = ?
|
|
8
|
+
AND manifest_graphs.digest = dtr.root_digest
|
|
9
|
+
),
|
|
10
|
+
retained_tagged_manifests AS (
|
|
11
|
+
SELECT DISTINCT
|
|
12
|
+
m.digest
|
|
13
|
+
FROM selected_graphs
|
|
14
|
+
CROSS JOIN manifest_graphs
|
|
15
|
+
CROSS JOIN manifests m
|
|
16
|
+
JOIN tags t
|
|
17
|
+
ON t.scan_id = m.scan_id
|
|
18
|
+
AND t.version_id = m.version_id
|
|
19
|
+
AND t.is_digest_tag = 0
|
|
20
|
+
WHERE manifest_graphs.scan_id = m.scan_id
|
|
21
|
+
AND selected_graphs.graph_id = manifest_graphs.graph_id
|
|
22
|
+
AND manifest_graphs.digest = m.digest
|
|
23
|
+
AND m.scan_id = ?
|
|
24
|
+
AND NOT EXISTS (
|
|
25
|
+
SELECT 1
|
|
26
|
+
FROM temp_direct_target_roots dtr
|
|
27
|
+
WHERE dtr.root_digest = m.digest
|
|
28
|
+
)
|
|
29
|
+
),
|
|
30
|
+
retained_manifests AS (
|
|
31
|
+
SELECT
|
|
32
|
+
retained.digest
|
|
33
|
+
FROM retained_tagged_manifests retained
|
|
34
|
+
|
|
35
|
+
UNION
|
|
36
|
+
|
|
37
|
+
SELECT
|
|
38
|
+
mr.descendant_digest AS digest
|
|
39
|
+
FROM retained_tagged_manifests retained
|
|
40
|
+
CROSS JOIN manifest_reachability mr
|
|
41
|
+
WHERE mr.scan_id = ?
|
|
42
|
+
AND mr.ancestor_digest = retained.digest
|
|
43
|
+
AND mr.min_distance > 0
|
|
44
|
+
)
|
|
45
|
+
SELECT DISTINCT
|
|
46
|
+
dtr.root_digest
|
|
47
|
+
FROM temp_direct_target_roots dtr
|
|
48
|
+
WHERE dtr.root_manifest_kind = 'index_manifest'
|
|
49
|
+
AND EXISTS (
|
|
50
|
+
SELECT 1
|
|
51
|
+
FROM manifest_edges me
|
|
52
|
+
JOIN manifests child
|
|
53
|
+
ON child.scan_id = ?
|
|
54
|
+
AND child.digest = me.child_digest
|
|
55
|
+
WHERE me.scan_id = ?
|
|
56
|
+
AND me.parent_digest = dtr.root_digest
|
|
57
|
+
AND me.edge_kind = 'referrer'
|
|
58
|
+
AND child.manifest_kind = 'signature_manifest'
|
|
59
|
+
)
|
|
60
|
+
AND EXISTS (
|
|
61
|
+
SELECT 1
|
|
62
|
+
FROM manifest_edges me
|
|
63
|
+
JOIN manifests child
|
|
64
|
+
ON child.scan_id = ?
|
|
65
|
+
AND child.digest = me.child_digest
|
|
66
|
+
WHERE me.scan_id = ?
|
|
67
|
+
AND me.parent_digest = dtr.root_digest
|
|
68
|
+
AND me.edge_kind = 'image-child'
|
|
69
|
+
AND child.manifest_kind <> 'signature_manifest'
|
|
70
|
+
)
|
|
71
|
+
AND NOT EXISTS (
|
|
72
|
+
SELECT 1
|
|
73
|
+
FROM manifest_edges me
|
|
74
|
+
JOIN manifests child
|
|
75
|
+
ON child.scan_id = ?
|
|
76
|
+
AND child.digest = me.child_digest
|
|
77
|
+
WHERE me.scan_id = ?
|
|
78
|
+
AND me.parent_digest = dtr.root_digest
|
|
79
|
+
AND me.edge_kind = 'image-child'
|
|
80
|
+
AND child.manifest_kind <> 'signature_manifest'
|
|
81
|
+
AND child.digest NOT IN (
|
|
82
|
+
SELECT digest
|
|
83
|
+
FROM retained_manifests
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
`;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { PlannerSql } from "./_planner-sql.js";
|
|
2
|
+
import { type DeletePlanRoot, type PlanArtifacts } from "./_planner-types.js";
|
|
3
|
+
export declare class PlannerPlanArtifacts {
|
|
4
|
+
#private;
|
|
5
|
+
constructor(sql: PlannerSql);
|
|
6
|
+
build(scanId: number, directTargetRoots: DeletePlanRoot[]): PlanArtifacts;
|
|
7
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { mapBlockedRootRow, mapClosureManifestRow } from "./_planner-types.js";
|
|
2
|
+
import { _LIST_BLOCKED_ROOTS_SQL } from "./_planner-plan-artifacts-blocked-roots-sql.js";
|
|
3
|
+
import { _LIST_CLOSURE_MANIFESTS_SQL } from "./_planner-plan-artifacts-closure-sql.js";
|
|
4
|
+
import { _LIST_SUPPORTED_UNTAG_ONLY_ROOT_DIGESTS_SQL } from "./_planner-plan-artifacts-supported-untag-only-sql.js";
|
|
5
|
+
export class PlannerPlanArtifacts {
|
|
6
|
+
#sql;
|
|
7
|
+
constructor(sql) {
|
|
8
|
+
this.#sql = sql;
|
|
9
|
+
}
|
|
10
|
+
build(scanId, directTargetRoots) {
|
|
11
|
+
const deleteRootCandidates = directTargetRoots.filter((root) => root.selectionMode === "delete-root");
|
|
12
|
+
if (deleteRootCandidates.length === 0) {
|
|
13
|
+
return {
|
|
14
|
+
closureManifests: [],
|
|
15
|
+
blockedRoots: [],
|
|
16
|
+
fullyDeletableRoots: [],
|
|
17
|
+
supportedUntagOnlyRootDigests: new Set()
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
return this.#withDirectTargetRootsTempTable(deleteRootCandidates, () => {
|
|
21
|
+
const closureManifests = this.#listClosureManifests(scanId);
|
|
22
|
+
const blockedRoots = this.#listBlockedRoots(scanId);
|
|
23
|
+
const blockedVersionIds = new Set(blockedRoots.map((root) => root.blockedVersionId));
|
|
24
|
+
const fullyDeletableRoots = deleteRootCandidates.filter((root) => !blockedVersionIds.has(root.versionId));
|
|
25
|
+
const supportedUntagOnlyRootDigests = this.#listSupportedUntagOnlyRootDigests(scanId);
|
|
26
|
+
return {
|
|
27
|
+
closureManifests,
|
|
28
|
+
blockedRoots,
|
|
29
|
+
fullyDeletableRoots,
|
|
30
|
+
supportedUntagOnlyRootDigests
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
#listSupportedUntagOnlyRootDigests(scanId) {
|
|
35
|
+
const rows = this.#sql.all(_LIST_SUPPORTED_UNTAG_ONLY_ROOT_DIGESTS_SQL, [
|
|
36
|
+
scanId,
|
|
37
|
+
scanId,
|
|
38
|
+
scanId,
|
|
39
|
+
scanId,
|
|
40
|
+
scanId,
|
|
41
|
+
scanId,
|
|
42
|
+
scanId,
|
|
43
|
+
scanId,
|
|
44
|
+
scanId
|
|
45
|
+
]);
|
|
46
|
+
return new Set(rows.map((row) => row.root_digest));
|
|
47
|
+
}
|
|
48
|
+
#listClosureManifests(scanId) {
|
|
49
|
+
return this.#sql
|
|
50
|
+
.all(_LIST_CLOSURE_MANIFESTS_SQL, [scanId, scanId, scanId, scanId, scanId, scanId, scanId])
|
|
51
|
+
.map(mapClosureManifestRow);
|
|
52
|
+
}
|
|
53
|
+
#listBlockedRoots(scanId) {
|
|
54
|
+
return this.#sql
|
|
55
|
+
.all(_LIST_BLOCKED_ROOTS_SQL, [scanId, scanId, scanId, scanId, scanId])
|
|
56
|
+
.map(mapBlockedRootRow);
|
|
57
|
+
}
|
|
58
|
+
#withDirectTargetRootsTempTable(directTargetRoots, callback) {
|
|
59
|
+
this.#sql.exec(`
|
|
60
|
+
CREATE TEMP TABLE IF NOT EXISTS temp_direct_target_roots (
|
|
61
|
+
root_version_id INTEGER NOT NULL,
|
|
62
|
+
root_digest TEXT NOT NULL,
|
|
63
|
+
root_manifest_kind TEXT,
|
|
64
|
+
direct_target_reason TEXT NOT NULL,
|
|
65
|
+
selection_mode TEXT NOT NULL
|
|
66
|
+
)
|
|
67
|
+
`);
|
|
68
|
+
this.#sql.exec(`
|
|
69
|
+
CREATE INDEX IF NOT EXISTS idx_temp_direct_target_roots_digest
|
|
70
|
+
ON temp_direct_target_roots(root_digest)
|
|
71
|
+
`);
|
|
72
|
+
this.#sql.exec(`
|
|
73
|
+
CREATE INDEX IF NOT EXISTS idx_temp_direct_target_roots_version_digest
|
|
74
|
+
ON temp_direct_target_roots(root_version_id, root_digest)
|
|
75
|
+
`);
|
|
76
|
+
this.#sql.exec("DELETE FROM temp_direct_target_roots");
|
|
77
|
+
this.#insertDirectTargetRoots(directTargetRoots);
|
|
78
|
+
try {
|
|
79
|
+
return callback();
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
this.#sql.exec("DELETE FROM temp_direct_target_roots");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
#insertDirectTargetRoots(directTargetRoots) {
|
|
86
|
+
const insertSql = `
|
|
87
|
+
INSERT INTO temp_direct_target_roots (
|
|
88
|
+
root_version_id,
|
|
89
|
+
root_digest,
|
|
90
|
+
root_manifest_kind,
|
|
91
|
+
direct_target_reason,
|
|
92
|
+
selection_mode
|
|
93
|
+
) VALUES (?, ?, ?, ?, ?)
|
|
94
|
+
`;
|
|
95
|
+
this.#sql.traceSql(insertSql, ["<chunked rows omitted>"]);
|
|
96
|
+
const insert = this.#sql.database.prepare(insertSql);
|
|
97
|
+
const insertMany = this.#sql.database.transaction((roots) => {
|
|
98
|
+
for (const root of roots) {
|
|
99
|
+
insert.run(root.versionId, root.digest, root.manifestKind ?? null, root.reason, root.selectionMode);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
const chunkSize = 1000;
|
|
103
|
+
for (let index = 0; index < directTargetRoots.length; index += chunkSize) {
|
|
104
|
+
const chunk = directTargetRoots.slice(index, index + chunkSize);
|
|
105
|
+
insertMany(chunk);
|
|
106
|
+
this.#sql.logger.debug(`Inserted ${chunk.length} direct target root row(s) into temp_direct_target_roots`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type Database from "better-sqlite3";
|
|
2
|
+
import type { DeletePlan, PlannerLogger } from "./_planner-types.js";
|
|
3
|
+
export type { DeletePlan, DeletePlanBlockReasonCode, DeletePlanBlockedRoot, DeletePlanClosureManifest, DeletePlanProtectedRoot, DeletePlanRoot, DeletePlanRootDecision, DeletePlanSelectionMode, DeletePlanSelectionReason, DeletePlanValidationReasonCode, DeletePlanValidationStatus } from "./_planner-types.js";
|
|
4
|
+
export { DeletePlanValidationReasonCodes, DeletePlanValidationStatuses } from "./_planner-types.js";
|
|
5
|
+
export declare class PlannerRepository {
|
|
6
|
+
#private;
|
|
7
|
+
constructor(database: Database.Database, logger?: PlannerLogger);
|
|
8
|
+
getDeleteUntaggedPlan(owner: string, packageName: string): DeletePlan;
|
|
9
|
+
getLatestCompletedScanId(owner: string, packageName: string): number;
|
|
10
|
+
getKeepNUntaggedPlan(owner: string, packageName: string, keepCount: number): DeletePlan;
|
|
11
|
+
getDeleteUntaggedPlanWithCutoff(owner: string, packageName: string, options?: {
|
|
12
|
+
olderThan?: string;
|
|
13
|
+
cutoffTimestamp?: string;
|
|
14
|
+
}): DeletePlan;
|
|
15
|
+
getKeepNUntaggedPlanWithCutoff(owner: string, packageName: string, keepCount: number, options?: {
|
|
16
|
+
olderThan?: string;
|
|
17
|
+
cutoffTimestamp?: string;
|
|
18
|
+
}): DeletePlan;
|
|
19
|
+
getDeleteTagsPlan(owner: string, packageName: string, deleteTags: string[], excludeTags: string[]): DeletePlan;
|
|
20
|
+
getDeleteTagsPlanWithCutoff(owner: string, packageName: string, deleteTags: string[], excludeTags: string[], options?: {
|
|
21
|
+
deleteTagsRequested?: boolean;
|
|
22
|
+
deleteGhostImages?: boolean;
|
|
23
|
+
deletePartialImages?: boolean;
|
|
24
|
+
deleteOrphanedImages?: boolean;
|
|
25
|
+
keepNTagged?: number;
|
|
26
|
+
useRegex?: boolean;
|
|
27
|
+
olderThan?: string;
|
|
28
|
+
cutoffTimestamp?: string;
|
|
29
|
+
}): DeletePlan;
|
|
30
|
+
getCleanupPlanWithCutoff(owner: string, packageName: string, options?: {
|
|
31
|
+
deleteUntagged?: boolean;
|
|
32
|
+
deleteGhostImages?: boolean;
|
|
33
|
+
deletePartialImages?: boolean;
|
|
34
|
+
deleteOrphanedImages?: boolean;
|
|
35
|
+
deleteTags?: string[];
|
|
36
|
+
deleteTagsRequested?: boolean;
|
|
37
|
+
excludeTags?: string[];
|
|
38
|
+
keepNTagged?: number;
|
|
39
|
+
keepNUntagged?: number;
|
|
40
|
+
useRegex?: boolean;
|
|
41
|
+
olderThan?: string;
|
|
42
|
+
cutoffTimestamp?: string;
|
|
43
|
+
}): DeletePlan;
|
|
44
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { PlannerDirectTargetRoots } from "./_planner-direct-target-roots.js";
|
|
2
|
+
import { PlannerDirectTargetTags } from "./_planner-direct-target-tags.js";
|
|
3
|
+
import { PlannerLatestScan } from "./_planner-latest-scan.js";
|
|
4
|
+
import { buildPlanOutputs } from "./_planner-output.js";
|
|
5
|
+
import { PlannerPlanArtifacts } from "./_planner-plan-artifacts.js";
|
|
6
|
+
import { PlannerSql } from "./_planner-sql.js";
|
|
7
|
+
export { DeletePlanValidationReasonCodes, DeletePlanValidationStatuses } from "./_planner-types.js";
|
|
8
|
+
export class PlannerRepository {
|
|
9
|
+
#latestScan;
|
|
10
|
+
#directTargetTags;
|
|
11
|
+
#directTargetRoots;
|
|
12
|
+
#planArtifacts;
|
|
13
|
+
constructor(database, logger) {
|
|
14
|
+
const sql = new PlannerSql(database, logger);
|
|
15
|
+
this.#latestScan = new PlannerLatestScan(sql);
|
|
16
|
+
this.#directTargetTags = new PlannerDirectTargetTags(sql);
|
|
17
|
+
this.#directTargetRoots = new PlannerDirectTargetRoots(sql);
|
|
18
|
+
this.#planArtifacts = new PlannerPlanArtifacts(sql);
|
|
19
|
+
}
|
|
20
|
+
getDeleteUntaggedPlan(owner, packageName) {
|
|
21
|
+
return this.getDeleteUntaggedPlanWithCutoff(owner, packageName);
|
|
22
|
+
}
|
|
23
|
+
getLatestCompletedScanId(owner, packageName) {
|
|
24
|
+
return this.#latestScan.get(owner, packageName).scan_id;
|
|
25
|
+
}
|
|
26
|
+
getKeepNUntaggedPlan(owner, packageName, keepCount) {
|
|
27
|
+
return this.getKeepNUntaggedPlanWithCutoff(owner, packageName, keepCount);
|
|
28
|
+
}
|
|
29
|
+
getDeleteUntaggedPlanWithCutoff(owner, packageName, options) {
|
|
30
|
+
return this.getCleanupPlanWithCutoff(owner, packageName, {
|
|
31
|
+
deleteUntagged: true,
|
|
32
|
+
olderThan: options?.olderThan,
|
|
33
|
+
cutoffTimestamp: options?.cutoffTimestamp
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
getKeepNUntaggedPlanWithCutoff(owner, packageName, keepCount, options) {
|
|
37
|
+
return this.getCleanupPlanWithCutoff(owner, packageName, {
|
|
38
|
+
keepNUntagged: keepCount,
|
|
39
|
+
olderThan: options?.olderThan,
|
|
40
|
+
cutoffTimestamp: options?.cutoffTimestamp
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
getDeleteTagsPlan(owner, packageName, deleteTags, excludeTags) {
|
|
44
|
+
return this.getDeleteTagsPlanWithCutoff(owner, packageName, deleteTags, excludeTags);
|
|
45
|
+
}
|
|
46
|
+
getDeleteTagsPlanWithCutoff(owner, packageName, deleteTags, excludeTags, options) {
|
|
47
|
+
return this.getCleanupPlanWithCutoff(owner, packageName, {
|
|
48
|
+
deleteTags,
|
|
49
|
+
excludeTags,
|
|
50
|
+
deleteGhostImages: options?.deleteGhostImages,
|
|
51
|
+
deletePartialImages: options?.deletePartialImages,
|
|
52
|
+
deleteOrphanedImages: options?.deleteOrphanedImages,
|
|
53
|
+
deleteTagsRequested: options?.deleteTagsRequested ?? true,
|
|
54
|
+
keepNTagged: options?.keepNTagged,
|
|
55
|
+
useRegex: options?.useRegex,
|
|
56
|
+
olderThan: options?.olderThan,
|
|
57
|
+
cutoffTimestamp: options?.cutoffTimestamp
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
getCleanupPlanWithCutoff(owner, packageName, options) {
|
|
61
|
+
const scan = this.#latestScan.get(owner, packageName);
|
|
62
|
+
const deleteTags = options?.deleteTags ?? [];
|
|
63
|
+
const excludeTags = options?.excludeTags ?? [];
|
|
64
|
+
const directTargetTags = this.#directTargetTags.listDeleteTagDirectTargetTags(scan.scan_id, deleteTags, excludeTags, options?.useRegex ?? false, options?.deleteOrphanedImages ?? false, options?.cutoffTimestamp);
|
|
65
|
+
const directTargetRoots = this.#directTargetRoots.list(scan.scan_id, {
|
|
66
|
+
deleteTags,
|
|
67
|
+
deleteTagsRequested: options?.deleteTagsRequested ?? false,
|
|
68
|
+
deleteOrphanedImages: options?.deleteOrphanedImages ?? false,
|
|
69
|
+
excludeTags,
|
|
70
|
+
deleteUntagged: options?.deleteUntagged ?? false,
|
|
71
|
+
keepNTagged: options?.keepNTagged,
|
|
72
|
+
keepNUntagged: options?.keepNUntagged,
|
|
73
|
+
useRegex: options?.useRegex ?? false,
|
|
74
|
+
cutoffTimestamp: options?.cutoffTimestamp
|
|
75
|
+
});
|
|
76
|
+
const planArtifacts = this.#planArtifacts.build(scan.scan_id, directTargetRoots);
|
|
77
|
+
return {
|
|
78
|
+
owner: scan.owner,
|
|
79
|
+
packageName: scan.package_name,
|
|
80
|
+
scanCompletedAt: scan.scan_completed_at,
|
|
81
|
+
plannerInputs: _buildPlannerInputs({
|
|
82
|
+
deleteGhostImages: options?.deleteGhostImages || undefined,
|
|
83
|
+
deletePartialImages: options?.deletePartialImages || undefined,
|
|
84
|
+
deleteOrphanedImages: options?.deleteOrphanedImages || undefined,
|
|
85
|
+
deleteTags,
|
|
86
|
+
deleteUntagged: options?.deleteUntagged || undefined,
|
|
87
|
+
excludeTags,
|
|
88
|
+
keepNTagged: options?.keepNTagged,
|
|
89
|
+
keepNUntagged: options?.keepNUntagged,
|
|
90
|
+
useRegex: options?.useRegex || undefined,
|
|
91
|
+
olderThan: options?.olderThan,
|
|
92
|
+
cutoffTimestamp: options?.cutoffTimestamp
|
|
93
|
+
}),
|
|
94
|
+
...buildPlanOutputs(directTargetTags, directTargetRoots, planArtifacts)
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function _buildPlannerInputs(inputs) {
|
|
99
|
+
return Object.fromEntries(Object.entries(inputs).filter(([, value]) => {
|
|
100
|
+
if (value === undefined) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
return !(Array.isArray(value) && value.length === 0);
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type Database from "better-sqlite3";
|
|
2
|
+
import { type PlannerLogger } from "./_planner-types.js";
|
|
3
|
+
export declare class PlannerSql {
|
|
4
|
+
#private;
|
|
5
|
+
constructor(database: Database.Database, logger?: PlannerLogger);
|
|
6
|
+
get database(): Database.Database;
|
|
7
|
+
get logger(): PlannerLogger;
|
|
8
|
+
exec(sql: string, params?: Array<number | string | null>): void;
|
|
9
|
+
get<T>(sql: string, params: Array<number | string>): T | undefined;
|
|
10
|
+
all<T>(sql: string, params: Array<number | string>): T[];
|
|
11
|
+
traceSql(sql: string, params: Array<number | string | null>): void;
|
|
12
|
+
}
|