code-provenance 0.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/README.md ADDED
@@ -0,0 +1,99 @@
1
+ # code-provenance
2
+
3
+ Resolve Docker images in a docker-compose file to their exact source code commits on GitHub.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install code-provenance
9
+ ```
10
+
11
+ Requires Node.js 20+.
12
+
13
+ ## CLI Usage
14
+
15
+ ```bash
16
+ npx code-provenance [compose-file] [--json] [--verbose]
17
+ ```
18
+
19
+ - `compose-file` -- path to a docker-compose file (default: `docker-compose.yml`)
20
+ - `--json` -- output results as JSON
21
+ - `--verbose`, `-v` -- show resolution steps for each image
22
+
23
+ ### Example
24
+
25
+ ```bash
26
+ npx code-provenance docker-compose.yml
27
+ ```
28
+
29
+ ```
30
+ ┌─────────┬────────────────┬────────────────────────────┬──────────────┬──────────┬────────────┐
31
+ │ SERVICE │ IMAGE │ REPO │ COMMIT │ STATUS │ CONFIDENCE │
32
+ ├─────────┼────────────────┼────────────────────────────┼──────────────┼──────────┼────────────┤
33
+ │ web │ traefik:v3.6.0 │ github.com/traefik/traefik │ 06db5168c0d9 │ resolved │ exact │
34
+ └─────────┴────────────────┴────────────────────────────┴──────────────┴──────────┴────────────┘
35
+ ```
36
+
37
+ ## Library Usage
38
+
39
+ ```typescript
40
+ import { readFileSync } from "node:fs";
41
+ import { parseCompose, parseImageRef, resolveImage } from "code-provenance";
42
+
43
+ const yaml = readFileSync("docker-compose.yml", "utf-8");
44
+ for (const [service, image] of parseCompose(yaml)) {
45
+ const ref = parseImageRef(image);
46
+ const result = await resolveImage(service, ref);
47
+ console.log(`${result.service}: ${result.commit} (${result.confidence})`);
48
+ }
49
+ ```
50
+
51
+ ## API Reference
52
+
53
+ ### Exports
54
+
55
+ - `parseCompose(yaml: string): [string, string][]` -- parse a docker-compose YAML string and return `[serviceName, imageString]` pairs
56
+ - `parseImageRef(image: string): ImageRef` -- parse a Docker image string into its components
57
+ - `resolveImage(service: string, ref: ImageRef): Promise<ImageResult>` -- resolve an image reference to its source code commit
58
+ - `formatTable(results: ImageResult[]): string` -- format results as a table
59
+ - `formatJson(results: ImageResult[]): string` -- format results as JSON
60
+
61
+ ### ImageRef
62
+
63
+ | Field | Type | Description |
64
+ |-------|------|-------------|
65
+ | `registry` | `string` | e.g. `"ghcr.io"`, `"docker.io"` |
66
+ | `namespace` | `string` | e.g. `"myorg"`, `"library"` |
67
+ | `name` | `string` | e.g. `"traefik"`, `"postgres"` |
68
+ | `tag` | `string` | e.g. `"v3.6.0"`, `"latest"` |
69
+ | `raw` | `string` | original image string from docker-compose |
70
+
71
+ ### ImageResult
72
+
73
+ | Field | Type | Description |
74
+ |-------|------|-------------|
75
+ | `service` | `string` | service name from docker-compose |
76
+ | `image` | `string` | original image string |
77
+ | `registry` | `string` | image registry |
78
+ | `repo` | `string \| null` | GitHub repository URL |
79
+ | `tag` | `string` | image tag |
80
+ | `commit` | `string \| null` | resolved commit SHA |
81
+ | `commit_url` | `string \| null` | URL to the commit on GitHub |
82
+ | `status` | `string` | `"resolved"`, `"repo_not_found"`, `"repo_found_tag_not_matched"`, or `"no_tag"` |
83
+ | `resolution_method` | `string \| null` | how the commit was resolved (e.g. `"oci_labels"`, `"tag_match"`) |
84
+ | `confidence` | `string \| null` | `"exact"` or `"approximate"` |
85
+ | `steps` | `string[]` | resolution steps taken (useful with `--verbose`) |
86
+
87
+ ## Authentication
88
+
89
+ Set `GITHUB_TOKEN` for full functionality (digest resolution, `:latest` on GHCR, higher rate limits):
90
+
91
+ ```bash
92
+ export GITHUB_TOKEN=ghp_your_token_here
93
+ ```
94
+
95
+ Create a classic token at https://github.com/settings/tokens with `read:packages` scope.
96
+
97
+ ## License
98
+
99
+ MIT
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from "node:fs";
3
+ import { existsSync } from "node:fs";
4
+ import { parseCompose, parseImageRef } from "./composeParser.js";
5
+ import { resolveImage } from "./resolver.js";
6
+ import { formatJson, formatTable } from "./output.js";
7
+ function printHelp() {
8
+ console.log(`usage: code-provenance [-h] [--json] [compose_file]
9
+
10
+ Resolve Docker images to their source code commits on GitHub.
11
+
12
+ positional arguments:
13
+ compose_file Path to docker-compose file (default: docker-compose.yml)
14
+
15
+ options:
16
+ -h, --help show this help message and exit
17
+ --json Output results as JSON
18
+ -v, --verbose Show resolution steps`);
19
+ }
20
+ async function main() {
21
+ const args = process.argv.slice(2);
22
+ if (args.includes("-h") || args.includes("--help")) {
23
+ printHelp();
24
+ return 0;
25
+ }
26
+ const jsonOutput = args.includes("--json");
27
+ const verbose = args.includes("--verbose") || args.includes("-v");
28
+ const positionalArgs = args.filter((a) => a !== "--json" && a !== "--verbose" && a !== "-v");
29
+ const composeFile = positionalArgs[0] || "docker-compose.yml";
30
+ if (!existsSync(composeFile)) {
31
+ console.error(`Error: ${composeFile} not found`);
32
+ return 1;
33
+ }
34
+ const yamlContent = readFileSync(composeFile, "utf-8");
35
+ const services = parseCompose(yamlContent);
36
+ if (services.length === 0) {
37
+ console.error("No services with images found.");
38
+ return 0;
39
+ }
40
+ // Resolve all images in parallel
41
+ const results = await Promise.all(services.map(([serviceName, imageString]) => {
42
+ const ref = parseImageRef(imageString);
43
+ return resolveImage(serviceName, ref);
44
+ }));
45
+ if (verbose) {
46
+ for (const r of results) {
47
+ console.error(`\nResolving ${r.image} ...`);
48
+ for (const step of r.steps) {
49
+ console.error(` ${step}`);
50
+ }
51
+ console.error(` → ${r.status}` +
52
+ (r.status === "resolved"
53
+ ? ` (${r.resolution_method}, ${r.confidence})`
54
+ : ""));
55
+ }
56
+ console.error();
57
+ }
58
+ if (jsonOutput) {
59
+ console.log(formatJson(results));
60
+ }
61
+ else {
62
+ console.log(formatTable(results));
63
+ }
64
+ return 0;
65
+ }
66
+ main()
67
+ .then((code) => process.exit(code))
68
+ .catch((err) => {
69
+ console.error(err);
70
+ process.exit(1);
71
+ });
72
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { ImageRef } from "./types.js";
2
+ /**
3
+ * Parse a Docker image string into an ImageRef.
4
+ */
5
+ export declare function parseImageRef(imageString: string): ImageRef;
6
+ /**
7
+ * Parse docker-compose YAML and return list of [serviceName, imageString] pairs.
8
+ */
9
+ export declare function parseCompose(yamlContent: string): [string, string][];
@@ -0,0 +1,72 @@
1
+ import YAML from "yaml";
2
+ /**
3
+ * Parse a Docker image string into an ImageRef.
4
+ */
5
+ export function parseImageRef(imageString) {
6
+ const raw = imageString;
7
+ let tag;
8
+ let namePart;
9
+ // Handle digest references (image@sha256:...)
10
+ if (imageString.includes("@")) {
11
+ const atIdx = imageString.indexOf("@");
12
+ namePart = imageString.slice(0, atIdx);
13
+ tag = imageString.slice(atIdx + 1);
14
+ }
15
+ else {
16
+ const lastSegment = imageString.split("/").pop();
17
+ if (lastSegment.includes(":")) {
18
+ const colonPos = imageString.lastIndexOf(":");
19
+ tag = imageString.slice(colonPos + 1);
20
+ namePart = imageString.slice(0, colonPos);
21
+ }
22
+ else {
23
+ tag = "latest";
24
+ namePart = imageString;
25
+ }
26
+ }
27
+ // Determine registry
28
+ const parts = namePart.split("/");
29
+ let registry;
30
+ let remaining;
31
+ if (parts.length >= 2 && (parts[0].includes(".") || parts[0].includes(":"))) {
32
+ registry = parts[0];
33
+ remaining = parts.slice(1);
34
+ }
35
+ else {
36
+ registry = "docker.io";
37
+ remaining = parts;
38
+ }
39
+ // Determine namespace and name
40
+ let namespace;
41
+ let name;
42
+ if (remaining.length === 1) {
43
+ namespace = "library";
44
+ name = remaining[0];
45
+ }
46
+ else if (remaining.length === 2) {
47
+ namespace = remaining[0];
48
+ name = remaining[1];
49
+ }
50
+ else {
51
+ namespace = remaining[0];
52
+ name = remaining.slice(1).join("/");
53
+ }
54
+ return { registry, namespace, name, tag, raw };
55
+ }
56
+ /**
57
+ * Parse docker-compose YAML and return list of [serviceName, imageString] pairs.
58
+ */
59
+ export function parseCompose(yamlContent) {
60
+ const data = YAML.parse(yamlContent);
61
+ const services = data?.services ?? {};
62
+ const results = [];
63
+ for (const [serviceName, serviceConfig] of Object.entries(services)) {
64
+ if (serviceConfig !== null &&
65
+ typeof serviceConfig === "object" &&
66
+ "image" in serviceConfig) {
67
+ results.push([serviceName, serviceConfig.image]);
68
+ }
69
+ }
70
+ return results;
71
+ }
72
+ //# sourceMappingURL=composeParser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composeParser.js","sourceRoot":"","sources":["../src/composeParser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,65 @@
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { parseImageRef, parseCompose } from "./composeParser.js";
4
+ describe("parseImageRef", () => {
5
+ it("parses ghcr with tag", () => {
6
+ const ref = parseImageRef("ghcr.io/excalidraw/excalidraw:v0.17.3");
7
+ assert.equal(ref.registry, "ghcr.io");
8
+ assert.equal(ref.namespace, "excalidraw");
9
+ assert.equal(ref.name, "excalidraw");
10
+ assert.equal(ref.tag, "v0.17.3");
11
+ });
12
+ it("parses docker hub official image", () => {
13
+ const ref = parseImageRef("postgres:16");
14
+ assert.equal(ref.registry, "docker.io");
15
+ assert.equal(ref.namespace, "library");
16
+ assert.equal(ref.name, "postgres");
17
+ assert.equal(ref.tag, "16");
18
+ });
19
+ it("parses docker hub namespaced image", () => {
20
+ const ref = parseImageRef("traefik/whoami:v1.10.3");
21
+ assert.equal(ref.registry, "docker.io");
22
+ assert.equal(ref.namespace, "traefik");
23
+ assert.equal(ref.name, "whoami");
24
+ assert.equal(ref.tag, "v1.10.3");
25
+ });
26
+ it("defaults to latest when no tag", () => {
27
+ const ref = parseImageRef("nginx");
28
+ assert.equal(ref.registry, "docker.io");
29
+ assert.equal(ref.namespace, "library");
30
+ assert.equal(ref.name, "nginx");
31
+ assert.equal(ref.tag, "latest");
32
+ });
33
+ it("handles digest reference", () => {
34
+ const ref = parseImageRef("ghcr.io/org/app@sha256:abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890");
35
+ assert.equal(ref.registry, "ghcr.io");
36
+ assert.equal(ref.namespace, "org");
37
+ assert.equal(ref.name, "app");
38
+ assert.equal(ref.tag, "sha256:abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890");
39
+ });
40
+ });
41
+ describe("parseCompose", () => {
42
+ it("extracts services with image field", () => {
43
+ const yaml = `
44
+ services:
45
+ web:
46
+ image: nginx:latest
47
+ api:
48
+ build: .
49
+ db:
50
+ image: postgres:16
51
+ `;
52
+ const result = parseCompose(yaml);
53
+ assert.equal(result.length, 2);
54
+ assert.deepEqual(result[0], ["web", "nginx:latest"]);
55
+ assert.deepEqual(result[1], ["db", "postgres:16"]);
56
+ });
57
+ it("handles empty services", () => {
58
+ const yaml = `
59
+ services: {}
60
+ `;
61
+ const result = parseCompose(yaml);
62
+ assert.equal(result.length, 0);
63
+ });
64
+ });
65
+ //# sourceMappingURL=composeParser.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composeParser.test.js","sourceRoot":"","sources":["../src/composeParser.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Resolve an image tag to a commit SHA by matching against git tags.
3
+ * Tries exact match first, then prefix match (e.g., v2.10 -> highest v2.10.x).
4
+ * Returns [commit_sha, is_exact_match] or null.
5
+ */
6
+ export declare function resolveTagToCommit(owner: string, repo: string, tag: string): Promise<[string, boolean] | null>;
7
+ /**
8
+ * Get the commit SHA of the latest GitHub release.
9
+ * Returns [commit_sha, tag_name] or null.
10
+ */
11
+ export declare function getLatestReleaseCommit(owner: string, repo: string): Promise<[string, string] | null>;
12
+ /**
13
+ * Get the latest commit SHA on the default branch.
14
+ */
15
+ export declare function getLatestCommit(owner: string, repo: string): Promise<string | null>;
16
+ /**
17
+ * Check if a GitHub repo exists.
18
+ */
19
+ export declare function checkGithubRepoExists(owner: string, repo: string): Promise<boolean>;
20
+ interface PackageVersionResult {
21
+ repo: string;
22
+ commit: string | null;
23
+ tags: string[];
24
+ }
25
+ /**
26
+ * Find the commit for a GHCR image by its digest.
27
+ */
28
+ export declare function resolveGhcrDigestViaPackages(owner: string, packageName: string, digest: string): Promise<PackageVersionResult | null>;
29
+ /**
30
+ * Find the commit for a GHCR image's :latest tag.
31
+ */
32
+ export declare function resolveGhcrLatestViaPackages(owner: string, packageName: string): Promise<PackageVersionResult | null>;
33
+ /**
34
+ * Try to find the GitHub repo for a Docker Hub image.
35
+ */
36
+ export declare function inferRepoFromDockerhub(namespace: string, name: string): Promise<[string, string] | null>;
37
+ export {};
package/dist/github.js ADDED
@@ -0,0 +1,270 @@
1
+ function githubHeaders() {
2
+ const headers = {
3
+ Accept: "application/vnd.github+json",
4
+ };
5
+ const token = process.env.GITHUB_TOKEN;
6
+ if (token) {
7
+ headers.Authorization = `Bearer ${token}`;
8
+ }
9
+ return headers;
10
+ }
11
+ function normalizeTag(tag) {
12
+ return tag.replace(/^v+/, "");
13
+ }
14
+ function isPrefixMatch(imageTag, gitTag) {
15
+ const normImage = normalizeTag(imageTag);
16
+ const normGit = normalizeTag(gitTag);
17
+ return normGit.startsWith(normImage + ".");
18
+ }
19
+ function parseVersionTuple(tag) {
20
+ let norm = normalizeTag(tag);
21
+ // Strip pre-release suffixes like -rc1, -beta2
22
+ norm = norm.split(/[-+]/)[0];
23
+ const parts = norm.split(".");
24
+ try {
25
+ const nums = parts.map((p) => {
26
+ const n = parseInt(p, 10);
27
+ if (isNaN(n))
28
+ throw new Error();
29
+ return n;
30
+ });
31
+ return nums;
32
+ }
33
+ catch {
34
+ return null;
35
+ }
36
+ }
37
+ function compareVersions(a, b) {
38
+ const len = Math.max(a.length, b.length);
39
+ for (let i = 0; i < len; i++) {
40
+ const av = a[i] ?? 0;
41
+ const bv = b[i] ?? 0;
42
+ if (av !== bv)
43
+ return av - bv;
44
+ }
45
+ return 0;
46
+ }
47
+ /**
48
+ * Parse the Link header for pagination.
49
+ * Returns the "next" URL or null.
50
+ */
51
+ function parseNextLink(linkHeader) {
52
+ if (!linkHeader)
53
+ return null;
54
+ const match = linkHeader.match(/<([^>]+)>;\s*rel="next"/);
55
+ return match ? match[1] : null;
56
+ }
57
+ /**
58
+ * Resolve an image tag to a commit SHA by matching against git tags.
59
+ * Tries exact match first, then prefix match (e.g., v2.10 -> highest v2.10.x).
60
+ * Returns [commit_sha, is_exact_match] or null.
61
+ */
62
+ export async function resolveTagToCommit(owner, repo, tag) {
63
+ const headers = githubHeaders();
64
+ let url = `https://api.github.com/repos/${owner}/${repo}/tags?per_page=100`;
65
+ const prefixCandidates = [];
66
+ while (url) {
67
+ const resp = await fetch(url, {
68
+ headers,
69
+ signal: AbortSignal.timeout(10000),
70
+ });
71
+ if (!resp.ok)
72
+ return null;
73
+ const gitTags = await resp.json();
74
+ for (const gitTag of gitTags) {
75
+ const name = gitTag.name;
76
+ // Exact match (with/without v prefix)
77
+ if (name === tag ||
78
+ name === `v${tag}` ||
79
+ normalizeTag(name) === normalizeTag(tag)) {
80
+ return [gitTag.commit.sha, true];
81
+ }
82
+ // Collect prefix match candidates
83
+ if (isPrefixMatch(tag, name)) {
84
+ const version = parseVersionTuple(name);
85
+ if (version !== null) {
86
+ prefixCandidates.push([version, gitTag.commit.sha]);
87
+ }
88
+ }
89
+ }
90
+ url = parseNextLink(resp.headers.get("link"));
91
+ }
92
+ // Return the highest version among prefix matches
93
+ if (prefixCandidates.length > 0) {
94
+ prefixCandidates.sort((a, b) => compareVersions(b[0], a[0]));
95
+ return [prefixCandidates[0][1], false];
96
+ }
97
+ return null;
98
+ }
99
+ /**
100
+ * Get the commit SHA of the latest GitHub release.
101
+ * Returns [commit_sha, tag_name] or null.
102
+ */
103
+ export async function getLatestReleaseCommit(owner, repo) {
104
+ const headers = githubHeaders();
105
+ try {
106
+ const resp = await fetch(`https://api.github.com/repos/${owner}/${repo}/releases/latest`, { headers, signal: AbortSignal.timeout(10000) });
107
+ if (!resp.ok)
108
+ return null;
109
+ const data = (await resp.json());
110
+ const tagName = data.tag_name;
111
+ if (!tagName)
112
+ return null;
113
+ const tagResult = await resolveTagToCommit(owner, repo, tagName);
114
+ if (tagResult) {
115
+ return [tagResult[0], tagName];
116
+ }
117
+ return null;
118
+ }
119
+ catch {
120
+ return null;
121
+ }
122
+ }
123
+ /**
124
+ * Get the latest commit SHA on the default branch.
125
+ */
126
+ export async function getLatestCommit(owner, repo) {
127
+ const headers = githubHeaders();
128
+ try {
129
+ const resp = await fetch(`https://api.github.com/repos/${owner}/${repo}/commits?per_page=1`, { headers, signal: AbortSignal.timeout(10000) });
130
+ if (!resp.ok)
131
+ return null;
132
+ const commits = (await resp.json());
133
+ if (commits.length > 0)
134
+ return commits[0].sha;
135
+ }
136
+ catch {
137
+ // ignore
138
+ }
139
+ return null;
140
+ }
141
+ /**
142
+ * Check if a GitHub repo exists.
143
+ */
144
+ export async function checkGithubRepoExists(owner, repo) {
145
+ const headers = githubHeaders();
146
+ try {
147
+ const resp = await fetch(`https://api.github.com/repos/${owner}/${repo}`, { headers, signal: AbortSignal.timeout(10000) });
148
+ return resp.status === 200;
149
+ }
150
+ catch {
151
+ return false;
152
+ }
153
+ }
154
+ /**
155
+ * Find a GHCR package version by digest or tag via the GitHub Packages API.
156
+ */
157
+ async function findGhcrPackageVersion(owner, packageName, options) {
158
+ const headers = githubHeaders();
159
+ if (!headers.Authorization)
160
+ return null;
161
+ for (const entityType of ["orgs", "users"]) {
162
+ const pkgBase = `https://api.github.com/${entityType}/${owner}/packages/container/${packageName}`;
163
+ // Get package metadata for source repo
164
+ let fullName;
165
+ try {
166
+ const pkgResp = await fetch(pkgBase, {
167
+ headers,
168
+ signal: AbortSignal.timeout(10000),
169
+ });
170
+ if (pkgResp.status === 403)
171
+ return null;
172
+ if (!pkgResp.ok)
173
+ continue;
174
+ const pkgData = await pkgResp.json();
175
+ fullName = pkgData?.repository?.full_name;
176
+ if (!fullName)
177
+ continue;
178
+ }
179
+ catch {
180
+ continue;
181
+ }
182
+ // Search versions
183
+ let versionsUrl = `${pkgBase}/versions?per_page=50`;
184
+ try {
185
+ while (versionsUrl) {
186
+ const resp = await fetch(versionsUrl, {
187
+ headers,
188
+ signal: AbortSignal.timeout(10000),
189
+ });
190
+ if (!resp.ok)
191
+ break;
192
+ const versions = (await resp.json());
193
+ for (const version of versions) {
194
+ const name = version.name ?? "";
195
+ const metadata = version.metadata?.container ?? {};
196
+ const tags = metadata.tags ?? [];
197
+ // Match by digest (version name is the digest)
198
+ if (options.matchDigest && name !== options.matchDigest) {
199
+ if (!options.matchTag)
200
+ continue;
201
+ }
202
+ // Match by tag
203
+ if (options.matchTag && !tags.includes(options.matchTag))
204
+ continue;
205
+ // Found matching version - resolve tags to a commit
206
+ const [repoOwner, repoName] = fullName.split("/", 2);
207
+ const resolvableTags = tags.filter((t) => t !== "latest");
208
+ for (const t of resolvableTags) {
209
+ const tagResult = await resolveTagToCommit(repoOwner, repoName, t);
210
+ if (tagResult) {
211
+ return { repo: fullName, commit: tagResult[0], tags };
212
+ }
213
+ }
214
+ return { repo: fullName, commit: null, tags };
215
+ }
216
+ versionsUrl = parseNextLink(resp.headers.get("link"));
217
+ }
218
+ }
219
+ catch {
220
+ continue;
221
+ }
222
+ }
223
+ return null;
224
+ }
225
+ /**
226
+ * Find the commit for a GHCR image by its digest.
227
+ */
228
+ export async function resolveGhcrDigestViaPackages(owner, packageName, digest) {
229
+ return findGhcrPackageVersion(owner, packageName, { matchDigest: digest });
230
+ }
231
+ /**
232
+ * Find the commit for a GHCR image's :latest tag.
233
+ */
234
+ export async function resolveGhcrLatestViaPackages(owner, packageName) {
235
+ return findGhcrPackageVersion(owner, packageName, { matchTag: "latest" });
236
+ }
237
+ /**
238
+ * Try to find the GitHub repo for a Docker Hub image.
239
+ */
240
+ export async function inferRepoFromDockerhub(namespace, name) {
241
+ // For official images (library/X), try the image name as org/repo directly
242
+ if (namespace === "library") {
243
+ if (await checkGithubRepoExists(name, name)) {
244
+ return [name, name];
245
+ }
246
+ }
247
+ // For namespaced images, try namespace/name on GitHub
248
+ if (namespace !== "library") {
249
+ if (await checkGithubRepoExists(namespace, name)) {
250
+ return [namespace, name];
251
+ }
252
+ }
253
+ // Fall back to scraping Docker Hub description for GitHub links
254
+ try {
255
+ const resp = await fetch(`https://hub.docker.com/v2/repositories/${namespace}/${name}`, { signal: AbortSignal.timeout(10000) });
256
+ if (!resp.ok)
257
+ return null;
258
+ const data = (await resp.json());
259
+ const text = (data.full_description || "") + " " + (data.description || "");
260
+ const match = text.match(/https?:\/\/github\.com\/([\w.-]+)\/([\w.-]+)/);
261
+ if (match) {
262
+ return [match[1], match[2]];
263
+ }
264
+ }
265
+ catch {
266
+ // ignore
267
+ }
268
+ return null;
269
+ }
270
+ //# sourceMappingURL=github.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.js","sourceRoot":"","sources":["../src/github.ts"],"names":[],"mappings":"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"}