Created
January 9, 2021 18:28
-
-
Save djm/167dd9db0dbdf9316254306c90dbc1aa to your computer and use it in GitHub Desktop.
Problem: pulling docker images with dynamically generated tags, using just the GitHub Action yaml syntax
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Build Docker image & run dependent jobs that pull tagged image | |
# Desires: | |
# | |
# 1) First job builds, pushes and tags docker image with ref/pr-number/sha - something specific to that branch. | |
# 2) n-jobs after this depend on success of first job, and will pull an image based on tag used in first job. | |
# 3) The n-jobs mount host GA workspace, or checkout code within the container itself. Not bothered which. | |
# 4) Rely on GitHub Action's yaml docker constructs only, rather than calling docker @ the cli | |
# This is not a _true_ requirement! I know how to get around it, but it is what I originally | |
# set out hoping to do - because it works very nicely when you're dealing with static tags | |
# (e.g 'latest'). Though, if we were to use only static tags with this per-branch flow you would | |
# create a race condition when multiple open PRs result in image rebuilds (`latest` tag may not | |
# be the one from your branch). | |
on: | |
push: | |
branches: | |
- master | |
pull_request: | |
types: [opened, synchronize, reopened] | |
jobs: | |
# This job runs successfully (at least when dependent_job_b is not defined) | |
docker-build-push: | |
name: Build Docker image & push | |
runs-on: ubuntu-latest | |
steps: | |
- name: Set up Docker Buildx | |
uses: docker/setup-buildx-action@v1 | |
# Docker Meta handles manipulating various | |
# GitHub Action contexts into sane Docker | |
# image tags, based on whether this is a | |
# push to a mainline branch or a pull request. | |
# | |
# dev/master -> :dev/:master & sha | |
# pr -> :pr2 & sha | |
# | |
- name: Instantiate Docker Meta | |
id: docker_meta | |
uses: crazy-max/ghaction-docker-meta@v1 | |
with: | |
images: example-org/example-repo | |
tag-sha: true | |
# Sets up buildx to work with GitHub Actions | |
# Cache. This caches buildx's layers, so that | |
# rebuilding reuses layers that have not changed. | |
- name: Cache Docker layers | |
uses: actions/cache@v2 | |
with: | |
path: /tmp/.buildx-cache | |
key: ${{ runner.os }}-buildx-${{ github.sha }} | |
# This restore key means "fall back to the latest | |
# key that is prefixed like this"; this means that | |
# a cache miss on the exact key will fallback to the | |
# most recently created buildx cache key. This is | |
# safe because it's a layer cache, and buildx will | |
# only pull a cached layer if it knows it needs it | |
# (they are content addressed). | |
restore-keys: | | |
${{ runner.os }}-buildx- | |
- name: Login to DockerHub | |
uses: docker/login-action@v1 | |
with: | |
username: ${{ secrets.DOCKER_HUB_USERNAME }} | |
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} | |
- name: Build and push | |
uses: docker/build-push-action@v2 | |
with: | |
# Pull forces Docker to check the network for the | |
# latest version of base images. With this enabled | |
# we can trust that we're always using the latest | |
# base image, as it will fail when it cannot verify | |
# that rather than building with the local latest. | |
pull: true | |
# Ensures the image is pushed to the DH repo on build. | |
push: true | |
# Use Docker Meta output to set correct tags & labels. | |
tags: ${{ steps.docker_meta.outputs.tags }} | |
labels: ${{ steps.docker_meta.outputs.labels }} | |
# Ensure we read & write to the GH action cache. | |
cache-from: type=local,src=/tmp/.buildx-cache | |
cache-to: type=local,dest=/tmp/.buildx-cache | |
# This dependent job uses job.container.image. The workflow runs, but fails | |
# immediately on this job with: "invalid reference format" on the "Initialize containers" step. | |
dependent_job_example_a: | |
name: Check thing a | |
needs: docker-build-push | |
runs-on: ubuntu-latest | |
container: | |
image: example-org/example-repo:${{ github.sha }} # <-- the cause. | |
steps: | |
- name: Check out the repository inside the container | |
uses: actions/checkout@v2 | |
- name: Run thing a inside the container | |
run: echo "thing a" | |
# This dependent job uses job.steps[x].uses, and fails the entire | |
# workflow immediately with: | |
# | |
# "The workflow is not valid. .github/workflows/workflow.yml (Line: x, Col: x): Unrecognized named-value: 'github'." | |
# | |
dependent_job_example_b: | |
name: Check thing b | |
needs: docker-build-push | |
runs-on: ubuntu-latest | |
steps: | |
- name: Check out the repository on the guest | |
uses: actions/checkout@v2 | |
- name: Run thing b inside the container, having mounted workspace. | |
uses: docker://example-org/example-repo:${{ github.sha }} # <-- cause | |
with: | |
entrypoint: /bin/echo | |
args: "Hello" |
Nope..
We don't change the image much, so we can get away with this - but it is susceptible to a race condition.
@djm thanks for sharing!
Also found that hacky worakround by @girtsf, but haven't tried yet
@fliptheweb You're welcome. Good luck! Let me know if you find anything better 😁
Oh that is a different solution! We may need to try that.
For all the folks reading this in the future, the hacky workaround from @fliptheweb doesn't work unfortunately.
For anyone looking for a solution, you can use workflow_call
using outputs
and inputs
.
- Use
output
when calling the next job
# file1.yml
name: Build and Publish Docker Image
on:
pull_request:
branches:
- main
env:
REGISTRY: ghcr.io
# github.repository as <account>/<repo>
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Log into registry ${{ env.REGISTRY }}
uses: docker/[email protected]
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Docker metadata
id: meta
uses: docker/[email protected]
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Build and push Docker image
uses: docker/[email protected]
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
outputs:
image-tag: ${{ steps.meta.outputs.version }}
run-job2:
needs:
- build-and-push-image
uses: ./.github/workflows/file2.yml
secrets: inherit
with:
image-tag: ${{ needs.build-and-push-image.outputs.image-tag }}
- Use
worflow_call
inputs
in container image
# file2.yml
on:
workflow_call:
inputs:
image-tag:
type: string
default: 'main'
jobs:
job2
runs-on: ubuntu-latest
container:
image: ghcr.io/${{ github.repository }}:${{ inputs.image-tag }}
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi @djm!
We faced with the same issue😔
Did you find any solution/workaroud?
Thanks for your attention!