Docker images are basically zip files, why should building them take any more privileges than writing files? This is a zero-privilege, zero-capability, zero-permission, zero-container, zero-chroot, tiny OCI image creator.
All it can do is take a base image and add files to it, updating standard metadata (command, environment, ports), and pushing the result somewhere.
It's has both a CLI command and a public API for use in other Go code.
go install github.com/andrewbaxter/dinker
See https://github.com/andrewbaxter/terrars/tree/master/helloworld which has an example of creating a statically linked binary and publishing it as a minimal Docker image.
In order to push images you need to set Settings / Code and automation / Actions / General / Workflow permissions to Read and write permissions.
Add this workflow (using rust, for example) in .github/workflows/build.yaml:
name: Build
on:
push:
tags:
- "*"
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
target: x86_64-unknown-linux-musl
- uses: actions-rs/cargo@v1
with:
command: build
args: --target x86_64-unknown-linux-musl --release
- env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
cat > dinker.json << ELEPHANT
{
"dests": [
{
"ref": "docker://ghcr.io/andrewbaxter/somewords:$GITHUB_REF_NAME",
"user": "$GITHUB_ACTOR",
"password": "$GITHUB_TOKEN"
},
{
"ref": "docker://ghcr.io/andrewbaxter/somewords:latest",
"user": "$GITHUB_ACTOR",
"password": "$GITHUB_TOKEN"
}
],
"arch": "amd64",
"os": "linux",
"files": [
{
"source": "target/x86_64-unknown-linux-musl/release/somewords",
"mode": "755"
}
]
}
ELEPHANT
- uses: docker://ghcr.io/andrewbaxter/dinker:latest
with:
args: /dinker dinker.jsonThis is an example, where I have a Go binary hello in my current directory.
-
Create a config file named
dinker.jsonlike{ "from": "alpine.tar", "from_pull": "docker://alpine:3.17.0", "dests": [ { "ref": "docker-daemon:hello:latest", "host": "unix:///var/run/docker2.sock" } ], "files": [ { "source": "hello", "dest": "hello", "mode": "755" } ], "cmd": ["/hello"] }This says to base the image on Alpine, add
helloat the root of the filesystem, and by default run it.If
alpine.tardoesn't exist locally, pull it from the public registry.When built, push it to my private registry at
localhost:5000. -
Run
dinker dinker.json -
Done!
There's one function: dinkerlib.BuildImage()
It takes a path to a local oci-image tar file, and an output directory name. It returns a hash of the inputs as used in the interpolation of dest on the command line above.
The image is constructed in the directory with the OCI layout, but it isn't put into a tar file or pushed anywhere - you can convert it to other formats or upload it using Image in "github.com/containers/image/v5/copy", with a source reference generated using Transport.ParseReference in "github.com/containers/image/v5/copy".
The json file has these options:
-
destAn array of places to save or push the built image.
The options for the elements are:
Required
-
refWhere to save the built image, using this format: https://github.com/containers/image/blob/main/docs/containers-transports.5.md.This is a pattern - you can add the following strings which will be replaced with generated information:
-
{hash}- A sha256 sum of all the information used to generate the image (note: this should be stable but has no formal specification and is unrelated to the pushed manifest hash). -
{short_hash}- The first hex digits of the hash
-
Optional
-
userCredentials for pushing
-
passwordCredentials for pushing
-
httpTrue if this dest is over http (disable tls validation)
-
hostIf using the
docker-daemontransport which doesn't support host specification, override the default docker daemon.
-
-
filesFiles to add to the image. This is an array of objects with these fields:
-
source- Required, the location of the file on the building system -
dest- Optional, where to store the file in the image. If not specified, puts it at the root of the image with the same filename assource. -
mode- Octal string with file mode (ex: 644)
-
-
archDefaults to
fromimage architecture -
osDefaults to
fromimage os
-
fromAdd onto the layers from this image (like
FROMin Docker). This is a path to an OCI image archive tar file. If the file does not exist, it will download the image usingfrom_pulland store it here. If not specified, use no base image (this will produce a single layer image with just the specified files). -
from_pullWhere to pull the
fromimage if it doesn't exist, using this format: https://github.com/containers/image/blob/main/docs/containers-transports.5.md. -
from_userCredentials for
from_pullif necessary -
from_passwordCredentials for
from_pullif necessary -
from_httpTrue if
from_pullsource is over http (disable tls validation) -
from_hostIf using the
docker-daemontransport which doesn't support host specification, override the default docker daemon. -
add_envRecord with string key-value pairs. Add additional default environment values
-
clear_envBoolean. If true, don't inherit environment variables from
fromimage. -
working_dirContainer working directory, defaults to
fromimage working directory. -
userUser id to run process in container as. Defaults to value in
fromimage -
entrypointArray of strings. See Docker documentation for details. This is not inherited from the base image.
-
cmdArray of strings. See Docker documentation for details. This is not inherited from the base image.
-
portsPorts within the container to expose. This is an array of records with fields:
-
port- Required, the port that the program within the container listens on -
transport- Optional, defaults totcp.tcporudp.
These are not inherited from the base image.
-
-
labelsString key-value record. Arbitrary metadata. These are not inherited from the base image.
-
stop_signalThe signal to use when stopping the container. Values like
SIGTERMSIGINTSIGQUIT. This is not inherited from the base image.