This project is a GitHub Action that builds a Docker-compatible image on various platforms, tags the image, and then publishes a Docker image to various registries. The action can be triggered by a push to a branch, a pull request, a release, a schedule, or a manual trigger.
The images are tagged with SBOM attestations and OCI metadata.
The goal is to make it easy to build and publish images to various registries without a lot of configuration. When the project started, it was just to build for DockerHub with an image name based on the user's DockerHub username and the name of the repository. The project then grew to support the GitHub Container Registry (GHCR). Labels and tags were added, followed by multiple platforms (e.g., AMD64 and various ARM platforms that Single-Board Computers (SBCs) like Raspberry Pis and such).
The project then grew to support more registries, including Quay, Harbor, ECR, and custom registries.
The first use case was to build and publish images that could be used to run containers that would run on a laptop, shortly followed by images that could run on a Raspberry Pi.
The action will add OCI metadata to the image, including the following:
- title
- description
- url
- source
- version
- created
- revision
- license
The action will also update the description of the image on DockerHub, Quay, and Harbor using the README.md file in the repository.
The action will also generate Software Bill of Materials (SBOM) files in SPDX format and use them to generate attestations for each image that is built.
The action is a composite action that uses several other actions to build and publish images. The action uses the following actions:
- docker/build-push-action
- docker/login-action
- docker/setup-qemu-action
- docker/setup-buildx-action
- docker/metadata-action
- docker/build-push-action
- anchore/sbom-action
- actions/attest-sbom
- christian-korneck/update-container-description-action
By default, the action builds images for the following platforms:
- linux/amd64
- linux/arm64
- linux/arm/v6
- linux/arm/v7
The action can be configured to build images for other platforms by setting the
platforms
input. The input should be a comma-separated list of platforms.
The arm
images are built using QEMU emulation so that they can run on
ARM-based devices such as the Raspberry Pi.
The action can be configured to publish images to the following registries:
- DockerHub (docker.io)
- GitHub Container Registry (ghcr.io)
- Quay (quay.io)
- Harbor (goharbor.io)
- Amazon Elastic Container Registry (ECR)
- custom registry
The action can be configured to publish images to other registries by setting
_username
and _token
inputs for the respective registries:
- DockerHub:
dockerhub_username
anddockerhub_token
- GitHub Container Registry:
ghcr_username
andghcr_token
- Quay:
quay_username
andquay_token
- Harbor:
harbor_username
andharbor_token
- Amazon Elastic Container Registry (ECR):
ecr_access_key_id
andecr_secret_access_key
- Custom Registry:
custom_registry_username
andcustom_registry_token
The action adds various tags to the image, including the following:
- every build
edge
{short sha}
: (short SHA hash of the commit){long sha}
(long SHA hash of the commit)
- when the action is triggered by a push to a branch
latest
{major}.{minor}.{patch}
{major}.{minor}
{major}
To use the action, add the following to your GitHub Actions workflow:
- name: Publish Image to Various Registries
uses: wesdean/publish-image-to-various-registries@v1
with:
platforms: 'linux/amd64,linux/arm64,linux/arm/v6,linux/arm/v7'
dockerfile: 'Dockerfile'
context: '.'
github_ref: ${{ github.ref }}
repository_name: ${{ github.repository }}
# to push to DockerHub, use:
dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }}
dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
# to push to GitHub Container Registry (GHCR), use:
ghcr_username: ${{ secrets.GHCR_USERNAME }}
# to push to Quay, use:
quay_username: ${{ secrets.QUAY_USERNAME }}
quay_token: ${{ secrets.QUAY_TOKEN }}
# to push to Harbor, use:
harbor_username: ${{ secrets.HARBOR_USERNAME }}
harbor_token: ${{ secrets.HARBOR_TOKEN }}
# to push to Amazon Elastic Container Registry (ECR), use:
ecr_access_key_id: ${{ secrets.ECR_ACCESS_KEY_ID }}
ecr_secret_access_key: ${{ secrets.ECR_SECRET_ACCESS_KEY }}
ecr_region: 'us-east-1'
# to push to a custom registry, use:
custom_registry_username: ${{ secrets.CUSTOM_REGISTRY_USERNAME }}
custom_registry_token: ${{ secrets.CUSTOM_REGISTRY_TOKEN }}
custom_registry: 'registry.example.com'
All of the registry-specific inputs are optional. That said, if none of the registry inputs are set, the action will build the image and tag it, but it will not publish the image. It'll just eat your billing minutes. I recommend against that.
The following inputs are available:
- basic
- context: The build context for the Docker build. Default:
.
. - dockerfile: The path to the Dockerfile. Default:
Dockerfile
. - platforms: The platforms to build images for. Default:
linux/amd64,linux/arm64,linux/arm/v6,linux/arm/v7
. - repository_name: The name of the repository. Consider using
${{ github.repository }}
to get the repository name. REQUIRED - github_ref: The GitHub ref that triggered the action. Consider using
${{ github.ref }}
to get the ref that triggered the action. REQUIRED - force_release: If
true
, the action will treat any build as a release and tag the image with the appropriate tags. Default:false
.
- context: The build context for the Docker build. Default:
- DockerHub
- dockerhub_image: The name of the image on DockerHub. Default:
{ dockerhub username }/{ repository name }
. - dockerhub_username: The username for DockerHub. Required to use DockerHub.
- dockerhub_token: The token for DockerHub. Required to use DockerHub.
- dockerhub_registry: The URL for DockerHub. Default:
docker.io
.
- dockerhub_image: The name of the image on DockerHub. Default:
- GitHub Container Registry (GHCR)
- ghcr_image: The name of the image on GHCR. Default:
{ ghcr username }/{ repository name }
. - ghcr_username: The username for GHCR. Required to use GHCR.
- ghcr_token: The token for GHCR. Required to use GHCR.
- ghcr_registry: The URL for GHCR. Default:
ghcr.io
.
- ghcr_image: The name of the image on GHCR. Default:
- Quay
- quay_image: The name of the image on Quay. Default:
{ quay username }/{ repository name }
. - quay_username: The username for Quay. Required to use Quay.
- quay_token: The token for Quay. Required to use Quay.
- quay_registry: The URL for Quay. Default:
quay.io
.
- quay_image: The name of the image on Quay. Default:
- Harbor
- harbor_image: The name of the image on Harbor. Default:
{ harbor username }/{ repository name }
. - harbor_username: The username for Harbor. Required to use Harbor.
- harbor_token: The token for Harbor. Required to use Harbor.
- harbor_registry: The registry for Harbor. Default:
goharbor.io
.
- harbor_image: The name of the image on Harbor. Default:
- Amazon Elastic Container Registry (ECR):
- ecr_image: The name of the image on ECR. Default:
{ repository name }
. - ecr_access_key_id: The access key ID for ECR. Required to use ECR.
- ecr_secret_access_key: The secret access key for ECR. Required to use ECR.
- ecr_region: The region for ECR. Default:
us-east-1
. - ecr_registry: The URL to use with ECR. Default:
{ ecr account id }.dkr.ecr.{ region }.amazonaws.com
.
- ecr_image: The name of the image on ECR. Default:
- Custom Registry
- custom_registry_image: The name of the image on the custom registry.
Default:
{ repository name }
. - custom_registry_username: The username for the custom registry. Required to use a custom registry.
- custom_registry_token: The token for the custom registry. Required to use a custom registry.
- custom_registry: The URL to the custom registry.
- custom_registry_image: The name of the image on the custom registry.
Default:
The following outputs are available:
- DockerHub
- dockerhub:
true
if the image was published to DockerHub,false
otherwise. - dockerhub_image: The name of the image on DockerHub.
- dockerhub:
- GitHub Container Registry (GHCR)
- ghcr:
true
if the image was published to GHCR,false
otherwise. - ghcr_image: The name of the image on GHCR.
- ghcr:
- Quay
- quay:
true
if the image was published to Quay,false
otherwise. - quay_image: The name of the image on Quay.
- quay:
- Harbor
- harbor:
true
if the image was published to Harbor,false
otherwise. - harbor_image: The name of the image on Harbor.
- harbor:
- Amazon Elastic Container Registry (ECR)
- ecr:
true
if the image was published to ECR,false
otherwise. - ecr_image: The name of the image on ECR.
- ecr_image_url: The URL of the image on ECR.
- ecr:
- Custom Registry
- custom_registry:
true
if the image was published to a custom registry,false
otherwise. - custom_image: The name of the image on the custom registry.
- custom_registry:
This is a minimal example that builds an image and publishes it to DockerHub.
The action is triggered by a push to the main
branch or a tag that starts with
v
and a version number with a major release level greater than 0 (i.e., it
would run for v1.0.0
, but not for v0.1.0
). The action can also be triggered
manually.
---
name: Build an Awesome Image
# yamllint disable-line rule:truthy
on:
push:
branches:
- "main"
tags:
- "v[1-9][0-9]*.*"
workflow_dispatch:
permissions: read-all
jobs:
publish_image:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/[email protected]
with:
depth: 0
tags: true
- name: Build and Publish Image
uses: wesley-dean/publish_container@v1
with:
# pass the basic data for the build
github_ref: ${{ github.ref }}
repository_name: ${{ github.repository }}
# just push to DockerHub and nowhere else
dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }}
dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
This example pushes to both DockerHub and GHCR. The image is given different names on each registry.
---
name: Build an Awesome Image
# yamllint disable-line rule:truthy
on:
push:
branches:
- "main"
tags:
- "v[1-9][0-9]*.*"
workflow_dispatch:
permissions: read-all
jobs:
publish_image:
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/[email protected]
with:
depth: 0
tags: true
- name: Build and Publish Image
uses: wesley-dean/publish_container@v1
with:
# basic inputs
github_ref: ${{ github.ref }}
repository_name: ${{ github.repository }}
# DockerHub inputs
dockerhub_username: ${{ secrets.DOCKERHUB_USERNAME }}
dockerhub_token: ${{ secrets.DOCKERHUB_TOKEN }}
dockerhub_image: "myname/awesomeimage"
# GHCR inputs
ghcr_username: ${{ secrets.GHCR_USERNAME }}
ghcr_token: ${{ secrets.GHCR_TOKEN }}
ghcr_image: "my-name/awesome-image"
Because the action is a composite action, it doesn't have access to the
github.repository
context. The action needs the repository name to build the
image name for the various registries.
The action needs the ref to determine the version of the image. The ref is
used to determine the version of the image. The ref is also used to determine
the tags for the image. Like with the repository name, the action doesn't have
access to the github.ref
data.
The context variable is the path to the build context for the Docker build.
The default is the root of the repository (.
).
The action needs to know the path to the Dockerfile to build the image. The
default is Dockerfile
in the root of the build context (so, ./Dockerfile
).
Yes, the action can build images for other platforms. The platforms
input
is a comma-separated list of platforms. The default is
linux/amd64,linux/arm64,linux/arm/v6,linux/arm/v7
.
Yes, images can be pushed to multiple registries. The action can be configured to push images to DockerHub, GHCR, Quay, Harbor, ECR, and custom registries.
Yes, images on different registries can have different names. The action
supports setting the image name for each registry. For example, the image
name on DockerHub can be myname/awesomeimage
, while the image name on GHCR
can be my-name/awesome-image
. This can be done by setting the
dockerhub_image
and ghcr_image
inputs, respectively.
The action uses the github_ref
to determine the version of the image. If the
ref starts with refs/tags/v[1-9]
(e.g., v1.0.0
), the action considers this
run to be a "release" and sets the version to the tag.
Good eye. The action won't consider a tag that starts with v0
to be a
release. It will still build and push the image, but it'll only tag the image
with the edge
tag plus the short and long hashes.
If, however, the force_release
input is set to true
, the action will treat
any build -- v0.x.x or otherwise -- as a release and tag the image with the
appropriate tags.
Nothing. Nothing is stored. The action uses the credentials to authenticate to DockerHub, GHCR, etc. and when updating image descriptions on DockerHub, Quay, and Harbor. The credentials aren't used for anything else. When they're passed to the action as secrets, they're stored in the GitHub Actions environment and are not persisted, even in the logs. It is recommended that credentials be stored in GitHub Secrets, not as plain text or repository environment variables.
Want to be sure? Check the code. It's all there. Here's a link:
https://github.com/wesley-dean/publish_container/blob/main/action.yml
Images associated with releases are given the following tags:
- latest
- edge
{major}.{minor}.{patch}
{major}.{minor}
{major}
{short sha}
{long sha}
So, if the tag is v1.2.3
, the image will be tagged with the following:
- latest
- edge
- v1.2.3
- v1.2
- v1
- abc123
- abc123def456aaa7890123456789abcdef012345
(the SHA hashes are made up)
Images that are not associated with a release are given the following tags:
- edge
{short sha}
{long sha}
For images on DockerHub, Quay, and Harbor, the action updates the description of the image using the README.md file in the repository.
Yeah, no, this action just handles the most basic stuff. If you need to customize the build, you'll need to use the underlying actions directly. If you want to provide a PR to include those variables, I'm open to it.
This project is licensed under the Creative Commons License 1.0 Universal License - see the LICENSE file for details.
Contributions are welcome. Please read the CONTRIBUTING.md file for details.
Please read the CODE_OF_CONDUCT.md file for details on the code of conduct. Long story short, be nice to each other and treat each other with respect, compassion, and empathy, especially when you disagree.
- Wes Dean