ghcr-cleanup-manager 1.1.2 → 1.1.4

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 CHANGED
@@ -7,6 +7,23 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [v1.1.4] - 2026-07-02
11
+
12
+ ### Fixed
13
+
14
+ - Fixed the visualizer README examples to use the real release SQLite filename instead of a broken placeholder.
15
+
16
+ ## [v1.1.3] - 2026-07-01
17
+
18
+ ### Changed
19
+
20
+ - Digest-tag helper edges now ignore self-references from a digest-tagged artifact back to its own manifest.
21
+ - The visualizer details view now shows `sha256-*` tags. These tags are still hidden in the graph itself.
22
+
23
+ ### Fixed
24
+
25
+ - Visualizer docs and package-name references now consistently use `ghcr-cleanup-manager-visualizer`.
26
+
10
27
  ## [v1.1.2] - 2026-06-14
11
28
 
12
29
  ### Changed
package/README.md CHANGED
@@ -9,14 +9,14 @@ GHCR Cleanup Manager is a GHCR cleanup action for GitHub Container Registry pack
9
9
 
10
10
  GHCR Cleanup Manager is a GitHub Action for:
11
11
 
12
- - clean GHCR packages: including tagged and untagged images
13
- - preview cleanup: with `dry-run` before making changes
14
- - scan GHCR packages: into SQLite database artifacts
15
- - visualize GHCR package graphs: and their changes with the
16
- [ghcr-cleanup-manager-visualizer](https://github.com/ghcr-manager/ghcr-cleanup-manager/blob/main/visualizer/README.md)
12
+ - clean GHCR packages including tagged and untagged images
13
+ - preview cleanup with `dry-run` before making changes
14
+ - scan GHCR packages into SQLite database artifacts
15
+ - visualize GHCR package graphs and their changes with the
16
+ [visualizer](https://github.com/ghcr-manager/ghcr-cleanup-manager/blob/main/visualizer/README.md)
17
17
 
18
- It is built for safe GHCR cleanup on real OCI package graphs, including multi-arch images, attestations, cosign
19
- signatures, and other referrers that simpler GHCR cleanup actions often mishandle.
18
+ It is built for safe GHCR cleanup on OCI package graphs, including multi-arch images, attestations, cosign signatures,
19
+ and other referrers that simpler GHCR cleanup actions often mishandle.
20
20
 
21
21
  [![Example compare view: red-bordered manifests are present in the older scan and removed in the newer one.](https://raw.githubusercontent.com/ghcr-manager/ghcr-cleanup-manager/main/docs/images/visualizer/graph-2images-cosign--wide.png "Example compare view: red-bordered manifests are present in the older scan and removed in the newer one.")](https://github.com/ghcr-manager/ghcr-cleanup-manager/blob/main/docs/images/visualizer/graph-2images-cosign--wide.png)
22
22
 
@@ -41,7 +41,7 @@ jobs:
41
41
 
42
42
  - name: Preview GHCR cleanup
43
43
  id: ghcr-cleanup-manager
44
- uses: ghcr-manager/ghcr-cleanup-manager@v1.1.2
44
+ uses: ghcr-manager/ghcr-cleanup-manager@v1.1.4
45
45
  with:
46
46
  command: cleanup
47
47
  token: ${{ github.token }}
@@ -49,7 +49,7 @@ jobs:
49
49
  package: PACKAGE
50
50
  dry-run: true
51
51
  delete-untagged: true
52
- keep-n-tagged: "10"
52
+ keep-n-tagged: 10
53
53
  exclude-tags: |
54
54
  latest
55
55
  upload-artifacts: true
@@ -84,7 +84,7 @@ The action supports two commands:
84
84
  ### Preview cleanup
85
85
 
86
86
  ```yaml
87
- - uses: ghcr-manager/ghcr-cleanup-manager@v1.1.2
87
+ - uses: ghcr-manager/ghcr-cleanup-manager@v1.1.4
88
88
  with:
89
89
  command: cleanup
90
90
  token: ${{ github.token }}
@@ -95,7 +95,7 @@ The action supports two commands:
95
95
  pr-.*
96
96
  use-regex: true
97
97
  older-than: 30 days
98
- keep-n-tagged: "5"
98
+ keep-n-tagged: 5
99
99
  exclude-tags: |
100
100
  latest
101
101
  stable
@@ -105,14 +105,14 @@ The action supports two commands:
105
105
  ### Apply cleanup
106
106
 
107
107
  ```yaml
108
- - uses: ghcr-manager/ghcr-cleanup-manager@v1.1.2
108
+ - uses: ghcr-manager/ghcr-cleanup-manager@v1.1.4
109
109
  with:
110
110
  command: cleanup
111
111
  token: ${{ github.token }}
112
112
  owner: OWNER
113
113
  package: PACKAGE
114
114
  delete-untagged: true
115
- keep-n-tagged: "10"
115
+ keep-n-tagged: 10
116
116
  upload-artifacts: true
117
117
  scan-after-cleanup: true
118
118
  ```
@@ -127,7 +127,7 @@ off `dry-run`.
127
127
  ### Scan one package
128
128
 
129
129
  ```yaml
130
- - uses: ghcr-manager/ghcr-cleanup-manager@v1.1.2
130
+ - uses: ghcr-manager/ghcr-cleanup-manager@v1.1.4
131
131
  with:
132
132
  command: scan
133
133
  token: ${{ github.token }}
@@ -218,8 +218,8 @@ The release assets also include one merged SQLite DB from GHCR Cleanup Manager's
218
218
  as a quick visualizer demo and as a compact way to inspect dozens of real cleanup and graph cases.
219
219
 
220
220
  ```sh
221
- curl -LO https://github.com/ghcr-manager/ghcr-cleanup-manager/releases/latest/download/ghcr-cleanup-manager([^/s]*.sqlite)
222
- npx ghcr-cleanup-manager-visualizer --db ./ghcr-cleanup-manager([^/s]*.sqlite)
221
+ curl -LO https://github.com/ghcr-manager/ghcr-cleanup-manager/releases/latest/download/ghcr-cleanup-manager-release-scenarios.sqlite
222
+ npx ghcr-cleanup-manager-visualizer --db ./ghcr-cleanup-manager-release-scenarios.sqlite
223
223
  ```
224
224
 
225
225
  Docker image available:
@@ -9,7 +9,8 @@ export function rebuildManifestReachability(database, scanId) {
9
9
  parentDigestsByChild.set(digest, new Set());
10
10
  neighborDigestsByDigest.set(digest, new Set());
11
11
  }
12
- for (const manifestEdge of _loadManifestEdges(database, scanId)) {
12
+ const manifestEdges = _loadManifestEdges(database, scanId);
13
+ for (const manifestEdge of manifestEdges) {
13
14
  childDigestsByParent.get(manifestEdge.parent_digest)?.add(manifestEdge.child_digest);
14
15
  parentDigestsByChild.get(manifestEdge.child_digest)?.add(manifestEdge.parent_digest);
15
16
  neighborDigestsByDigest.get(manifestEdge.parent_digest)?.add(manifestEdge.child_digest);
@@ -55,7 +56,7 @@ export function rebuildManifestReachability(database, scanId) {
55
56
  }
56
57
  }
57
58
  if (descendantDistancesByDigest.size !== manifestDigests.length) {
58
- throw new Error("manifest reachability build detected a cycle in manifest_edges");
59
+ throw new Error(_buildCycleErrorMessage(manifestDigests, remainingChildrenCount, manifestEdges));
59
60
  }
60
61
  const insertRow = database.prepare(`
61
62
  INSERT OR REPLACE INTO manifest_reachability(
@@ -131,6 +132,7 @@ function _refreshDigestTagEdges(database, scanId) {
131
132
  AND child_manifest.digest = 'sha256:' || SUBSTR(t.tag, 8, 64)
132
133
  WHERE t.scan_id = ?
133
134
  AND t.is_digest_tag = 1
135
+ AND m.digest != child_manifest.digest
134
136
  `)
135
137
  .run(scanId);
136
138
  }
@@ -143,13 +145,23 @@ function _loadManifestDigests(database, scanId) {
143
145
  function _loadManifestEdges(database, scanId) {
144
146
  return database
145
147
  .prepare(`
146
- SELECT DISTINCT parent_digest, child_digest
148
+ SELECT DISTINCT parent_digest, child_digest, edge_kind
147
149
  FROM manifest_edges
148
150
  WHERE scan_id = ?
149
- ORDER BY parent_digest, child_digest
151
+ ORDER BY parent_digest, child_digest, edge_kind
150
152
  `)
151
153
  .all(scanId);
152
154
  }
155
+ function _buildCycleErrorMessage(manifestDigests, remainingChildrenCount, manifestEdges) {
156
+ const unresolvedDigests = new Set(manifestDigests.filter((digest) => (remainingChildrenCount.get(digest) ?? 0) > 0));
157
+ const unresolvedDigestList = Array.from(unresolvedDigests).join(", ");
158
+ for (const edge of manifestEdges) {
159
+ if (unresolvedDigests.has(edge.parent_digest) && unresolvedDigests.has(edge.child_digest)) {
160
+ return `manifest reachability build detected a cycle in manifest_edges; example unresolved edge: ${edge.parent_digest} --${edge.edge_kind}--> ${edge.child_digest}; unresolved digests: ${unresolvedDigestList}`;
161
+ }
162
+ }
163
+ return `manifest reachability build detected a cycle in manifest_edges; unresolved digests: ${unresolvedDigestList}`;
164
+ }
153
165
  function _setMinDistance(distances, digest, distance) {
154
166
  const currentDistance = distances.get(digest);
155
167
  if (currentDistance === undefined || distance < currentDistance) {
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "ghcr-cleanup-manager",
3
3
  "description": "GHCR Cleanup Manager: GitHub Action and CLI for safe GitHub Container Registry cleanup and inspection.",
4
4
  "homepage": "https://github.com/ghcr-manager/ghcr-cleanup-manager#readme",
5
- "version": "v1.1.2",
5
+ "version": "v1.1.4",
6
6
  "license": "MIT",
7
7
  "type": "module",
8
8
  "keywords": [
@@ -68,7 +68,7 @@
68
68
  "eslint": "^10.2.1",
69
69
  "eslint-plugin-yml": "^3.4.0",
70
70
  "globals": "^17.5.0",
71
- "markdownlint-cli2": "^0.22.1",
71
+ "markdownlint-cli2": "^0.23.0",
72
72
  "prettier": "^3.8.3",
73
73
  "tsx": "^4.20.6",
74
74
  "typescript": "^6.0.3",