Compare commits

..

No commits in common. "master" and "v3.2.0" have entirely different histories.

32 changed files with 7098 additions and 3320 deletions

3
.eslintignore Normal file
View File

@ -0,0 +1,3 @@
/dist/**
/coverage/**
/node_modules/**

24
.eslintrc.json Normal file
View File

@ -0,0 +1,24 @@
{
"env": {
"node": true,
"es6": true,
"jest": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:jest/recommended",
"plugin:prettier/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"@typescript-eslint",
"jest",
"prettier"
]
}

View File

@ -14,7 +14,7 @@ to the public under the [project's open source license](LICENSE).
5. Format code and build javascript artifacts: `docker buildx bake pre-checkin`
6. Validate all code has correctly formatted and built: `docker buildx bake validate`
7. Push to your fork and [submit a pull request](https://github.com/docker/setup-qemu-action/compare)
8. Pat yourself on the back and wait for your pull request to be reviewed and merged.
8. Pat your self on the back and wait for your pull request to be reviewed and merged.
Here are a few things you can do that will increase the likelihood of your pull request being accepted:

View File

@ -5,7 +5,6 @@ concurrency:
cancel-in-progress: true
on:
workflow_dispatch:
schedule:
- cron: '0 10 * * *'
push:
@ -22,7 +21,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
-
name: Set up QEMU
id: qemu
@ -45,7 +44,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
-
name: Set up QEMU
id: qemu
@ -62,7 +61,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
-
name: Stop docker
run: |
@ -80,45 +79,3 @@ jobs:
echo "::error::Should have failed"
exit 1
fi
cache-image:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
cache:
- true
- false
steps:
-
name: Checkout
uses: actions/checkout@v6
-
name: Set up QEMU
id: qemu
uses: ./
with:
image: tonistiigi/binfmt:master
cache-image: ${{ matrix.cache }}
-
name: Available platforms
run: echo ${{ steps.qemu.outputs.platforms }}
version:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
image:
- tonistiigi/binfmt:master
- tonistiigi/binfmt:latest
- tonistiigi/binfmt:qemu-v7.0.0
steps:
-
name: Checkout
uses: actions/checkout@v6
-
name: Set up QEMU
uses: ./
with:
image: ${{ matrix.image }}

View File

@ -1,17 +0,0 @@
name: pr-assign-author
permissions:
contents: read
on:
pull_request_target:
types:
- opened
- reopened
jobs:
run:
uses: crazy-max/.github/.github/workflows/pr-assign-author.yml@1b673f36fad86812f538c1df9794904038a23cbf
permissions:
contents: read
pull-requests: write

View File

@ -1,21 +0,0 @@
name: publish
on:
release:
types:
- published
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
packages: write
steps:
-
name: Checkout
uses: actions/checkout@v6
-
name: Publish
uses: actions/publish-immutable-action@v0.0.4

View File

@ -17,16 +17,15 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
-
name: Test
uses: docker/bake-action@v7
uses: docker/bake-action@v5
with:
source: .
targets: test
-
name: Upload coverage
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@v4
with:
files: ./coverage/clover.xml
file: ./coverage/clover.xml
token: ${{ secrets.CODECOV_TOKEN }}

View File

@ -1,49 +0,0 @@
name: update-dist
on:
pull_request:
types:
- opened
- synchronize
jobs:
update-dist:
if: github.actor == 'dependabot[bot]'
runs-on: ubuntu-latest
steps:
-
name: GitHub auth token from GitHub App
id: docker-read-app
uses: actions/create-github-app-token@v2
with:
app-id: ${{ secrets.GHACTIONS_REPO_WRITE_APP_ID }}
private-key: ${{ secrets.GHACTIONS_REPO_WRITE_APP_PRIVATE_KEY }}
owner: docker
-
name: Checkout
uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 0
token: ${{ steps.docker-read-app.outputs.token || github.token }}
-
name: Build
uses: docker/bake-action@v7
with:
source: .
targets: build
-
name: Commit and push dist
run: |
if [ -n "$(git status --porcelain -- dist)" ]; then
(
set -x
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add dist
git commit -m "chore: update generated content"
git push
)
else
echo "No changes in dist"
fi

View File

@ -15,17 +15,16 @@ jobs:
prepare:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.generate.outputs.matrix }}
targets: ${{ steps.targets.outputs.matrix }}
steps:
-
name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
-
name: Generate matrix
id: generate
uses: docker/bake-action/subaction/matrix@v7
with:
target: validate
name: Targets matrix
id: targets
run: |
echo "matrix=$(docker buildx bake validate --print | jq -cr '.group.validate.targets')" >> $GITHUB_OUTPUT
validate:
runs-on: ubuntu-latest
@ -34,10 +33,13 @@ jobs:
strategy:
fail-fast: false
matrix:
include: ${{ fromJson(needs.prepare.outputs.matrix) }}
target: ${{ fromJson(needs.prepare.outputs.targets) }}
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Validate
uses: docker/bake-action@v7
uses: docker/bake-action@v5
with:
targets: ${{ matrix.target }}

View File

@ -6,5 +6,6 @@
"singleQuote": true,
"trailingComma": "none",
"bracketSpacing": false,
"arrowParens": "avoid"
"arrowParens": "avoid",
"parser": "typescript"
}

Binary file not shown.

View File

@ -1,9 +1,3 @@
# https://yarnpkg.com/configuration/yarnrc
compressionLevel: mixed
enableGlobalCache: false
enableHardenedMode: true
logFilters:
- code: YN0013
level: discard
@ -11,9 +5,9 @@ logFilters:
level: discard
- code: YN0076
level: discard
- code: YN0086
level: discard
nodeLinker: node-modules
npmAuthToken: "${NODE_AUTH_TOKEN:-fallback}"
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"

View File

@ -32,33 +32,19 @@ jobs:
steps:
-
name: Set up QEMU
uses: docker/setup-qemu-action@v4
uses: docker/setup-qemu-action@v3
```
> [!NOTE]
> If you are using [`docker/setup-buildx-action`](https://github.com/docker/setup-buildx-action),
> this action should come before it:
>
> ```yaml
> -
> name: Set up QEMU
> uses: docker/setup-qemu-action@v4
> -
> name: Set up Docker Buildx
> uses: docker/setup-buildx-action@v4
> ```
## Customizing
### inputs
The following inputs can be used as `step.with` keys:
| Name | Type | Default | Description |
|---------------|--------|-------------------------------------------------------------------------------|----------------------------------------------------|
| `image` | String | [`tonistiigi/binfmt:latest`](https://hub.docker.com/r/tonistiigi/binfmt/tags) | QEMU static binaries Docker image |
| `platforms` | String | `all` | Platforms to install (e.g., `arm64,riscv64,arm`) |
| `cache-image` | Bool | `true` | Cache binfmt image to GitHub Actions cache backend |
| Name | Type | Default | Description |
|-------------|--------|-------------------------------------------------------------------------------|--------------------------------------------------|
| `image` | String | [`tonistiigi/binfmt:latest`](https://hub.docker.com/r/tonistiigi/binfmt/tags) | QEMU static binaries Docker image |
| `platforms` | String | `all` | Platforms to install (e.g., `arm64,riscv64,arm`) |
### outputs

View File

@ -1,6 +1,6 @@
import {beforeEach, describe, expect, test} from 'vitest';
import {beforeEach, describe, expect, test} from '@jest/globals';
import * as context from '../src/context.js';
import * as context from '../src/context';
describe('getInputs', () => {
beforeEach(() => {
@ -13,46 +13,38 @@ describe('getInputs', () => {
});
// prettier-ignore
const cases: [number, Map<string, string>, context.Inputs][] = [
test.each([
[
0,
new Map<string, string>([
['cache-image', 'true'],
]),
new Map<string, string>([]),
{
image: 'docker.io/tonistiigi/binfmt:latest',
platforms: 'all',
cacheImage: true,
}
} as context.Inputs
],
[
1,
new Map<string, string>([
['image', 'docker/binfmt:latest'],
['platforms', 'arm64,riscv64,arm'],
['cache-image', 'false'],
]),
{
image: 'docker/binfmt:latest',
platforms: 'arm64,riscv64,arm',
cacheImage: false,
}
} as context.Inputs
],
[
2,
new Map<string, string>([
['platforms', 'arm64, riscv64, arm '],
['cache-image', 'true'],
]),
{
image: 'docker.io/tonistiigi/binfmt:latest',
platforms: 'arm64,riscv64,arm',
cacheImage: true,
}
} as context.Inputs
]
];
test.each(cases)(
'[%d] given %o as inputs, returns %o',
])(
'[%d] given %p as inputs, returns %p',
async (num: number, inputs: Map<string, string>, expected: context.Inputs) => {
inputs.forEach((value: string, name: string) => {
setInput(name, value);

View File

@ -1,12 +0,0 @@
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'docker-setup-qemu-action-'));
process.env = Object.assign({}, process.env, {
TEMP: tmpDir,
GITHUB_REPOSITORY: 'docker/setup-qemu-action',
RUNNER_TEMP: path.join(tmpDir, 'runner-temp'),
RUNNER_TOOL_CACHE: path.join(tmpDir, 'runner-tool-cache')
});

View File

@ -15,16 +15,11 @@ inputs:
description: 'Platforms to install (e.g. arm64,riscv64,arm)'
default: 'all'
required: false
cache-image:
description: 'Cache binfmt image to GitHub Actions cache backend'
default: 'true'
required: false
outputs:
platforms:
description: 'Available platforms (comma separated)'
runs:
using: 'node24'
using: 'node20'
main: 'dist/index.js'
post: 'dist/index.js'

View File

@ -1,13 +1,12 @@
# syntax=docker/dockerfile:1
ARG NODE_VERSION=24
ARG NODE_VERSION=20
FROM node:${NODE_VERSION}-alpine AS base
RUN apk add --no-cache cpio findutils git rsync
RUN apk add --no-cache cpio findutils git
WORKDIR /src
RUN --mount=type=bind,target=.,rw \
--mount=type=cache,target=/src/.yarn/cache <<EOT
set -e
corepack enable
yarn --version
yarn config set --home enableTelemetry 0
@ -35,27 +34,18 @@ RUN --mount=type=bind,target=.,rw <<EOT
EOT
FROM deps AS build
RUN --mount=target=/context \
RUN --mount=type=bind,target=.,rw \
--mount=type=cache,target=/src/.yarn/cache \
--mount=type=cache,target=/src/node_modules <<EOT
set -e
rsync -a /context/. .
rm -rf dist
yarn run build
mkdir /out
cp -r dist /out
EOT
--mount=type=cache,target=/src/node_modules \
yarn run build && mkdir /out && cp -Rf dist /out/
FROM scratch AS build-update
COPY --from=build /out /
FROM build AS build-validate
RUN --mount=target=/context \
--mount=target=.,type=tmpfs <<EOT
RUN --mount=type=bind,target=.,rw <<EOT
set -e
rsync -a /context/. .
git add -A
rm -rf dist
cp -rf /out/* .
if [ -n "$(git status --porcelain -- dist)" ]; then
echo >&2 'ERROR: Build result differs. Please build first with "docker buildx bake build"'
@ -68,7 +58,8 @@ FROM deps AS format
RUN --mount=type=bind,target=.,rw \
--mount=type=cache,target=/src/.yarn/cache \
--mount=type=cache,target=/src/node_modules \
yarn run format && mkdir /out && find . -name '*.ts' -not -path './node_modules/*' -not -path './.yarn/*' | cpio -pdm /out
yarn run format \
&& mkdir /out && find . -name '*.ts' -not -path './node_modules/*' -not -path './.yarn/*' | cpio -pdm /out
FROM scratch AS format-update
COPY --from=format /out /
@ -83,7 +74,7 @@ FROM deps AS test
RUN --mount=type=bind,target=.,rw \
--mount=type=cache,target=/src/.yarn/cache \
--mount=type=cache,target=/src/node_modules \
yarn run test --coverage --coverage.reportsDirectory=/tmp/coverage
yarn run test --coverage --coverageDirectory=/tmp/coverage
FROM scratch AS test-coverage
COPY --from=test /tmp/coverage /

105
dist/index.js generated vendored

File diff suppressed because one or more lines are too long

2
dist/index.js.map generated vendored

File diff suppressed because one or more lines are too long

2456
dist/licenses.txt generated vendored

File diff suppressed because it is too large Load Diff

3
dist/package.json generated vendored
View File

@ -1,3 +0,0 @@
{
"type": "module"
}

1
dist/sourcemap-register.cjs generated vendored

File diff suppressed because one or more lines are too long

1
dist/sourcemap-register.js generated vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,9 +1,3 @@
target "_common" {
args = {
BUILDKIT_CONTEXT_KEEP_GIT_DIR = 1
}
}
group "default" {
targets = ["build"]
}
@ -17,49 +11,42 @@ group "validate" {
}
target "build" {
inherits = ["_common"]
dockerfile = "dev.Dockerfile"
target = "build-update"
output = ["."]
}
target "build-validate" {
inherits = ["_common"]
dockerfile = "dev.Dockerfile"
target = "build-validate"
output = ["type=cacheonly"]
}
target "format" {
inherits = ["_common"]
dockerfile = "dev.Dockerfile"
target = "format-update"
output = ["."]
}
target "lint" {
inherits = ["_common"]
dockerfile = "dev.Dockerfile"
target = "lint"
output = ["type=cacheonly"]
}
target "vendor" {
inherits = ["_common"]
dockerfile = "dev.Dockerfile"
target = "vendor-update"
output = ["."]
}
target "vendor-validate" {
inherits = ["_common"]
dockerfile = "dev.Dockerfile"
target = "vendor-validate"
output = ["type=cacheonly"]
}
target "test" {
inherits = ["_common"]
dockerfile = "dev.Dockerfile"
target = "test-coverage"
output = ["./coverage"]

View File

@ -1,52 +0,0 @@
import {defineConfig} from 'eslint/config';
import js from '@eslint/js';
import tseslint from '@typescript-eslint/eslint-plugin';
import vitest from '@vitest/eslint-plugin';
import globals from 'globals';
import eslintConfigPrettier from 'eslint-config-prettier/flat';
import eslintPluginPrettier from 'eslint-plugin-prettier';
export default defineConfig([
{
ignores: ['.yarn/**/*', 'coverage/**/*', 'dist/**/*']
},
js.configs.recommended,
...tseslint.configs['flat/recommended'],
eslintConfigPrettier,
{
languageOptions: {
globals: {
...globals.node
}
}
},
{
files: ['__tests__/**'],
...vitest.configs.recommended,
languageOptions: {
globals: {
...globals.node,
...vitest.environments.env.globals
}
},
rules: {
...vitest.configs.recommended.rules,
'vitest/no-conditional-expect': 'error',
'vitest/no-disabled-tests': 0
}
},
{
plugins: {
prettier: eslintPluginPrettier
},
rules: {
'prettier/prettier': 'error',
'@typescript-eslint/no-require-imports': [
'error',
{
allowAsImport: true
}
]
}
}
]);

29
jest.config.ts Normal file
View File

@ -0,0 +1,29 @@
import fs from 'fs';
import os from 'os';
import path from 'path';
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'docker-setup-qemu-action-'));
process.env = Object.assign({}, process.env, {
TEMP: tmpDir,
GITHUB_REPOSITORY: 'docker/setup-qemu-action',
RUNNER_TEMP: path.join(tmpDir, 'runner-temp'),
RUNNER_TOOL_CACHE: path.join(tmpDir, 'runner-tool-cache')
}) as {
[key: string]: string;
};
module.exports = {
clearMocks: true,
moduleFileExtensions: ['js', 'ts'],
testMatch: ['**/*.test.ts'],
transform: {
'^.+\\.ts$': 'ts-jest'
},
moduleNameMapper: {
'^csv-parse/sync': '<rootDir>/node_modules/csv-parse/dist/cjs/sync.cjs'
},
collectCoverageFrom: ['src/**/{!(main.ts),}.ts'],
coveragePathIgnorePatterns: ['lib/', 'node_modules/', '__mocks__/', '__tests__/'],
verbose: true
};

View File

@ -1,13 +1,16 @@
{
"name": "docker-setup-qemu",
"description": "Install QEMU static binaries",
"type": "module",
"main": "src/main.ts",
"scripts": {
"build": "ncc build src/main.ts --source-map --minify --license licenses.txt",
"lint": "eslint --max-warnings=0 .",
"format": "eslint --fix .",
"test": "vitest run"
"lint": "yarn run prettier && yarn run eslint",
"format": "yarn run prettier:fix && yarn run eslint:fix",
"eslint": "eslint --max-warnings=0 .",
"eslint:fix": "eslint --fix .",
"prettier": "prettier --check \"./**/*.ts\"",
"prettier:fix": "prettier --write \"./**/*.ts\"",
"test": "jest"
},
"repository": {
"type": "git",
@ -20,25 +23,24 @@
],
"author": "Docker Inc.",
"license": "Apache-2.0",
"packageManager": "yarn@4.9.2",
"packageManager": "yarn@3.6.3",
"dependencies": {
"@actions/core": "^3.0.0",
"@docker/actions-toolkit": "^0.79.0"
"@actions/core": "^1.10.1",
"@docker/actions-toolkit": "^0.35.0"
},
"devDependencies": {
"@eslint/js": "^9.39.3",
"@types/node": "^24.11.0",
"@typescript-eslint/eslint-plugin": "^8.56.1",
"@typescript-eslint/parser": "^8.56.1",
"@vercel/ncc": "^0.38.4",
"@vitest/coverage-v8": "^4.0.18",
"@vitest/eslint-plugin": "^1.6.9",
"eslint": "^9.39.3",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.5",
"globals": "^17.3.0",
"prettier": "^3.8.1",
"typescript": "^5.9.3",
"vitest": "^4.0.18"
"@types/node": "^20.12.12",
"@typescript-eslint/eslint-plugin": "^7.9.0",
"@typescript-eslint/parser": "^7.9.0",
"@vercel/ncc": "^0.38.1",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-jest": "^28.5.0",
"eslint-plugin-prettier": "^5.1.3",
"jest": "^29.7.0",
"prettier": "^3.2.5",
"ts-jest": "^29.1.2",
"ts-node": "^10.9.2",
"typescript": "^5.4.5"
}
}

View File

@ -1,16 +1,14 @@
import * as core from '@actions/core';
import {Util} from '@docker/actions-toolkit/lib/util.js';
import {Util} from '@docker/actions-toolkit/lib/util';
export interface Inputs {
image: string;
platforms: string;
cacheImage: boolean;
}
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')
platforms: Util.getInputList('platforms').join(',') || 'all'
};
}

View File

@ -1,9 +1,8 @@
import * as context from './context';
import * as core from '@actions/core';
import * as actionsToolkit from '@docker/actions-toolkit';
import {Docker} from '@docker/actions-toolkit/lib/docker/docker.js';
import * as context from './context.js';
import {Docker} from '@docker/actions-toolkit/lib/docker/docker';
interface Platforms {
supported: string[];
@ -21,11 +20,7 @@ actionsToolkit.run(
});
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], {
await Docker.getExecOutput(['pull', input.image], {
ignoreReturnCode: true
}).then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) {
@ -34,8 +29,8 @@ actionsToolkit.run(
});
});
await core.group(`Binfmt version`, async () => {
await Docker.getExecOutput(['run', '--rm', '--privileged', input.image, '--version'], {
await core.group(`Image info`, async () => {
await Docker.getExecOutput(['image', 'inspect', input.image], {
ignoreReturnCode: true
}).then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) {

View File

@ -1,8 +1,9 @@
{
"compilerOptions": {
"module": "nodenext",
"moduleResolution": "nodenext",
"esModuleInterop": true,
"target": "es6",
"module": "commonjs",
"strict": true,
"newLine": "lf",
"outDir": "./lib",
"rootDir": "./src",
@ -11,7 +12,10 @@
"resolveJsonModule": true,
"useUnknownInCatchVariables": false,
},
"include": [
"src/**/*.ts"
"exclude": [
"./__tests__/**/*",
"./lib/**/*",
"node_modules",
"jest.config.ts"
]
}

View File

@ -1,16 +0,0 @@
import {defineConfig} from 'vitest/config';
export default defineConfig({
test: {
clearMocks: true,
environment: 'node',
setupFiles: ['./__tests__/setup.unit.ts'],
include: ['**/*.test.ts'],
coverage: {
provider: 'v8',
reporter: ['clover'],
include: ['src/**/*.ts'],
exclude: ['src/**/main.ts']
}
}
});

7358
yarn.lock

File diff suppressed because it is too large Load Diff