Skip to content

Deploy

Deploy #109

Workflow file for this run

name: Deploy
on:
push:
branches: [main]
schedule:
- cron: "0 00,12 * * *" # Twice a day
workflow_dispatch:
inputs:
force:
description: Force rebuild and republish all image tags
default: true
type: boolean
image-name:
description: Override image name for this manual run
default: ""
type: string
env:
IMAGE_NAME: ${{ inputs.image-name || vars.IMAGE_NAME || 'nikolaik/python-nodejs' }}
jobs:
generate-matrix:
name: Generate build matrix
runs-on: ubuntu-latest
needs: [test]
outputs:
version_matrix: ${{ steps.set-matrix.outputs.matrix }}
arch_matrix: ${{ steps.set-matrix.outputs.arch_matrix }}
latest_key: ${{ steps.set-matrix.outputs.latest_key }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 2
- uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
with:
enable-cache: true
- name: Generate build matrix
id: set-matrix
run: |
FORCE=
if [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ inputs.force }}" == "true" ]]; then
FORCE="--force"
elif git log --pretty=format:"%s" HEAD^..HEAD | grep -q '\[force\]'; then
FORCE="--force"
fi
uv run dpn $FORCE build-matrix --event ${{ github.event_name }}
build-arch:
name: ${{ matrix.key }} (${{ matrix.arch }})
runs-on: ${{ matrix.runner }}
if: needs.generate-matrix.outputs.arch_matrix != ''
needs: [generate-matrix]
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.generate-matrix.outputs.arch_matrix) }}
steps:
# Setup
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
with:
enable-cache: true
- name: Generate Dockerfile from config
run: |
context="$(echo '${{ toJSON(matrix) }}' | jq -c '{key, python, python_canonical, python_image, nodejs, nodejs_canonical, distro, platforms, digest}')"
uv run dpn dockerfile --context "${context}"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
# Build
- name: Build image
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7
with:
context: .
file: dockerfiles/${{ matrix.key }}.Dockerfile
platforms: ${{ matrix.platform }}
load: true
tags: ${{ env.IMAGE_NAME }}:${{ matrix.key }}-${{ matrix.arch }}
# Test
- name: Run smoke tests
run: |
docker run --rm ${{ env.IMAGE_NAME }}:${{ matrix.key }}-${{ matrix.arch }} sh -c \
"node --version && npm --version && yarn --version && \
python --version && pip --version && pipenv --version && \
poetry --version && uv --version"
# Push
- name: Login to Docker Hub
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Push image
run: docker push "${IMAGE_NAME}:${{ matrix.key }}-${{ matrix.arch }}"
deploy:
name: Publish ${{ matrix.key }}
runs-on: ubuntu-latest
if: needs.generate-matrix.outputs.version_matrix != ''
needs: [generate-matrix, build-arch]
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.generate-matrix.outputs.version_matrix) }}
steps:
- name: Create local multi-arch manifest
run: |
refs=("${IMAGE_NAME}:${{ matrix.key }}-amd64")
if echo '${{ toJSON(matrix.platforms) }}' | jq -e '.[] == "linux/arm64"' > /dev/null; then
refs+=("${IMAGE_NAME}:${{ matrix.key }}-arm64")
fi
docker manifest create "${IMAGE_NAME}:${{ matrix.key }}" "${refs[@]}"
if [[ "${{ needs.generate-matrix.outputs.latest_key }}" == "${{ matrix.key }}" ]]; then
docker manifest create "${IMAGE_NAME}:latest" "${refs[@]}"
fi
- name: Login to Docker Hub
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Install regctl
uses: regclient/actions/regctl-installer@1b705e32d40851370799ea5814e83d0a5f6a70dc # v0.1.0
- name: Push multi-arch manifest
id: push-manifest
run: |
docker manifest push "${IMAGE_NAME}:${{ matrix.key }}"
digest="$(regctl image digest "${IMAGE_NAME}:${{ matrix.key }}")"
echo "digest=${digest}" >> "$GITHUB_OUTPUT"
- name: Push latest manifest
if: needs.generate-matrix.outputs.latest_key == matrix.key
run: docker manifest push "${IMAGE_NAME}:latest"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
- name: Add digest to build context
run: |
mkdir builds/
digest="${{ steps.push-manifest.outputs.digest }}"
echo '${{ toJSON(matrix) }}' |
jq --arg digest "$digest" \
'. +={"digest": $digest}' \
>> "builds/${{ matrix.key }}.json"
- name: Upload build context
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: build-${{ matrix.key }}
path: builds/*
if-no-files-found: error
retention-days: 1
release:
name: Update versions.json and README.md
runs-on: ubuntu-latest
needs: [deploy]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
with:
enable-cache: true
- name: Download metadata for builds
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
path: builds
pattern: build-*
merge-multiple: true
- name: Update versions.json and README.md, then commit and push changes (if any)
run: |
uv run dpn --verbose release --builds-dir builds/
clean_checkout=$(git status --porcelain)
if [[ -n "${clean_checkout}" ]]; then
git config --global user.name "Nikolai Kristiansen" > /dev/null 2>&1
git config --global user.email nikolaik@users.noreply.github.com > /dev/null 2>&1
# Update README.md
today=$(date +%Y-%m-%d)
sed -i -E "s/Last updated by bot: .*/Last updated by bot: ${today}/" README.md
git add versions.json README.md
git commit -m '🗃 Updated python/node versions [skip ci]'
git push --quiet origin main
else
echo "Nothing changed, nothing to archive."
fi
test:
uses: ./.github/workflows/tests.yaml