ghcr-manager 0.9.4 → 0.9.6
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 +31 -0
- package/README.md +9 -9
- package/dist/cleanup-summary/_cleanup-summary-markdown.js +7 -6
- package/dist/cleanup-summary/_cleanup-summary.d.ts +8 -4
- package/dist/cleanup-summary/_cleanup-summary.js +8 -4
- package/dist/cleanup-summary/index.d.ts +1 -1
- package/dist/cli/_cleanup-command.js +22 -2
- package/dist/cli/_tag-selector-resolver.js +2 -2
- package/dist/core/_digest-tag.d.ts +2 -0
- package/dist/core/_digest-tag.js +12 -0
- package/dist/core/_types.d.ts +2 -1
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.js +1 -0
- package/dist/db/_cleanup-run-writer.js +69 -50
- package/dist/db/_db-merge-cleanup-copy.js +128 -80
- package/dist/db/_db-merge-scan-copy.js +1 -1
- package/dist/db/_manifest-reachability.js +25 -0
- package/dist/db/_scan-writer.js +118 -116
- package/dist/db/index.d.ts +1 -1
- package/dist/db/planner/_planner-direct-target-roots.js +2 -0
- package/dist/db/planner/_planner-direct-target-tags.js +5 -0
- package/dist/db/planner/_planner-output.d.ts +1 -1
- package/dist/db/planner/_planner-output.js +0 -11
- package/dist/db/planner/_planner-repository.d.ts +1 -1
- package/dist/db/planner/_planner-types.d.ts +12 -18
- package/dist/db/planner/index.d.ts +1 -1
- package/package.json +1 -1
- package/resources/sql/schema/001_schema.sql +20 -0
- package/resources/sql/views/003_v_scan_root_manifests.sql +1 -0
- package/resources/sql/views/{004_v_digest_derived_tag_relations.sql → 004_v_digest_tag_relations.sql} +7 -8
- package/resources/sql/views/005_v_cleanup_root_closure_members.sql +2 -1
- package/resources/sql/views/007_v_cleanup_root_decision_readable.sql +67 -0
|
@@ -1,7 +1,30 @@
|
|
|
1
1
|
export class DbMergeCleanupCopy {
|
|
2
2
|
#database;
|
|
3
|
+
#insertCleanupRunStatement;
|
|
4
|
+
#copyRootDecisionsStatementByAttachName = new Map();
|
|
5
|
+
#copySelectedTagsStatementByAttachName = new Map();
|
|
6
|
+
#copyProtectedRootBlocksStatementByAttachName = new Map();
|
|
7
|
+
#listCleanupUuidsStatementByTableName = new Map();
|
|
3
8
|
constructor(database) {
|
|
4
9
|
this.#database = database;
|
|
10
|
+
this.#insertCleanupRunStatement = this.#database.prepare(`
|
|
11
|
+
INSERT INTO cleanup_runs(
|
|
12
|
+
scan_id,
|
|
13
|
+
cleanup_uuid,
|
|
14
|
+
cleanup_started_at,
|
|
15
|
+
github_actions_run_url,
|
|
16
|
+
dry_run,
|
|
17
|
+
planner_inputs_json,
|
|
18
|
+
direct_target_tag_count,
|
|
19
|
+
direct_target_root_count,
|
|
20
|
+
delete_root_candidate_count,
|
|
21
|
+
untag_only_root_count,
|
|
22
|
+
fully_deletable_root_count,
|
|
23
|
+
blocked_delete_root_count,
|
|
24
|
+
protected_root_count
|
|
25
|
+
)
|
|
26
|
+
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
27
|
+
`);
|
|
5
28
|
}
|
|
6
29
|
copyCleanupRuns(attachName, sourceScanId, targetScanId, existingCleanupUuids) {
|
|
7
30
|
const rows = this.#database
|
|
@@ -31,92 +54,117 @@ export class DbMergeCleanupCopy {
|
|
|
31
54
|
if (knownCleanupUuids.has(row.cleanup_uuid)) {
|
|
32
55
|
continue;
|
|
33
56
|
}
|
|
34
|
-
const cleanupRunId = Number(this.#
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
cleanup_uuid,
|
|
39
|
-
cleanup_started_at,
|
|
40
|
-
github_actions_run_url,
|
|
41
|
-
dry_run,
|
|
42
|
-
planner_inputs_json,
|
|
43
|
-
direct_target_tag_count,
|
|
44
|
-
direct_target_root_count,
|
|
45
|
-
delete_root_candidate_count,
|
|
46
|
-
untag_only_root_count,
|
|
47
|
-
fully_deletable_root_count,
|
|
48
|
-
blocked_delete_root_count,
|
|
49
|
-
protected_root_count
|
|
50
|
-
)
|
|
51
|
-
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
52
|
-
`)
|
|
53
|
-
.run(targetScanId, row.cleanup_uuid, row.cleanup_started_at, row.github_actions_run_url, row.dry_run, row.planner_inputs_json, row.direct_target_tag_count, row.direct_target_root_count, row.delete_root_candidate_count, row.untag_only_root_count, row.fully_deletable_root_count, row.blocked_delete_root_count, row.protected_root_count).lastInsertRowid);
|
|
54
|
-
this.#database
|
|
55
|
-
.prepare(`
|
|
56
|
-
INSERT INTO cleanup_root_decisions(
|
|
57
|
-
cleanup_run_id,
|
|
58
|
-
scan_id,
|
|
59
|
-
digest,
|
|
60
|
-
selection_mode,
|
|
61
|
-
selection_reason,
|
|
62
|
-
validation_status,
|
|
63
|
-
validation_reason_code,
|
|
64
|
-
validation_reason,
|
|
65
|
-
blocking_digest,
|
|
66
|
-
overlap_digest
|
|
67
|
-
)
|
|
68
|
-
SELECT
|
|
69
|
-
?,
|
|
70
|
-
?,
|
|
71
|
-
digest,
|
|
72
|
-
selection_mode,
|
|
73
|
-
selection_reason,
|
|
74
|
-
validation_status,
|
|
75
|
-
validation_reason_code,
|
|
76
|
-
validation_reason,
|
|
77
|
-
blocking_digest,
|
|
78
|
-
overlap_digest
|
|
79
|
-
FROM ${attachName}.cleanup_root_decisions
|
|
80
|
-
WHERE cleanup_run_id = ?
|
|
81
|
-
AND scan_id = ?
|
|
82
|
-
`)
|
|
83
|
-
.run(cleanupRunId, targetScanId, row.cleanup_run_id, sourceScanId);
|
|
84
|
-
this.#database
|
|
85
|
-
.prepare(`
|
|
86
|
-
INSERT INTO cleanup_protected_root_blocks(
|
|
87
|
-
cleanup_run_id,
|
|
88
|
-
scan_id,
|
|
89
|
-
protected_digest,
|
|
90
|
-
blocked_digest,
|
|
91
|
-
block_reason_code,
|
|
92
|
-
overlap_digest
|
|
93
|
-
)
|
|
94
|
-
SELECT
|
|
95
|
-
?,
|
|
96
|
-
?,
|
|
97
|
-
protected_digest,
|
|
98
|
-
blocked_digest,
|
|
99
|
-
block_reason_code,
|
|
100
|
-
overlap_digest
|
|
101
|
-
FROM ${attachName}.cleanup_protected_root_blocks
|
|
102
|
-
WHERE cleanup_run_id = ?
|
|
103
|
-
AND scan_id = ?
|
|
104
|
-
`)
|
|
105
|
-
.run(cleanupRunId, targetScanId, row.cleanup_run_id, sourceScanId);
|
|
57
|
+
const cleanupRunId = Number(this.#insertCleanupRunStatement.run(targetScanId, row.cleanup_uuid, row.cleanup_started_at, row.github_actions_run_url, row.dry_run, row.planner_inputs_json, row.direct_target_tag_count, row.direct_target_root_count, row.delete_root_candidate_count, row.untag_only_root_count, row.fully_deletable_root_count, row.blocked_delete_root_count, row.protected_root_count).lastInsertRowid);
|
|
58
|
+
this.#copyRootDecisionsStatement(attachName).run(cleanupRunId, targetScanId, row.cleanup_run_id, sourceScanId);
|
|
59
|
+
this.#copySelectedTagsStatement(attachName).run(cleanupRunId, targetScanId, row.cleanup_run_id, sourceScanId);
|
|
60
|
+
this.#copyProtectedRootBlocksStatement(attachName).run(cleanupRunId, targetScanId, row.cleanup_run_id, sourceScanId);
|
|
106
61
|
knownCleanupUuids.add(row.cleanup_uuid);
|
|
107
62
|
importedCleanupRunCount += 1;
|
|
108
63
|
}
|
|
109
64
|
return importedCleanupRunCount;
|
|
110
65
|
}
|
|
111
66
|
listCleanupUuids(tableName, scanId) {
|
|
112
|
-
const rows = this.#
|
|
113
|
-
.prepare(`
|
|
114
|
-
SELECT cleanup_uuid
|
|
115
|
-
FROM ${tableName}
|
|
116
|
-
WHERE scan_id = ?
|
|
117
|
-
ORDER BY cleanup_run_id
|
|
118
|
-
`)
|
|
119
|
-
.all(scanId);
|
|
67
|
+
const rows = this.#listCleanupUuidsStatement(tableName).all(scanId);
|
|
120
68
|
return rows.map((row) => row.cleanup_uuid);
|
|
121
69
|
}
|
|
70
|
+
#copyRootDecisionsStatement(attachName) {
|
|
71
|
+
const cached = this.#copyRootDecisionsStatementByAttachName.get(attachName);
|
|
72
|
+
if (cached) {
|
|
73
|
+
return cached;
|
|
74
|
+
}
|
|
75
|
+
const statement = this.#database.prepare(`
|
|
76
|
+
INSERT INTO cleanup_root_decisions(
|
|
77
|
+
cleanup_run_id,
|
|
78
|
+
scan_id,
|
|
79
|
+
digest,
|
|
80
|
+
selection_mode,
|
|
81
|
+
selection_reason,
|
|
82
|
+
validation_status,
|
|
83
|
+
validation_reason_code,
|
|
84
|
+
validation_reason,
|
|
85
|
+
blocking_digest,
|
|
86
|
+
overlap_digest
|
|
87
|
+
)
|
|
88
|
+
SELECT
|
|
89
|
+
?,
|
|
90
|
+
?,
|
|
91
|
+
digest,
|
|
92
|
+
selection_mode,
|
|
93
|
+
selection_reason,
|
|
94
|
+
validation_status,
|
|
95
|
+
validation_reason_code,
|
|
96
|
+
validation_reason,
|
|
97
|
+
blocking_digest,
|
|
98
|
+
overlap_digest
|
|
99
|
+
FROM ${attachName}.cleanup_root_decisions
|
|
100
|
+
WHERE cleanup_run_id = ?
|
|
101
|
+
AND scan_id = ?
|
|
102
|
+
`);
|
|
103
|
+
this.#copyRootDecisionsStatementByAttachName.set(attachName, statement);
|
|
104
|
+
return statement;
|
|
105
|
+
}
|
|
106
|
+
#copySelectedTagsStatement(attachName) {
|
|
107
|
+
const cached = this.#copySelectedTagsStatementByAttachName.get(attachName);
|
|
108
|
+
if (cached) {
|
|
109
|
+
return cached;
|
|
110
|
+
}
|
|
111
|
+
const statement = this.#database.prepare(`
|
|
112
|
+
INSERT INTO cleanup_selected_tags(
|
|
113
|
+
cleanup_run_id,
|
|
114
|
+
scan_id,
|
|
115
|
+
tag
|
|
116
|
+
)
|
|
117
|
+
SELECT
|
|
118
|
+
?,
|
|
119
|
+
?,
|
|
120
|
+
tag
|
|
121
|
+
FROM ${attachName}.cleanup_selected_tags
|
|
122
|
+
WHERE cleanup_run_id = ?
|
|
123
|
+
AND scan_id = ?
|
|
124
|
+
`);
|
|
125
|
+
this.#copySelectedTagsStatementByAttachName.set(attachName, statement);
|
|
126
|
+
return statement;
|
|
127
|
+
}
|
|
128
|
+
#copyProtectedRootBlocksStatement(attachName) {
|
|
129
|
+
const cached = this.#copyProtectedRootBlocksStatementByAttachName.get(attachName);
|
|
130
|
+
if (cached) {
|
|
131
|
+
return cached;
|
|
132
|
+
}
|
|
133
|
+
const statement = this.#database.prepare(`
|
|
134
|
+
INSERT INTO cleanup_protected_root_blocks(
|
|
135
|
+
cleanup_run_id,
|
|
136
|
+
scan_id,
|
|
137
|
+
protected_digest,
|
|
138
|
+
blocked_digest,
|
|
139
|
+
block_reason_code,
|
|
140
|
+
overlap_digest
|
|
141
|
+
)
|
|
142
|
+
SELECT
|
|
143
|
+
?,
|
|
144
|
+
?,
|
|
145
|
+
protected_digest,
|
|
146
|
+
blocked_digest,
|
|
147
|
+
block_reason_code,
|
|
148
|
+
overlap_digest
|
|
149
|
+
FROM ${attachName}.cleanup_protected_root_blocks
|
|
150
|
+
WHERE cleanup_run_id = ?
|
|
151
|
+
AND scan_id = ?
|
|
152
|
+
`);
|
|
153
|
+
this.#copyProtectedRootBlocksStatementByAttachName.set(attachName, statement);
|
|
154
|
+
return statement;
|
|
155
|
+
}
|
|
156
|
+
#listCleanupUuidsStatement(tableName) {
|
|
157
|
+
const cached = this.#listCleanupUuidsStatementByTableName.get(tableName);
|
|
158
|
+
if (cached) {
|
|
159
|
+
return cached;
|
|
160
|
+
}
|
|
161
|
+
const statement = this.#database.prepare(`
|
|
162
|
+
SELECT cleanup_uuid
|
|
163
|
+
FROM ${tableName}
|
|
164
|
+
WHERE scan_id = ?
|
|
165
|
+
ORDER BY cleanup_run_id
|
|
166
|
+
`);
|
|
167
|
+
this.#listCleanupUuidsStatementByTableName.set(tableName, statement);
|
|
168
|
+
return statement;
|
|
169
|
+
}
|
|
122
170
|
}
|
|
@@ -25,7 +25,7 @@ export class DbMergeScanCopy {
|
|
|
25
25
|
const copySpecs = [
|
|
26
26
|
"package_versions(scan_id, version_id, created_at, updated_at)",
|
|
27
27
|
"package_version_payloads(scan_id, version_id, raw_json)",
|
|
28
|
-
"tags(scan_id, tag, version_id)",
|
|
28
|
+
"tags(scan_id, tag, version_id, is_digest_tag)",
|
|
29
29
|
"manifests(scan_id, version_id, digest, media_type, artifact_type, config_media_type, subject_digest, annotations_json, platform_os, platform_architecture, platform_variant, manifest_kind)",
|
|
30
30
|
"manifest_descriptors(scan_id, parent_digest, child_digest, media_type, artifact_type, platform_os, platform_architecture, platform_variant)",
|
|
31
31
|
"manifest_payloads(scan_id, digest, raw_json)",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export function rebuildManifestReachability(database, scanId) {
|
|
2
|
+
_refreshDigestTagEdges(database, scanId);
|
|
2
3
|
const manifestDigests = _loadManifestDigests(database, scanId);
|
|
3
4
|
const childDigestsByParent = new Map();
|
|
4
5
|
const parentDigestsByChild = new Map();
|
|
@@ -70,6 +71,30 @@ export function rebuildManifestReachability(database, scanId) {
|
|
|
70
71
|
});
|
|
71
72
|
rebuild();
|
|
72
73
|
}
|
|
74
|
+
function _refreshDigestTagEdges(database, scanId) {
|
|
75
|
+
database.prepare("DELETE FROM manifest_edges WHERE scan_id = ? AND edge_kind = 'digest-tag-referrer'").run(scanId);
|
|
76
|
+
database
|
|
77
|
+
.prepare(`
|
|
78
|
+
INSERT OR IGNORE INTO manifest_edges(scan_id, parent_digest, child_digest, edge_kind)
|
|
79
|
+
SELECT
|
|
80
|
+
t.scan_id,
|
|
81
|
+
'sha256:' || SUBSTR(t.tag, 8, 64) AS parent_digest,
|
|
82
|
+
m.digest AS child_digest,
|
|
83
|
+
'digest-tag-referrer' AS edge_kind
|
|
84
|
+
FROM tags t
|
|
85
|
+
JOIN manifests m
|
|
86
|
+
ON m.scan_id = t.scan_id
|
|
87
|
+
AND m.version_id = t.version_id
|
|
88
|
+
JOIN manifests parent_manifest
|
|
89
|
+
ON parent_manifest.scan_id = t.scan_id
|
|
90
|
+
AND parent_manifest.digest = 'sha256:' || SUBSTR(t.tag, 8, 64)
|
|
91
|
+
WHERE t.scan_id = ?
|
|
92
|
+
AND t.tag LIKE 'sha256-%'
|
|
93
|
+
AND LENGTH(t.tag) >= 71
|
|
94
|
+
AND SUBSTR(t.tag, 8, 64) NOT GLOB '*[^0-9A-Fa-f]*'
|
|
95
|
+
`)
|
|
96
|
+
.run(scanId);
|
|
97
|
+
}
|
|
73
98
|
function _loadManifestDigests(database, scanId) {
|
|
74
99
|
const rows = database
|
|
75
100
|
.prepare("SELECT digest FROM manifests WHERE scan_id = ? ORDER BY digest")
|
package/dist/db/_scan-writer.js
CHANGED
|
@@ -1,55 +1,130 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { isDigestTag } from "../core/index.js";
|
|
2
3
|
import { resolveGitHubActionsRunUrl } from "./_github-actions-run-url.js";
|
|
3
4
|
import { rebuildManifestReachability } from "./_manifest-reachability.js";
|
|
4
5
|
export class ScanWriter {
|
|
5
6
|
#database;
|
|
7
|
+
#startScanStatement;
|
|
8
|
+
#markScanCompletedStatement;
|
|
9
|
+
#markScanFailedStatement;
|
|
10
|
+
#insertPackageVersionStatement;
|
|
11
|
+
#insertPackageVersionPayloadStatement;
|
|
12
|
+
#insertTagStatement;
|
|
13
|
+
#insertManifestStatement;
|
|
14
|
+
#insertManifestPayloadStatement;
|
|
15
|
+
#insertManifestDescriptorStatement;
|
|
16
|
+
#insertManifestEdgeStatement;
|
|
6
17
|
#activeScanId = null;
|
|
7
18
|
constructor(database) {
|
|
8
19
|
this.#database = database;
|
|
20
|
+
this.#startScanStatement = this.#database.prepare(`
|
|
21
|
+
INSERT INTO package_scans(
|
|
22
|
+
scan_uuid,
|
|
23
|
+
owner,
|
|
24
|
+
package_name,
|
|
25
|
+
package_metadata_json,
|
|
26
|
+
github_actions_run_url,
|
|
27
|
+
scan_started_at,
|
|
28
|
+
scan_completed_at,
|
|
29
|
+
status
|
|
30
|
+
)
|
|
31
|
+
VALUES(?, ?, ?, ?, ?, ?, NULL, 'running')
|
|
32
|
+
`);
|
|
33
|
+
this.#markScanCompletedStatement = this.#database.prepare(`
|
|
34
|
+
UPDATE package_scans
|
|
35
|
+
SET scan_completed_at = ?, status = 'completed'
|
|
36
|
+
WHERE scan_id = ?
|
|
37
|
+
`);
|
|
38
|
+
this.#markScanFailedStatement = this.#database.prepare(`
|
|
39
|
+
UPDATE package_scans
|
|
40
|
+
SET scan_completed_at = ?, status = 'failed'
|
|
41
|
+
WHERE scan_id = ?
|
|
42
|
+
`);
|
|
43
|
+
this.#insertPackageVersionStatement = this.#database.prepare(`
|
|
44
|
+
INSERT OR REPLACE INTO package_versions(scan_id, version_id, created_at, updated_at)
|
|
45
|
+
VALUES(@scanId, @versionId, @createdAt, @updatedAt)
|
|
46
|
+
`);
|
|
47
|
+
this.#insertPackageVersionPayloadStatement = this.#database.prepare(`
|
|
48
|
+
INSERT OR REPLACE INTO package_version_payloads(scan_id, version_id, raw_json)
|
|
49
|
+
VALUES(?, ?, ?)
|
|
50
|
+
`);
|
|
51
|
+
this.#insertTagStatement = this.#database.prepare(`
|
|
52
|
+
INSERT OR REPLACE INTO tags(scan_id, tag, version_id, is_digest_tag)
|
|
53
|
+
VALUES(@scanId, @tag, @versionId, @isDigestTag)
|
|
54
|
+
`);
|
|
55
|
+
this.#insertManifestStatement = this.#database.prepare(`
|
|
56
|
+
INSERT OR REPLACE INTO manifests(
|
|
57
|
+
scan_id,
|
|
58
|
+
version_id,
|
|
59
|
+
digest,
|
|
60
|
+
media_type,
|
|
61
|
+
artifact_type,
|
|
62
|
+
config_media_type,
|
|
63
|
+
subject_digest,
|
|
64
|
+
annotations_json,
|
|
65
|
+
platform_os,
|
|
66
|
+
platform_architecture,
|
|
67
|
+
platform_variant,
|
|
68
|
+
manifest_kind
|
|
69
|
+
)
|
|
70
|
+
VALUES(
|
|
71
|
+
@scanId,
|
|
72
|
+
@versionId,
|
|
73
|
+
@digest,
|
|
74
|
+
@mediaType,
|
|
75
|
+
@artifactType,
|
|
76
|
+
@configMediaType,
|
|
77
|
+
@subjectDigest,
|
|
78
|
+
@annotationsJson,
|
|
79
|
+
@platformOs,
|
|
80
|
+
@platformArchitecture,
|
|
81
|
+
@platformVariant,
|
|
82
|
+
@manifestKind
|
|
83
|
+
)
|
|
84
|
+
`);
|
|
85
|
+
this.#insertManifestPayloadStatement = this.#database.prepare(`
|
|
86
|
+
INSERT OR REPLACE INTO manifest_payloads(scan_id, digest, raw_json)
|
|
87
|
+
VALUES(?, ?, ?)
|
|
88
|
+
`);
|
|
89
|
+
this.#insertManifestDescriptorStatement = this.#database.prepare(`
|
|
90
|
+
INSERT OR REPLACE INTO manifest_descriptors(
|
|
91
|
+
scan_id,
|
|
92
|
+
parent_digest,
|
|
93
|
+
child_digest,
|
|
94
|
+
media_type,
|
|
95
|
+
artifact_type,
|
|
96
|
+
platform_os,
|
|
97
|
+
platform_architecture,
|
|
98
|
+
platform_variant
|
|
99
|
+
)
|
|
100
|
+
VALUES(
|
|
101
|
+
@scanId,
|
|
102
|
+
@parentDigest,
|
|
103
|
+
@childDigest,
|
|
104
|
+
@mediaType,
|
|
105
|
+
@artifactType,
|
|
106
|
+
@platformOs,
|
|
107
|
+
@platformArchitecture,
|
|
108
|
+
@platformVariant
|
|
109
|
+
)
|
|
110
|
+
`);
|
|
111
|
+
this.#insertManifestEdgeStatement = this.#database.prepare(`
|
|
112
|
+
INSERT OR IGNORE INTO manifest_edges(scan_id, parent_digest, child_digest, edge_kind)
|
|
113
|
+
VALUES(@scanId, @parentDigest, @childDigest, @edgeKind)
|
|
114
|
+
`);
|
|
9
115
|
}
|
|
10
116
|
startScan(owner, packageName, scanStartedAt, packageMetadata) {
|
|
11
|
-
const result = this.#
|
|
12
|
-
.prepare(`
|
|
13
|
-
INSERT INTO package_scans(
|
|
14
|
-
scan_uuid,
|
|
15
|
-
owner,
|
|
16
|
-
package_name,
|
|
17
|
-
package_metadata_json,
|
|
18
|
-
github_actions_run_url,
|
|
19
|
-
scan_started_at,
|
|
20
|
-
scan_completed_at,
|
|
21
|
-
status
|
|
22
|
-
)
|
|
23
|
-
VALUES(?, ?, ?, ?, ?, ?, NULL, 'running')
|
|
24
|
-
`)
|
|
25
|
-
.run(randomUUID(), owner, packageName, packageMetadata.rawJson, resolveGitHubActionsRunUrl(), scanStartedAt);
|
|
117
|
+
const result = this.#startScanStatement.run(randomUUID(), owner, packageName, packageMetadata.rawJson, resolveGitHubActionsRunUrl(), scanStartedAt);
|
|
26
118
|
this.#activeScanId = Number(result.lastInsertRowid);
|
|
27
119
|
}
|
|
28
120
|
markScanCompleted(scanCompletedAt) {
|
|
29
|
-
this.#
|
|
30
|
-
.prepare(`
|
|
31
|
-
UPDATE package_scans
|
|
32
|
-
SET scan_completed_at = ?, status = 'completed'
|
|
33
|
-
WHERE scan_id = ?
|
|
34
|
-
`)
|
|
35
|
-
.run(scanCompletedAt, this.#requireScanId());
|
|
121
|
+
this.#markScanCompletedStatement.run(scanCompletedAt, this.#requireScanId());
|
|
36
122
|
}
|
|
37
123
|
markScanFailed(scanCompletedAt) {
|
|
38
|
-
this.#
|
|
39
|
-
.prepare(`
|
|
40
|
-
UPDATE package_scans
|
|
41
|
-
SET scan_completed_at = ?, status = 'failed'
|
|
42
|
-
WHERE scan_id = ?
|
|
43
|
-
`)
|
|
44
|
-
.run(scanCompletedAt, this.#requireScanId());
|
|
124
|
+
this.#markScanFailedStatement.run(scanCompletedAt, this.#requireScanId());
|
|
45
125
|
}
|
|
46
126
|
insertPackageVersion(version) {
|
|
47
|
-
this.#
|
|
48
|
-
.prepare(`
|
|
49
|
-
INSERT OR REPLACE INTO package_versions(scan_id, version_id, created_at, updated_at)
|
|
50
|
-
VALUES(@scanId, @versionId, @createdAt, @updatedAt)
|
|
51
|
-
`)
|
|
52
|
-
.run({
|
|
127
|
+
this.#insertPackageVersionStatement.run({
|
|
53
128
|
scanId: this.#requireScanId(),
|
|
54
129
|
versionId: version.versionId,
|
|
55
130
|
createdAt: version.createdAt,
|
|
@@ -57,57 +132,17 @@ export class ScanWriter {
|
|
|
57
132
|
});
|
|
58
133
|
}
|
|
59
134
|
insertPackageVersionPayload(versionId, rawJson) {
|
|
60
|
-
this.#
|
|
61
|
-
.prepare(`
|
|
62
|
-
INSERT OR REPLACE INTO package_version_payloads(scan_id, version_id, raw_json)
|
|
63
|
-
VALUES(?, ?, ?)
|
|
64
|
-
`)
|
|
65
|
-
.run(this.#requireScanId(), versionId, rawJson);
|
|
135
|
+
this.#insertPackageVersionPayloadStatement.run(this.#requireScanId(), versionId, rawJson);
|
|
66
136
|
}
|
|
67
137
|
insertTag(tag) {
|
|
68
|
-
this.#
|
|
69
|
-
.prepare(`
|
|
70
|
-
INSERT OR REPLACE INTO tags(scan_id, tag, version_id)
|
|
71
|
-
VALUES(@scanId, @tag, @versionId)
|
|
72
|
-
`)
|
|
73
|
-
.run({
|
|
138
|
+
this.#insertTagStatement.run({
|
|
74
139
|
scanId: this.#requireScanId(),
|
|
75
|
-
...tag
|
|
140
|
+
...tag,
|
|
141
|
+
isDigestTag: isDigestTag(tag.tag) ? 1 : 0
|
|
76
142
|
});
|
|
77
143
|
}
|
|
78
144
|
insertManifest(manifest) {
|
|
79
|
-
this.#
|
|
80
|
-
.prepare(`
|
|
81
|
-
INSERT OR REPLACE INTO manifests(
|
|
82
|
-
scan_id,
|
|
83
|
-
version_id,
|
|
84
|
-
digest,
|
|
85
|
-
media_type,
|
|
86
|
-
artifact_type,
|
|
87
|
-
config_media_type,
|
|
88
|
-
subject_digest,
|
|
89
|
-
annotations_json,
|
|
90
|
-
platform_os,
|
|
91
|
-
platform_architecture,
|
|
92
|
-
platform_variant,
|
|
93
|
-
manifest_kind
|
|
94
|
-
)
|
|
95
|
-
VALUES(
|
|
96
|
-
@scanId,
|
|
97
|
-
@versionId,
|
|
98
|
-
@digest,
|
|
99
|
-
@mediaType,
|
|
100
|
-
@artifactType,
|
|
101
|
-
@configMediaType,
|
|
102
|
-
@subjectDigest,
|
|
103
|
-
@annotationsJson,
|
|
104
|
-
@platformOs,
|
|
105
|
-
@platformArchitecture,
|
|
106
|
-
@platformVariant,
|
|
107
|
-
@manifestKind
|
|
108
|
-
)
|
|
109
|
-
`)
|
|
110
|
-
.run({
|
|
145
|
+
this.#insertManifestStatement.run({
|
|
111
146
|
scanId: this.#requireScanId(),
|
|
112
147
|
versionId: manifest.versionId,
|
|
113
148
|
digest: manifest.digest,
|
|
@@ -123,38 +158,10 @@ export class ScanWriter {
|
|
|
123
158
|
});
|
|
124
159
|
}
|
|
125
160
|
insertManifestPayload(digest, rawJson) {
|
|
126
|
-
this.#
|
|
127
|
-
.prepare(`
|
|
128
|
-
INSERT OR REPLACE INTO manifest_payloads(scan_id, digest, raw_json)
|
|
129
|
-
VALUES(?, ?, ?)
|
|
130
|
-
`)
|
|
131
|
-
.run(this.#requireScanId(), digest, rawJson);
|
|
161
|
+
this.#insertManifestPayloadStatement.run(this.#requireScanId(), digest, rawJson);
|
|
132
162
|
}
|
|
133
163
|
insertManifestDescriptor(descriptor) {
|
|
134
|
-
this.#
|
|
135
|
-
.prepare(`
|
|
136
|
-
INSERT OR REPLACE INTO manifest_descriptors(
|
|
137
|
-
scan_id,
|
|
138
|
-
parent_digest,
|
|
139
|
-
child_digest,
|
|
140
|
-
media_type,
|
|
141
|
-
artifact_type,
|
|
142
|
-
platform_os,
|
|
143
|
-
platform_architecture,
|
|
144
|
-
platform_variant
|
|
145
|
-
)
|
|
146
|
-
VALUES(
|
|
147
|
-
@scanId,
|
|
148
|
-
@parentDigest,
|
|
149
|
-
@childDigest,
|
|
150
|
-
@mediaType,
|
|
151
|
-
@artifactType,
|
|
152
|
-
@platformOs,
|
|
153
|
-
@platformArchitecture,
|
|
154
|
-
@platformVariant
|
|
155
|
-
)
|
|
156
|
-
`)
|
|
157
|
-
.run({
|
|
164
|
+
this.#insertManifestDescriptorStatement.run({
|
|
158
165
|
scanId: this.#requireScanId(),
|
|
159
166
|
parentDigest: descriptor.parentDigest,
|
|
160
167
|
childDigest: descriptor.childDigest,
|
|
@@ -166,12 +173,7 @@ export class ScanWriter {
|
|
|
166
173
|
});
|
|
167
174
|
}
|
|
168
175
|
insertManifestEdge(edge) {
|
|
169
|
-
this.#
|
|
170
|
-
.prepare(`
|
|
171
|
-
INSERT OR IGNORE INTO manifest_edges(scan_id, parent_digest, child_digest, edge_kind)
|
|
172
|
-
VALUES(@scanId, @parentDigest, @childDigest, @edgeKind)
|
|
173
|
-
`)
|
|
174
|
-
.run({
|
|
176
|
+
this.#insertManifestEdgeStatement.run({
|
|
175
177
|
scanId: this.#requireScanId(),
|
|
176
178
|
...edge
|
|
177
179
|
});
|
package/dist/db/index.d.ts
CHANGED
|
@@ -4,6 +4,6 @@ export { CleanupRunWriter } from "./_cleanup-run-writer.js";
|
|
|
4
4
|
export { DbMergeRepository } from "./_db-merge-repository.js";
|
|
5
5
|
export { PlannerRepository } from "./planner/index.js";
|
|
6
6
|
export { SnapshotRepository } from "./_snapshot-repository.js";
|
|
7
|
-
export type { DeletePlan } from "./planner/index.js";
|
|
7
|
+
export type { DeletePlan, DeletePlanBlockReasonCode, DeletePlanSelectionMode, DeletePlanSelectionReason } from "./planner/index.js";
|
|
8
8
|
export type { DbMergeSourceSummary } from "./_db-merge-repository.js";
|
|
9
9
|
export declare function openDatabase(databasePath: string): Database.Database;
|
|
@@ -22,6 +22,7 @@ export class PlannerDirectTargetRoots {
|
|
|
22
22
|
SELECT DISTINCT t.version_id, t.tag
|
|
23
23
|
FROM tags t
|
|
24
24
|
WHERE t.scan_id = ?
|
|
25
|
+
AND t.is_digest_tag = 0
|
|
25
26
|
AND (${selectedTagPredicate.sql})
|
|
26
27
|
`
|
|
27
28
|
: `
|
|
@@ -36,6 +37,7 @@ export class PlannerDirectTargetRoots {
|
|
|
36
37
|
SELECT DISTINCT xt.version_id
|
|
37
38
|
FROM tags xt
|
|
38
39
|
WHERE xt.scan_id = ?
|
|
40
|
+
AND xt.is_digest_tag = 0
|
|
39
41
|
AND (${excludedTagPredicate.sql})
|
|
40
42
|
`
|
|
41
43
|
: `
|
|
@@ -36,7 +36,12 @@ export class PlannerDirectTargetTags {
|
|
|
36
36
|
JOIN package_versions pv
|
|
37
37
|
ON pv.scan_id = t.scan_id
|
|
38
38
|
AND pv.version_id = t.version_id
|
|
39
|
+
JOIN v_scan_root_manifests roots
|
|
40
|
+
ON roots.scan_id = t.scan_id
|
|
41
|
+
AND roots.root_version_id = t.version_id
|
|
39
42
|
WHERE t.scan_id = ?
|
|
43
|
+
AND t.is_digest_tag = 0
|
|
44
|
+
AND roots.has_ancestor = 0
|
|
40
45
|
AND (${selectedTagPredicate.sql})
|
|
41
46
|
${excludedRootSql}
|
|
42
47
|
${olderThanSql}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DeletePlan, DeletePlanBlockedRoot, DeletePlanProtectedRoot, DeletePlanRoot, DeletePlanRootDecision, PlanArtifacts } from "./_planner-types.js";
|
|
2
|
-
export declare function buildPlanOutputs(directTargetTags: string[], directTargetRoots: DeletePlanRoot[], planArtifacts: PlanArtifacts): Pick<DeletePlan, "
|
|
2
|
+
export declare function buildPlanOutputs(directTargetTags: string[], directTargetRoots: DeletePlanRoot[], planArtifacts: PlanArtifacts): Pick<DeletePlan, "directTargetTags" | "directTargetRoots" | "rootDecisions" | "protectedRoots" | "closureManifests" | "blockedRoots" | "fullyDeletableRoots" | "collateralTags">;
|
|
3
3
|
export declare function buildRootDecisions(directTargetRoots: DeletePlanRoot[], planArtifacts: PlanArtifacts): DeletePlanRootDecision[];
|
|
4
4
|
export declare function buildProtectedRoots(blockedRoots: DeletePlanBlockedRoot[]): DeletePlanProtectedRoot[];
|
|
5
5
|
export declare function buildBlockedValidationReason(blockedRoot?: DeletePlanBlockedRoot): string;
|
|
@@ -1,18 +1,7 @@
|
|
|
1
1
|
export function buildPlanOutputs(directTargetTags, directTargetRoots, planArtifacts) {
|
|
2
2
|
const rootDecisions = buildRootDecisions(directTargetRoots, planArtifacts);
|
|
3
3
|
const protectedRoots = buildProtectedRoots(planArtifacts.blockedRoots);
|
|
4
|
-
const deleteRootCandidateCount = directTargetRoots.filter((root) => root.selectionMode === "delete-root").length;
|
|
5
|
-
const untagOnlyRootCount = directTargetRoots.length - deleteRootCandidateCount;
|
|
6
4
|
return {
|
|
7
|
-
validationSummary: {
|
|
8
|
-
directTargetTagCount: directTargetTags.length,
|
|
9
|
-
directTargetRootCount: directTargetRoots.length,
|
|
10
|
-
deleteRootCandidateCount,
|
|
11
|
-
untagOnlyRootCount,
|
|
12
|
-
fullyDeletableRootCount: planArtifacts.fullyDeletableRoots.length,
|
|
13
|
-
blockedDeleteRootCount: rootDecisions.filter((decision) => decision.validationStatus === "blocked").length,
|
|
14
|
-
protectedRootCount: protectedRoots.length
|
|
15
|
-
},
|
|
16
5
|
directTargetTags,
|
|
17
6
|
directTargetRoots,
|
|
18
7
|
rootDecisions,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type Database from "better-sqlite3";
|
|
2
2
|
import type { DeletePlan, PlannerLogger } from "./_planner-types.js";
|
|
3
|
-
export type { DeletePlan, DeletePlanBlockedRoot, DeletePlanClosureManifest, DeletePlanProtectedRoot, DeletePlanRoot, DeletePlanRootDecision } from "./_planner-types.js";
|
|
3
|
+
export type { DeletePlan, DeletePlanBlockReasonCode, DeletePlanBlockedRoot, DeletePlanClosureManifest, DeletePlanProtectedRoot, DeletePlanRoot, DeletePlanRootDecision, DeletePlanSelectionMode, DeletePlanSelectionReason } from "./_planner-types.js";
|
|
4
4
|
export declare class PlannerRepository {
|
|
5
5
|
#private;
|
|
6
6
|
constructor(database: Database.Database, logger?: PlannerLogger);
|