diff --git a/__tests__/regclient/regctl.test.ts b/__tests__/regclient/regctl.test.ts index cd647b8..abb79ab 100644 --- a/__tests__/regclient/regctl.test.ts +++ b/__tests__/regclient/regctl.test.ts @@ -20,18 +20,19 @@ import * as semver from 'semver'; import {Exec} from '../../src/exec'; import {Regctl} from '../../src/regclient/regctl'; +import {Image} from '../../src/types/oci/config'; + describe('manifestGet', () => { // prettier-ignore test.each([ - ['moby/moby-bin:latest'], - ['crazymax/undock:latest'], + ['moby/moby-bin:28.1.0-rc.2'], ['crazymax/diun:4.17.0'], ])('given %p', async image => { const regctl = new Regctl(); const manifest = await regctl.manifestGet({ image: image, }); - console.log(`${image}: ${JSON.stringify(manifest, null, 2)}`); + console.log(`${image} manifest: ${JSON.stringify(manifest, null, 2)}`); expect(manifest).not.toBeNull(); expect(manifest?.config).toBeDefined(); expect(manifest?.config.digest).not.toEqual(''); @@ -40,6 +41,48 @@ describe('manifestGet', () => { }); }); +describe('blobGet', () => { + // prettier-ignore + test.each([ + ['moby/moby-bin', 'sha256:234fccbd13fde0ba978a19f728cbdc67e29bc76247ac560822bb6ae5236c0bf0'], + ['crazymax/diun', 'sha256:1e4881f66e0ec0f1710b837002107050bbbc0a231d8a42d7f422b56a139900bb'], + ])('given %p', async (repo, digest) => { + const regctl = new Regctl(); + const blob = await regctl.blobGet({ + repository: repo, + digest: digest + }); + expect(blob).toBeDefined(); + console.log(`${repo}:@${digest} blob: ${JSON.stringify(JSON.parse(blob), null, 2)}`); + }); +}); + +describe('image config', () => { + // prettier-ignore + test.each([ + ['moby/moby-bin:28.1.0-rc.2'], + ['crazymax/diun:4.17.0'], + ])('given %p', async image => { + const regctl = new Regctl(); + const manifest = await regctl.manifestGet({ + image: image, + }); + expect(manifest).not.toBeNull(); + expect(manifest?.config).toBeDefined(); + expect(manifest?.config.digest).not.toEqual(''); + const blob = await regctl.blobGet({ + repository: image, // image works as well + digest: manifest?.config.digest + }); + const imageConfig = JSON.parse(blob); + console.log(`${image} config: ${JSON.stringify(imageConfig, null, 2)}`); + expect(imageConfig).not.toBeNull(); + expect(imageConfig.config).toBeDefined(); + expect(imageConfig?.config?.Labels).toBeDefined(); + expect(Object.keys(imageConfig?.config?.Labels || {}).length).toBeGreaterThan(0); + }); +}); + describe('isAvailable', () => { it('checks regctl is available', async () => { const execSpy = jest.spyOn(Exec, 'getExecOutput'); diff --git a/src/regclient/regctl.ts b/src/regclient/regctl.ts index 2e2dc9f..b784d9d 100644 --- a/src/regclient/regctl.ts +++ b/src/regclient/regctl.ts @@ -25,6 +25,11 @@ export interface RegctlOpts { binPath?: string; } +export interface RegctlBlobGetOpts { + repository: string; + digest: string; +} + export interface RegctlManifestGetOpts { image: string; platform?: string; @@ -41,6 +46,19 @@ export class Regctl { this._versionOnce = false; } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public async blobGet(opts: RegctlBlobGetOpts): Promise { + return await Exec.getExecOutput(this.binPath, ['blob', 'get', opts.repository, opts.digest], { + ignoreReturnCode: true, + silent: true + }).then(res => { + if (res.stderr.length > 0 && res.exitCode != 0) { + throw new Error(res.stderr.trim()); + } + return res.stdout; + }); + } + public async manifestGet(opts: RegctlManifestGetOpts): Promise { return await Exec.getExecOutput(this.binPath, ['manifest', 'get', opts.image, `--platform=${opts.platform ?? 'local'}`, `--format={{json .}}`], { ignoreReturnCode: true,