Rename 'archs' input and improve multiarch

Expand readme
Add example

Signed-off-by: Tim Etchells <tetchel@gmail.com>
This commit is contained in:
Tim Etchells 2021-07-09 10:29:04 -04:00
parent 098556ccc2
commit 5cf7e9c453
12 changed files with 670 additions and 424 deletions

71
.github/workflows/multiarch.yml vendored Normal file
View file

@ -0,0 +1,71 @@
name: Multiarch build
on:
push:
pull_request:
workflow_dispatch:
schedule:
- cron: '0 0 * * *' # every day at midnight
env:
IMAGE_NAME: hello-world-multiarch
IMAGE_TAG: latest
jobs:
build:
name: Build image using Buildah
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
arch: [ amd64, i386, arm64v8 ]
steps:
# Checkout buildah action github repository
- name: Checkout Buildah action
uses: actions/checkout@v2
with:
path: "buildah-build"
- name: Install qemu dependency
run: sudo apt-get install -y qemu-user-static
- name: Create Dockerfile
run: |
cat > Dockerfile<<EOF
ARG ARCH
FROM docker.io/\${ARCH}/alpine:3.14
RUN echo "hello world"
ENTRYPOINT [ "sh", "-c", "echo -n 'Machine: ' && uname -m && echo -n 'Bits: ' && getconf LONG_BIT && echo 'goodbye world'" ]
EOF
- name: Build Image
id: build_image
uses: ./buildah-build/
with:
image: ${{ env.IMAGE_NAME }}
tags: ${{ env.IMAGE_TAG }}
arch: ${{ matrix.arch }}
build-args: ARCH=${{ matrix.arch }}
dockerfiles: |
./Dockerfile
- name: Echo Outputs
run: |
echo "Image: ${{ steps.build_image.outputs.image }}"
echo "Tags: ${{ steps.build_image.outputs.tags }}"
- name: Check images created
run: buildah images | grep '${{ env.IMAGE_NAME }}'
- name: Check image metadata
run: |
set -x
buildah inspect ${{ steps.build_image.outputs.image }}:${{ env.IMAGE_TAG }} | jq ".OCIv1.architecture"
buildah inspect ${{ steps.build_image.outputs.image }}:${{ env.IMAGE_TAG }} | jq ".Docker.architecture"
- name: Run image
run: |
podman run --rm ${{ steps.build_image.outputs.image }}:${{ env.IMAGE_TAG }}

View file

@ -84,7 +84,7 @@ jobs:
-jar
spring-petclinic-*.jar
port: 8080
archs: amd64,arm64
arch: amd64
workdir: "."
- name: Echo Outputs

View file

@ -25,7 +25,7 @@ After building your image, use [push-to-registry](https://github.com/redhat-acti
| Input Name | Description | Default |
| ---------- | ----------- | ------- |
| archs | Architecture(s) to build the image(s) for. For multiple architectures, separate by a comma. Refer to [Multi arch builds](#multi-arch-builds) to setup the `qemu-user-static` dependency. | None (host architecture)
| arch | Label the image with this ARCH, instead of defaulting to the host architecture. Refer to [Multi arch builds](#multi-arch-builds) to setup the `qemu-user-static` dependency. | None (host architecture)
| build-args | Build arguments to pass to the Docker build using `--build-arg`, if using a Dockerfile that requires ARGs. Use the form `arg_name=arg_value`, and separate arguments with newlines. | None
| context | Path to directory to use as the build context. | `.`
| dockerfiles | The list of Dockerfile paths to perform a build using docker instructions. This is a multiline input to allow multiple Dockerfiles. | **Must be provided**
@ -41,7 +41,7 @@ After building your image, use [push-to-registry](https://github.com/redhat-acti
| Input Name | Description | Default |
| ---------- | ----------- | ------- |
| archs | Architecture(s) to build the image(s) for. For multiple architectures, separate by a comma. | None (host architecture)
| arch | Label the image with this ARCH, instead of defaulting to the host architecture. | None (host architecture)
| base-image | The base image to use for the container. | **Must be provided**
| content | Paths to files or directories to copy inside the container to create the file image. This is a multiline input to allow you to copy multiple files/directories.| None
| entrypoint | The entry point to set for the container. This is a multiline input; split arguments across lines. | None
@ -146,11 +146,31 @@ jobs:
## Multi arch builds
Cross-architecture builds from dockerfiles containing `RUN` instructions require `qemu-user-static` emulation registered in the Linux kernel. Run `sudo apt install -y qemu-user-static` on Debian-based container hosts. Or run the following registration command for other distributions:
```
Refer to the [multi-arch example](./.github/workflows/multiarch.yml).
### Emulating RUN instructions
Cross-architecture builds from dockerfiles containing `RUN` instructions require `qemu-user-static` emulation registered in the Linux kernel.
For example, run `sudo apt install qemu-user-static` on Debian hosts, or `sudo dnf install qemu-user-static` on Fedora.
You can run a [containerized version of the registration](https://hub.docker.com/r/tonistiigi/binfmt) if the package does not exist for your distribution:
```sh
sudo podman run --rm --privileged docker.io/tonistiigi/binfmt --install all
```
The registration remains active until the container host reboots.
This registration remains active until the host reboots.
### The `arch` input
The `arch` argument overrides the `ARCH` label in the output image. It does not actually affect the architectures the output image will run on. The image must still be built for the required architecture.
There is a simple example [in this issue](https://github.com/redhat-actions/buildah-build/issues/60#issuecomment-876552452).
### Creating a Multi-Arch Image List
Use the [buildah manifest](https://github.com/containers/buildah/blob/main/docs/buildah-manifest.md) command to bundle images into an image list, so multiple image can be referenced by the same repository tag.
There are examples and explanations of the `manifest` command [in this issue](https://github.com/containers/buildah/issues/1590).
This action does not support the `manifest` command at this time, but there is [an issue open](https://github.com/redhat-actions/buildah-build/issues/61).
## Using private images

View file

@ -47,11 +47,11 @@ inputs:
description: 'Set to true to build using the OCI image format instead of the Docker image format'
default: 'false'
required: false
arch:
description: 'Label the image with this ARCH, instead of defaulting to the host architecture.'
required: false
archs:
description: |
Architecture(s) to build the image(s) for. For multiple architectures,
separate by a comma.
default: 'amd64'
description: 'Alias for "arch". "arch" takes precedence if both are set.'
required: false
extra-args:
description: |

2
dist/index.js vendored

File diff suppressed because one or more lines are too long

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

0
git-hooks/pre-commit Normal file → Executable file
View file

907
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -23,19 +23,18 @@
"@actions/core": "^1.2.6",
"@actions/exec": "^1.0.4",
"@actions/io": "^1.0.2",
"ini": "^2.0.0",
"language-recognizer": "0.0.1"
"ini": "^2.0.0"
},
"devDependencies": {
"@redhat-actions/action-io-generator": "^1.5.0",
"@redhat-actions/eslint-config": "^1.2.11",
"@redhat-actions/eslint-config": "^1.3.2",
"@redhat-actions/tsconfig": "^1.1.1",
"@types/ini": "^1.3.30",
"@types/node": "^12",
"@typescript-eslint/eslint-plugin": "^4.14.1",
"@typescript-eslint/parser": "^4.14.1",
"@typescript-eslint/eslint-plugin": "^4.28.2",
"@typescript-eslint/parser": "^4.28.2",
"@vercel/ncc": "^0.25.1",
"eslint": "^7.18.0",
"typescript": "^4.0.5"
"eslint": "^7.30.0",
"typescript": "^4.3.5"
}
}

View file

@ -14,13 +14,13 @@ export interface BuildahConfigSettings {
envs?: string[];
port?: string;
workingdir?: string;
archs?: string;
arch?: string;
}
interface Buildah {
buildUsingDocker(
image: string, context: string, dockerFiles: string[], buildArgs: string[],
useOCI: boolean, archs: string, layers: string, extraArgs: string[]
useOCI: boolean, arch: string, layers: string, extraArgs: string[]
): Promise<CommandResult>;
from(baseImage: string): Promise<CommandResult>;
copy(container: string, contentToCopy: string[]): Promise<CommandResult | undefined>;
@ -63,12 +63,12 @@ export class BuildahCli implements Buildah {
async buildUsingDocker(
image: string, context: string, dockerFiles: string[], buildArgs: string[],
useOCI: boolean, archs: string, layers: string, extraArgs: string[]
useOCI: boolean, arch: string, layers: string, extraArgs: string[]
): Promise<CommandResult> {
const args: string[] = [ "bud" ];
if (archs) {
if (arch) {
args.push("--arch");
args.push(archs);
args.push(arch);
}
dockerFiles.forEach((file) => {
args.push("-f");
@ -131,9 +131,9 @@ export class BuildahCli implements Buildah {
args.push(env);
});
}
if (settings.archs) {
if (settings.arch) {
args.push("--arch");
args.push(settings.archs);
args.push(settings.arch);
}
if (settings.workingdir) {
args.push("--workingdir");

View file

@ -1,10 +1,15 @@
// This file was auto-generated by action-io-generator. Do not edit by hand!
export enum Inputs {
/**
* Architecture(s) to build the image(s) for. For multiple architectures,
* separate by a comma.
* Label the image with this ARCH, instead of defaulting to the host architecture.
* Required: false
* Default: "amd64"
* Default: None.
*/
ARCH = "arch",
/**
* Alias for "arch". "arch" takes precedence if both are set.
* Required: false
* Default: None.
*/
ARCHS = "archs",
/**

View file

@ -33,21 +33,20 @@ export async function run(): Promise<void> {
const tagsList: string[] = tags.split(" ");
// info message if user doesn't provides any tag
if (!tagsList.length) {
if (tagsList.length === 0) {
core.info(`Input "${Inputs.TAGS}" is not provided, using default tag "${DEFAULT_TAG}"`);
tagsList.push(DEFAULT_TAG);
}
const newImage = `${image}:${tagsList[0]}`;
const useOCI = core.getInput(Inputs.OCI) === "true";
let archs: string | undefined = core.getInput(Inputs.ARCHS);
// remove white spaces (if any) in archs input
archs = archs.replace(/\s+/g, "");
const arch = getArch();
if (dockerFiles.length !== 0) {
await doBuildUsingDockerFiles(cli, newImage, workspace, dockerFiles, useOCI, archs);
await doBuildUsingDockerFiles(cli, newImage, workspace, dockerFiles, useOCI, arch);
}
else {
await doBuildFromScratch(cli, newImage, useOCI, archs);
await doBuildFromScratch(cli, newImage, useOCI, arch);
}
if (tagsList.length > 1) {
@ -58,7 +57,7 @@ export async function run(): Promise<void> {
}
async function doBuildUsingDockerFiles(
cli: BuildahCli, newImage: string, workspace: string, dockerFiles: string[], useOCI: boolean, archs: string
cli: BuildahCli, newImage: string, workspace: string, dockerFiles: string[], useOCI: boolean, arch: string
): Promise<void> {
if (dockerFiles.length === 1) {
core.info(`Performing build from Dockerfile`);
@ -81,12 +80,12 @@ async function doBuildUsingDockerFiles(
buildahBudExtraArgs = lines.flatMap((line) => line.split(" ")).map((arg) => arg.trim());
}
await cli.buildUsingDocker(
newImage, context, dockerFileAbsPaths, buildArgs, useOCI, archs, layers, buildahBudExtraArgs
newImage, context, dockerFileAbsPaths, buildArgs, useOCI, arch, layers, buildahBudExtraArgs
);
}
async function doBuildFromScratch(
cli: BuildahCli, newImage: string, useOCI: boolean, archs: string
cli: BuildahCli, newImage: string, useOCI: boolean, arch: string
): Promise<void> {
core.info(`Performing build from scratch`);
@ -107,7 +106,7 @@ async function doBuildFromScratch(
port,
workingdir: workingDir,
envs,
archs,
arch,
};
await cli.config(containerId, newImageConfig);
await cli.commit(containerId, newImage, useOCI);
@ -127,4 +126,19 @@ function getInputList(name: string): string[] {
);
}
function getArch(): string {
// 'arch' should be used over 'archs', see https://github.com/redhat-actions/buildah-build/issues/60
const archs = core.getInput(Inputs.ARCHS).replace(/\s+/g, "");
const arch = core.getInput(Inputs.ARCH).replace(/\s+/g, "");
if (arch && archs) {
core.warning(
`Please use only one input of "${Inputs.ARCH}" and "${Inputs.ARCHS}". "${Inputs.ARCH}" takes precedence, `
+ `so --arch argument will be "${arch}".`
);
}
return arch || archs;
}
run().catch(core.setFailed);