This action is intended to build a Docker image separately from its dependencies using builkit.
Building images using this strategy can allow you to speed up your CI/CD pipeline by avoiding building the same bootstrap image every time.
Caching this dependency image can also speedup your production build if done correctly.
This action will build the intermediary image and push it to a repository.
Next time this action runs, the image will be donwloaded instead (if the intermediary_image_as
matches an existing image).
Then the action will only build/cache your intermediary image. Building the final image is still up to you.
You should build your final image using DOCKER_BUILDKIT=1
An important notion to understand is how this action decides if a new intermediary image is necessary.
If you're using this action your Dockerfile will have (at least) two FROM
statements. This action will consider that the first FROM statement
is your intermediary image, and the next FROM
statements depend on that image.
In order to get a value representing the need of a new image we need a hash of:
- all the files upon which the intermediary image depends (dependency locks, any scripts...)
- The first FROM statement of the Dockerfile (in case you change the 2nd image only, the intermediary does not need to be rebuilt)
The first hash is up to you, the developer. You can pass whatever value you want, and if no files are used, you can pass an empty string.
The second hash will be computed automatically by this action, it will read the Dockerfile
from the first line and the first FROM statement until the next and hash the read part.
jobs:
build_my_docker_image:
- name: Build docker image and dependencies
id: deps
uses: jsmrcaga/action-docker-build-dependencies
with:
dockerfile_path: '.'
docker_username: ${{ secrets.DOCKER_USERNAME }}
docker_password: ${{ secrets.DOCKER_PASSWORD }}
docker_registry: ghcr.io
# Allow the action to read and compute intermediary hash from given hash and Dockerfile
# Very useful if you change the "dependency" part of your Dockerfile
compute_dockerfile_hash: true
intermediary_hash: hash-of-my-dependencies
intermediary_image_as: my-image
# Url WITHOUT tag. The tag is the computed hash
intermediary_image_url: https://ghcr.io/OWNER/my_intermediary_image
intermediary_build_flags: --build-arg NODE_ENV="development"
# Pushes the intermediary image in after your workflow runs
should_push_intermediary_image: true
- name: Build final image
run: DOCKER_BUILDKIT=1 docker build -t my_image:my_tag --build-arg BASE_IMAGE=${{ steps.deps.outputs.intermediary_tag }} .
Name | Required | Description |
---|---|---|
dockerfile_path |
No | The path to your Dockerfile's directory. Used to find the Dockerfile to hash and also to run the docker build command.g Defaults to ./ |
docker_username |
No | Username to login to Docker registry |
docker_password |
No | Password to login to Docker registry |
docker_registry |
No | Registry to which to login. Defaults to "empty" which in turn defaults to Docker Hub |
compute_dockerfile_hash |
No | Defaults to false . If this action should compute a hash of the first part of your Dockerfile (from the 1st line until the 2nd FROM ) |
intermediary_hash |
Yes | Should be the hash of any necessary files (ex: package.json, package-lock.json, requirements.txt ...) and will be used to compare if a new image is necessary or if the one in cache can be used. This will be rehashed with the Dockerfile hash if necessary. |
intermediary_image_as |
Yes | The name of the intermediary file as described in your Dockerfile. This will be used to build that image using --target |
intermediary_image_url |
Yes | This is the url without version used to find or publish the intermediary image. I personally use GitHub registry as a cache and upload my images where necessary (Docker Hub/ECR) |
intermediary_build_flags |
No | Any build flags you would like to append to the build command |
should_push_intermediary_image |
No | Defaults to true . If this action should push your intermediary image after your workflow runs |
The build command template looks like this:
docker build
--pull
--no-cache
--target ${ inputs.intermediary_image_as }
-t ${ inputs.intermediary_image_url }:${final_hash}
${ inputs.intermediary_build_flags }
${ inputs.dockerfile_path }
on:
release:
types: [published]
jobs:
build_my_docker_image:
runs-on: ubuntu-latest
- name: Hash dependency files
id: deps_hash
echo: "::set-output name=dependency_hash::${{ hashFiles('package.json', 'package-lock.json') }}"
- name: Build docker image and dependencies
uses: jsmrcaga/action-docker-build-dependencies
with:
docker_username: ${{ secrets.DOCKER_USERNAME }}
docker_password: ${{ secrets.DOCKER_PASSWORD }}
docker_registry: ghcr.io
intermediary_hash: ${{ steps.deps_hash.outputs.dependency_hash }}
intermediary_image_as: intermediary_image_as_in_dockerfile
# Note that your image name can be whatever you want (after my_username)
intermediary_image_url: https://ghcr.io/my_username/my_image_name
intermediary_build_flags: --build-arg NODE_ENV="development"
# Automatic hash will begin here
ARG base_image=intermediary_image_as_in_dockerfile
# This is the name of the intermediary image you need to set on the workflow config
FROM node as intermediary_image_as_in_dockerfile
COPY ./package.json /myapp
COPY ./package-lock.json /myapp
npm i
# Automatic hash will end here
# Note the $base_image is the arg name
FROM $base_image
COPY . /myapp
CMD run my app