diff --git a/__tests__/docker/install.test.itg.ts b/__tests__/docker/install.test.itg.ts index 42750260..4b4d943b 100644 --- a/__tests__/docker/install.test.itg.ts +++ b/__tests__/docker/install.test.itg.ts @@ -43,6 +43,7 @@ aarch64:https://cloud.debian.org/images/cloud/bookworm/20231013-1532/debian-12-g test.each([ {type: 'image', tag: '27.3.1'} as InstallSourceImage, {type: 'image', tag: 'master'} as InstallSourceImage, + {type: 'image', tag: 'latest'} as InstallSourceImage, {type: 'archive', version: 'v26.1.4', channel: 'stable'} as InstallSourceArchive, {type: 'archive', version: 'latest', channel: 'stable'} as InstallSourceArchive, ])( diff --git a/src/docker/assets.ts b/src/docker/assets.ts index 00f7332f..49d25636 100644 --- a/src/docker/assets.ts +++ b/src/docker/assets.ts @@ -237,12 +237,10 @@ provision: HOME=/tmp undock moby/moby-bin:{{srcImageTag}} /usr/local/bin - wget https://raw.githubusercontent.com/moby/moby/{{srcImageTag}}/contrib/init/systemd/docker.service \ - https://raw.githubusercontent.com/moby/moby/v{{srcImageTag}}/contrib/init/systemd/docker.service \ - -O /etc/systemd/system/docker.service || true - wget https://raw.githubusercontent.com/moby/moby/{{srcImageTag}}/contrib/init/systemd/docker.socket \ - https://raw.githubusercontent.com/moby/moby/v{{srcImageTag}}/contrib/init/systemd/docker.socket \ - -O /etc/systemd/system/docker.socket || true + wget https://raw.githubusercontent.com/moby/moby/{{gitCommit}}/contrib/init/systemd/docker.service \ + -O /etc/systemd/system/docker.service + wget https://raw.githubusercontent.com/moby/moby/{{gitCommit}}/contrib/init/systemd/docker.socket \ + -O /etc/systemd/system/docker.socket sed -i 's|^ExecStart=.*|ExecStart=/usr/local/bin/dockerd -H fd://|' /etc/systemd/system/docker.service sed -i 's|containerd.service||' /etc/systemd/system/docker.service diff --git a/src/docker/install.ts b/src/docker/install.ts index a6c8ca8d..2b673a9c 100644 --- a/src/docker/install.ts +++ b/src/docker/install.ts @@ -34,6 +34,11 @@ import {Util} from '../util'; import {limaYamlData, dockerServiceLogsPs1, setupDockerWinPs1} from './assets'; import {GitHubRelease} from '../types/github'; import {HubRepository} from '../hubRepository'; +import {Index} from '../types/oci'; +import {MEDIATYPE_IMAGE_INDEX_V1} from '../types/oci/mediatype'; +import {MEDIATYPE_IMAGE_MANIFEST_LIST_V2} from '../types/docker/mediatype'; +import {Manifest} from '../types/oci/manifest'; +import {ImageConfig} from '../types/oci/config'; export interface InstallSourceImage { type: 'image'; @@ -71,6 +76,8 @@ export class Install { private _version: string | undefined; private _toolDir: string | undefined; + private gitCommit: string | undefined; + private readonly limaInstanceName = 'docker-actions-toolkit'; constructor(opts: InstallOpts) { @@ -127,12 +134,21 @@ export class Install { const cli = await HubRepository.build('dockereng/cli-bin'); extractFolder = await cli.extractImage(tag); + const moby = await HubRepository.build('moby/moby-bin'); if (['win32', 'linux'].includes(platform)) { core.info(`Downloading dockerd from moby/moby-bin:${tag}`); - const moby = await HubRepository.build('moby/moby-bin'); await moby.extractImage(tag, extractFolder); } else if (platform == 'darwin') { - // On macOS, the docker daemon binary will be downloaded inside the lima VM + // On macOS, the docker daemon binary will be downloaded inside the lima VM. + // However, we will get the exact git revision from the image config + // to get the matching systemd unit files. + const manifest = await moby.getPlatformManifest(tag); + const config = await moby.getConfig(manifest.config.digest); + this.gitCommit = config.Labels?.['org.opencontainers.image.revision']; + if (!this.gitCommit) { + core.warning(`No git revision can be determined from the image. Will use master.`); + this.gitCommit = 'master'; + } } else { core.warning(`dockerd not supported on ${platform}, only the Docker cli will be available`); } @@ -229,6 +245,7 @@ export class Install { customImages: Install.limaCustomImages(), daemonConfig: limaDaemonConfig, dockerSock: `${limaDir}/docker.sock`, + gitCommit: this.gitCommit || this._version || 'master', srcType: src.type, srcArchiveVersion: this._version, // Use the resolved version (e.g. latest -> 27.4.0) srcArchiveChannel: srcArchive.channel, diff --git a/src/hubRepository.ts b/src/hubRepository.ts index e2a88845..6dd56001 100644 --- a/src/hubRepository.ts +++ b/src/hubRepository.ts @@ -19,10 +19,11 @@ import {Index} from './types/oci'; import os from 'os'; import * as core from '@actions/core'; import {Manifest} from './types/oci/manifest'; +import {ImageConfig} from './types/oci/config'; import * as tc from '@actions/tool-cache'; import fs from 'fs'; -import {MEDIATYPE_IMAGE_INDEX_V1, MEDIATYPE_IMAGE_MANIFEST_V1} from './types/oci/mediatype'; -import {MEDIATYPE_IMAGE_MANIFEST_V2, MEDIATYPE_IMAGE_MANIFEST_LIST_V2} from './types/docker/mediatype'; +import {MEDIATYPE_IMAGE_CONFIG_V1, MEDIATYPE_IMAGE_INDEX_V1, MEDIATYPE_IMAGE_MANIFEST_V1} from './types/oci/mediatype'; +import {MEDIATYPE_IMAGE_MANIFEST_V2, MEDIATYPE_IMAGE_MANIFEST_LIST_V2, MEDIATYPE_IMAGE_CONFIG_V1 as DOCKER_MEDIATYPE_IMAGE_CONFIG_V1} from './types/docker/mediatype'; import {DockerHub} from './dockerhub'; export class HubRepository { @@ -40,15 +41,19 @@ export class HubRepository { return new HubRepository(repository, token); } - // Unpacks the image layers and returns the path to the extracted image. - // Only OCI indexes/manifest list are supported for now. - public async extractImage(tag: string, destDir?: string): Promise { - const index = await this.getManifest(tag); + public async getPlatformManifest(tagOrDigest: string): Promise { + const index = await this.getManifest(tagOrDigest); if (index.mediaType != MEDIATYPE_IMAGE_INDEX_V1 && index.mediaType != MEDIATYPE_IMAGE_MANIFEST_LIST_V2) { throw new Error(`Unsupported image media type: ${index.mediaType}`); } const digest = HubRepository.getPlatformManifestDigest(index); - const manifest = await this.getManifest(digest); + return await this.getManifest(digest); + } + + // Unpacks the image layers and returns the path to the extracted image. + // Only OCI indexes/manifest list are supported for now. + public async extractImage(tag: string, destDir?: string): Promise { + const manifest = await this.getPlatformManifest(tag); const paths = manifest.layers.map(async layer => { const url = this.blobUrl(layer.digest); @@ -99,11 +104,19 @@ export class HubRepository { } public async getManifest(tagOrDigest: string): Promise { - const url = `https://registry-1.docker.io/v2/${this.repo}/manifests/${tagOrDigest}`; + return this.registryGet(tagOrDigest, 'manifests', [MEDIATYPE_IMAGE_INDEX_V1, MEDIATYPE_IMAGE_MANIFEST_LIST_V2, MEDIATYPE_IMAGE_MANIFEST_V1, MEDIATYPE_IMAGE_MANIFEST_V2]); + } + + public async getConfig(tagOrDigest: string): Promise { + return this.registryGet(tagOrDigest, 'blobs', [MEDIATYPE_IMAGE_CONFIG_V1, DOCKER_MEDIATYPE_IMAGE_CONFIG_V1]); + } + + public async registryGet(tagOrDigest: string, endpoint: 'manifests' | 'blobs', accept: Array): Promise { + const url = `https://registry-1.docker.io/v2/${this.repo}/${endpoint}/${tagOrDigest}`; const headers = { Authorization: `Bearer ${this.token}`, - Accept: [MEDIATYPE_IMAGE_INDEX_V1, MEDIATYPE_IMAGE_MANIFEST_LIST_V2, MEDIATYPE_IMAGE_MANIFEST_V1, MEDIATYPE_IMAGE_MANIFEST_V2].join(', ') + Accept: accept.join(', ') }; const resp = await HubRepository.http.get(url, headers); const body = await resp.readBody(); diff --git a/src/types/docker/mediatype.ts b/src/types/docker/mediatype.ts index d06d1e96..94686e60 100644 --- a/src/types/docker/mediatype.ts +++ b/src/types/docker/mediatype.ts @@ -17,3 +17,5 @@ export const MEDIATYPE_IMAGE_MANIFEST_LIST_V2 = 'application/vnd.docker.distribution.manifest.list.v2+json'; export const MEDIATYPE_IMAGE_MANIFEST_V2 = 'application/vnd.docker.distribution.manifest.v2+json'; + +export const MEDIATYPE_IMAGE_CONFIG_V1 = 'application/vnd.docker.container.image.v1+json'; diff --git a/src/types/oci/mediatype.ts b/src/types/oci/mediatype.ts index fca5c00c..26c52444 100644 --- a/src/types/oci/mediatype.ts +++ b/src/types/oci/mediatype.ts @@ -23,3 +23,5 @@ export const MEDIATYPE_IMAGE_INDEX_V1 = 'application/vnd.oci.image.index.v1+json export const MEDIATYPE_IMAGE_LAYER_V1 = 'application/vnd.oci.image.layer.v1.tar'; export const MEDIATYPE_EMPTY_JSON_V1 = 'application/vnd.oci.empty.v1+json'; + +export const MEDIATYPE_IMAGE_CONFIG_V1 = 'application/vnd.oci.image.config.v1+json';