Skip to content

Instantly share code, notes, and snippets.

@djm
Created January 9, 2021 18:28
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save djm/167dd9db0dbdf9316254306c90dbc1aa to your computer and use it in GitHub Desktop.
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
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"
@fliptheweb
Copy link

Hi @djm!
We faced with the same issue😔
Did you find any solution/workaroud?
Thanks for your attention!

@djm
Copy link
Author

djm commented Apr 26, 2022

@fliptheweb

Nope..

We don't change the image much, so we can get away with this - but it is susceptible to a race condition.

CleanShot 2022-04-26 at 14 55 17@2x

@fliptheweb
Copy link

fliptheweb commented Apr 26, 2022

@djm thanks for sharing!
Also found that hacky worakround by @girtsf, but haven't tried yet

@djm
Copy link
Author

djm commented Apr 26, 2022

@fliptheweb You're welcome. Good luck! Let me know if you find anything better 😁

@djm
Copy link
Author

djm commented Apr 26, 2022

Oh that is a different solution! We may need to try that.

@mhemken-vts
Copy link

For all the folks reading this in the future, the hacky workaround from @fliptheweb doesn't work unfortunately.

@roniemartinez
Copy link

For anyone looking for a solution, you can use workflow_call using outputs and inputs.

  1. 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/login-action@v2.1.0
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract Docker metadata
        id: meta
        uses: docker/metadata-action@v4.1.1
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

      - name: Build and push Docker image
        uses: docker/build-push-action@v3.2.0
        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 }}
  1. 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