feat: binfmt image local cached

This commit is contained in:
문성하 2025-02-02 21:15:42 +09:00
parent f30d974279
commit ee123d82a5
No known key found for this signature in database
6 changed files with 106 additions and 4 deletions

3
.gitignore vendored
View File

@ -51,3 +51,6 @@ jspm_packages/
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
# IDE
.idea/

View File

@ -23,6 +23,7 @@ describe('getInputs', () => {
image: 'docker.io/tonistiigi/binfmt:latest',
platforms: 'all',
cacheImage: true,
localCachePath: ''
} as context.Inputs
],
[
@ -36,6 +37,7 @@ describe('getInputs', () => {
image: 'docker/binfmt:latest',
platforms: 'arm64,riscv64,arm',
cacheImage: false,
localCachePath: ''
} as context.Inputs
],
[
@ -48,6 +50,21 @@ describe('getInputs', () => {
image: 'docker.io/tonistiigi/binfmt:latest',
platforms: 'arm64,riscv64,arm',
cacheImage: true,
localCachePath: ''
} as context.Inputs
],
[
3,
new Map<string, string>([
['platforms', 'arm64'],
['cache-image', 'false'],
['local-cache-path', '/tmp/cache'],
]),
{
image: 'docker.io/tonistiigi/binfmt:latest',
platforms: 'arm64',
cacheImage: false,
localCachePath: '/tmp/cache'
} as context.Inputs
]
])(

View File

@ -19,6 +19,12 @@ inputs:
description: 'Cache binfmt image to GitHub Actions cache backend'
default: 'true'
required: false
local-cache-path:
description: >
Local path to store the binfmt image. Using this enables local caching instead of GitHub Actions cache.
Note: The "latest" tag won't auto-update - delete the cached file to fetch updates.
default: ''
required: false
outputs:
platforms:

View File

@ -5,12 +5,14 @@ export interface Inputs {
image: string;
platforms: string;
cacheImage: boolean;
localCachePath: string;
}
export function getInputs(): Inputs {
return {
image: core.getInput('image') || 'docker.io/tonistiigi/binfmt:latest',
platforms: Util.getInputList('platforms').join(',') || 'all',
cacheImage: core.getBooleanInput('cache-image')
cacheImage: core.getBooleanInput('cache-image'),
localCachePath: core.getInput('local-cache-path') || ''
};
}

57
src/local-cache.ts Normal file
View File

@ -0,0 +1,57 @@
import * as fs from 'fs';
import * as path from 'path';
import * as core from '@actions/core';
import {Docker} from '@docker/actions-toolkit/lib/docker/docker';
function getImageCacheFileName(imageName: string): string {
return `${imageName.replace(/[\/\:]/g, '-')}.tar`;
}
export async function loadDockerImageFromCache(localCachePath: string, imageName: string): Promise<void> {
const cacheFilePath = path.join(localCachePath, getImageCacheFileName(imageName));
try {
if (fs.existsSync(cacheFilePath)) {
await Docker.getExecOutput(['load', '-i', cacheFilePath], {
ignoreReturnCode: true
}).then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) {
throw new Error(res.stderr.match(/(.*)\s*$/)?.[0]?.trim() ?? 'unknown error');
}
core.info(`Loaded image from ${cacheFilePath}`);
});
} else {
core.info(`Cache file not found at ${cacheFilePath}, pulling image instead`);
await Docker.pull(imageName);
}
} catch (error) {
core.warning(`Failed to check/load cache file: ${error}`);
await Docker.pull(imageName);
}
}
export async function saveDockerImageToCache(localCachePath: string, imageName: string): Promise<void> {
const cacheFilePath = path.join(localCachePath, getImageCacheFileName(imageName));
try {
if (!fs.existsSync(localCachePath)) {
fs.mkdirSync(localCachePath, {recursive: true});
}
if (fs.existsSync(cacheFilePath)) {
core.info(`Cache file already exists at ${cacheFilePath}, skipping save`);
return;
}
await Docker.getExecOutput(['save', '-o', cacheFilePath, imageName], {
ignoreReturnCode: true
}).then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) {
throw new Error(res.stderr.match(/(.*)\s*$/)?.[0]?.trim() ?? 'unknown error');
}
core.info(`Saved image to ${cacheFilePath}`);
});
} catch (error) {
core.warning(`Failed to save image to cache file: ${error}`);
}
}

View File

@ -3,6 +3,7 @@ import * as core from '@actions/core';
import * as actionsToolkit from '@docker/actions-toolkit';
import {Docker} from '@docker/actions-toolkit/lib/docker/docker';
import {loadDockerImageFromCache, saveDockerImageToCache} from './local-cache';
interface Platforms {
supported: string[];
@ -19,9 +20,15 @@ actionsToolkit.run(
await Docker.printInfo();
});
await core.group(`Pulling binfmt Docker image`, async () => {
await Docker.pull(input.image, input.cacheImage);
});
if (input.localCachePath !== '') {
await core.group(`Pulling binfmt Docker image`, async () => {
await loadDockerImageFromCache(input.localCachePath, input.image);
});
} else {
await core.group(`Pulling binfmt Docker image`, async () => {
await Docker.pull(input.image, input.cacheImage);
});
}
await core.group(`Image info`, async () => {
await Docker.getExecOutput(['image', 'inspect', input.image], {
@ -56,5 +63,15 @@ actionsToolkit.run(
core.setOutput('platforms', platforms.supported.join(','));
});
});
},
// post
async () => {
const input: context.Inputs = context.getInputs();
if (input.localCachePath !== '') {
await core.group(`Saving binfmt Docker image`, async () => {
await saveDockerImageToCache(input.localCachePath, input.image);
});
}
}
);