Skip to content

Keep DockerHub optional and report only successfully published tags #106

Keep DockerHub optional and report only successfully published tags

Keep DockerHub optional and report only successfully published tags #106

Workflow file for this run

name: Build and Publish
on:
schedule:
# At 04:00 on day-of-month 1.
- cron: "0 4 1 * *"
workflow_dispatch:
push:
branches:
- "main"
permissions:
contents: read
env:
DOCKERHUB_IMAGE: betterweb/node
FORGEJO_IMAGE: code.bettercorp.dev/bettercorp/docker-node
jobs:
prepare-matrix:
runs-on: blacksmith-2vcpu-ubuntu-2404
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
latest: ${{ steps.set-matrix.outputs.latest }}
devcontainer_matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- uses: actions/checkout@v6
- name: Select published supported Node images
id: set-matrix
run: scripts/prepare-matrix.sh >> "$GITHUB_OUTPUT"
build:
needs: prepare-matrix
runs-on: blacksmith-2vcpu-ubuntu-2404
strategy:
fail-fast: false
matrix: ${{fromJson(needs.prepare-matrix.outputs.matrix)}}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
with:
platforms: all
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v4
- name: Build and test runtime permissions
run: |
NODE_VERSION=${{ matrix.tag.version }}
docker build --build-arg NODE_VERSION="${NODE_VERSION#v}" -t betterweb/node:permission-test .
tests/permissions.sh betterweb/node:permission-test
- name: Login to Forgejo
uses: docker/login-action@v4
with:
registry: code.bettercorp.dev
username: ${{ secrets.BC_CODE_USER }}
password: ${{ secrets.BC_CODE_PACKAGE_TOKEN }}
logout: true
- name: Parse version
id: parse_version
run: |
VERSION=${{ matrix.tag.version }}
PARSED_VERSION=${VERSION#v}
echo "PARSED_VERSION=$PARSED_VERSION" >> $GITHUB_OUTPUT
shell: bash
- name: Is Latest
id: is_latest
run: |
if [ "${{ matrix.tag.version }}" = "${{ needs.prepare-matrix.outputs.latest }}" ]; then
echo "IS_LATEST=true" >> $GITHUB_OUTPUT
echo "This is the latest version"
else
echo "IS_LATEST=false" >> $GITHUB_OUTPUT
echo "This is not the latest version"
fi
- name: Build and push to Forgejo
id: build_image
uses: docker/build-push-action@v7
with:
build-args: |
NODE_MAJOR=${{ matrix.tag.major }}
NODE_VERSION=${{ steps.parse_version.outputs.PARSED_VERSION }}
context: .
platforms: ${{ matrix.tag.platforms }}
push: true
tags: |
${{ env.FORGEJO_IMAGE }}:${{steps.parse_version.outputs.PARSED_VERSION}}
${{ env.FORGEJO_IMAGE }}:${{matrix.tag.major}}
${{ steps.is_latest.outputs.IS_LATEST == 'true' && format('{0}:latest', env.FORGEJO_IMAGE) || '' }}
- name: Login to DockerHub
id: dockerhub_login
continue-on-error: true
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
logout: true
- name: Mirror the built image to DockerHub
id: mirror
if: steps.dockerhub_login.outcome == 'success'
continue-on-error: true
run: |
tags=(--tag "${{ env.DOCKERHUB_IMAGE }}:${{ steps.parse_version.outputs.PARSED_VERSION }}"
--tag "${{ env.DOCKERHUB_IMAGE }}:${{ matrix.tag.major }}")
if [ "${{ steps.is_latest.outputs.IS_LATEST }}" = true ]; then
tags+=(--tag "${{ env.DOCKERHUB_IMAGE }}:latest")
fi
docker buildx imagetools create "${tags[@]}" "${{ env.FORGEJO_IMAGE }}@${{ steps.build_image.outputs.digest }}"
- name: Write manifest part
run: |
TAGS=$(jq -nc \
--arg v "${{ steps.parse_version.outputs.PARSED_VERSION }}" \
--arg m "${{ matrix.tag.major }}" \
--arg latest "${{ steps.is_latest.outputs.IS_LATEST }}" \
--arg dockerhub "${{ env.DOCKERHUB_IMAGE }}" \
--argjson mirrored "${{ steps.mirror.outcome == 'success' }}" \
--arg forgejo "${{ env.FORGEJO_IMAGE }}" \
'[($dockerhub + ":" + $v), ($dockerhub + ":" + $m), ($forgejo + ":" + $v), ($forgejo + ":" + $m)] + (if $latest == "true" then [($dockerhub + ":latest"), ($forgejo + ":latest")] else [] end) | if $mirrored then . else map(select(startswith($dockerhub + ":") | not)) end')
PLATFORMS=$(echo "${{ matrix.tag.platforms }}" | jq -Rc 'split(",")')
jq -n \
--arg version "${{ steps.parse_version.outputs.PARSED_VERSION }}" \
--argjson major ${{ matrix.tag.major }} \
--arg variant "base" \
--argjson platforms "$PLATFORMS" \
--argjson tags "$TAGS" \
--arg digest "${{ steps.build_image.outputs.digest }}" \
--argjson is_latest ${{ steps.is_latest.outputs.IS_LATEST }} \
'{version:$version, major:$major, variant:$variant, platforms:$platforms, tags:$tags, digest:$digest, is_latest:$is_latest}' \
> part.json
cat part.json
- uses: actions/upload-artifact@v4
with:
name: manifest-part-base-${{ matrix.tag.major }}
path: part.json
build-dev:
needs: [prepare-matrix, build]
runs-on: blacksmith-2vcpu-ubuntu-2404
strategy:
fail-fast: false
matrix: ${{fromJson(needs.prepare-matrix.outputs.devcontainer_matrix)}}
steps:
- uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Login to Forgejo
uses: docker/login-action@v4
with:
registry: code.bettercorp.dev
username: ${{ secrets.BC_CODE_USER }}
password: ${{ secrets.BC_CODE_PACKAGE_TOKEN }}
logout: true
- name: Parse version
id: parse_version
run: |
VERSION=${{ matrix.tag.version }}
PARSED_VERSION=${VERSION#v}
echo "PARSED_VERSION=$PARSED_VERSION" >> $GITHUB_OUTPUT
shell: bash
- name: Is Latest
id: is_latest
run: |
if [ "${{ matrix.tag.version }}" = "${{ needs.prepare-matrix.outputs.latest }}" ]; then
echo "IS_LATEST=true" >> $GITHUB_OUTPUT
echo "This is the latest version"
else
echo "IS_LATEST=false" >> $GITHUB_OUTPUT
echo "This is not the latest version"
fi
- name: Build and push dev image to Forgejo
id: build_image
uses: docker/build-push-action@v7
with:
context: .
file: Dockerfile.dev
push: true
build-args: |
BASE_IMAGE=${{ env.FORGEJO_IMAGE }}
NODE_MAJOR=${{ matrix.tag.major }}
NODE_VERSION=${{ steps.parse_version.outputs.PARSED_VERSION }}
tags: |
${{ env.FORGEJO_IMAGE }}:dev-${{steps.parse_version.outputs.PARSED_VERSION}}
${{ env.FORGEJO_IMAGE }}:dev-${{matrix.tag.major}}
${{ steps.is_latest.outputs.IS_LATEST == 'true' && format('{0}:dev', env.FORGEJO_IMAGE) || '' }}
- name: Login to DockerHub
id: dockerhub_login
continue-on-error: true
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
logout: true
- name: Mirror the built image to DockerHub
id: mirror
if: steps.dockerhub_login.outcome == 'success'
continue-on-error: true
run: |
tags=(--tag "${{ env.DOCKERHUB_IMAGE }}:dev-${{ steps.parse_version.outputs.PARSED_VERSION }}"
--tag "${{ env.DOCKERHUB_IMAGE }}:dev-${{ matrix.tag.major }}")
if [ "${{ steps.is_latest.outputs.IS_LATEST }}" = true ]; then
tags+=(--tag "${{ env.DOCKERHUB_IMAGE }}:dev")
fi
docker buildx imagetools create "${tags[@]}" "${{ env.FORGEJO_IMAGE }}@${{ steps.build_image.outputs.digest }}"
- name: Write manifest part
run: |
TAGS=$(jq -nc \
--arg v "${{ steps.parse_version.outputs.PARSED_VERSION }}" \
--arg m "${{ matrix.tag.major }}" \
--arg latest "${{ steps.is_latest.outputs.IS_LATEST }}" \
--arg dockerhub "${{ env.DOCKERHUB_IMAGE }}" \
--argjson mirrored "${{ steps.mirror.outcome == 'success' }}" \
--arg forgejo "${{ env.FORGEJO_IMAGE }}" \
'[($dockerhub + ":dev-" + $v), ($dockerhub + ":dev-" + $m), ($forgejo + ":dev-" + $v), ($forgejo + ":dev-" + $m)] + (if $latest == "true" then [($dockerhub + ":dev"), ($forgejo + ":dev")] else [] end) | if $mirrored then . else map(select(startswith($dockerhub + ":") | not)) end')
jq -n \
--arg version "${{ steps.parse_version.outputs.PARSED_VERSION }}" \
--argjson major ${{ matrix.tag.major }} \
--arg variant "dev" \
--argjson platforms '["linux/amd64"]' \
--argjson tags "$TAGS" \
--arg digest "${{ steps.build_image.outputs.digest }}" \
--argjson is_latest ${{ steps.is_latest.outputs.IS_LATEST }} \
'{version:$version, major:$major, variant:$variant, platforms:$platforms, tags:$tags, digest:$digest, is_latest:$is_latest}' \
> part.json
cat part.json
- uses: actions/upload-artifact@v4
with:
name: manifest-part-dev-${{ matrix.tag.major }}
path: part.json
build-devcontainers:
needs: prepare-matrix
runs-on: blacksmith-2vcpu-ubuntu-2404
strategy:
fail-fast: false
matrix: ${{fromJson(needs.prepare-matrix.outputs.devcontainer_matrix)}}
steps:
- uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Login to Forgejo
uses: docker/login-action@v4
with:
registry: code.bettercorp.dev
username: ${{ secrets.BC_CODE_USER }}
password: ${{ secrets.BC_CODE_PACKAGE_TOKEN }}
logout: true
- name: Parse version
id: parse_version
run: |
VERSION=${{ matrix.tag.version }}
PARSED_VERSION=${VERSION#v}
echo "PARSED_VERSION=$PARSED_VERSION" >> $GITHUB_OUTPUT
shell: bash
- name: Is Latest
id: is_latest
run: |
if [ "${{ matrix.tag.version }}" = "${{ needs.prepare-matrix.outputs.latest }}" ]; then
echo "IS_LATEST=true" >> $GITHUB_OUTPUT
echo "This is the latest version"
else
echo "IS_LATEST=false" >> $GITHUB_OUTPUT
echo "This is not the latest version"
fi
- name: Build and push devcontainer image to Forgejo
id: build_image
uses: docker/build-push-action@v7
with:
context: .
file: Dockerfile.devcontainer
push: true
build-args: |
NODE_MAJOR=${{ matrix.tag.major }}
NODE_VERSION=${{ steps.parse_version.outputs.PARSED_VERSION }}
tags: |
${{ env.FORGEJO_IMAGE }}:devcontainer-${{steps.parse_version.outputs.PARSED_VERSION}}
${{ env.FORGEJO_IMAGE }}:devcontainer-${{matrix.tag.major}}
${{ steps.is_latest.outputs.IS_LATEST == 'true' && format('{0}:devcontainer-latest', env.FORGEJO_IMAGE) || '' }}
- name: Login to DockerHub
id: dockerhub_login
continue-on-error: true
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
logout: true
- name: Mirror the built image to DockerHub
id: mirror
if: steps.dockerhub_login.outcome == 'success'
continue-on-error: true
run: |
tags=(--tag "${{ env.DOCKERHUB_IMAGE }}:devcontainer-${{ steps.parse_version.outputs.PARSED_VERSION }}"
--tag "${{ env.DOCKERHUB_IMAGE }}:devcontainer-${{ matrix.tag.major }}")
if [ "${{ steps.is_latest.outputs.IS_LATEST }}" = true ]; then
tags+=(--tag "${{ env.DOCKERHUB_IMAGE }}:devcontainer-latest")
fi
docker buildx imagetools create "${tags[@]}" "${{ env.FORGEJO_IMAGE }}@${{ steps.build_image.outputs.digest }}"
- name: Write manifest part
run: |
TAGS=$(jq -nc \
--arg v "${{ steps.parse_version.outputs.PARSED_VERSION }}" \
--arg m "${{ matrix.tag.major }}" \
--arg latest "${{ steps.is_latest.outputs.IS_LATEST }}" \
--arg dockerhub "${{ env.DOCKERHUB_IMAGE }}" \
--argjson mirrored "${{ steps.mirror.outcome == 'success' }}" \
--arg forgejo "${{ env.FORGEJO_IMAGE }}" \
'[($dockerhub + ":devcontainer-" + $v), ($dockerhub + ":devcontainer-" + $m), ($forgejo + ":devcontainer-" + $v), ($forgejo + ":devcontainer-" + $m)] + (if $latest == "true" then [($dockerhub + ":devcontainer-latest"), ($forgejo + ":devcontainer-latest")] else [] end) | if $mirrored then . else map(select(startswith($dockerhub + ":") | not)) end')
jq -n \
--arg version "${{ steps.parse_version.outputs.PARSED_VERSION }}" \
--argjson major ${{ matrix.tag.major }} \
--arg variant "devcontainer" \
--argjson platforms '["linux/amd64"]' \
--argjson tags "$TAGS" \
--arg digest "${{ steps.build_image.outputs.digest }}" \
--argjson is_latest ${{ steps.is_latest.outputs.IS_LATEST }} \
'{version:$version, major:$major, variant:$variant, platforms:$platforms, tags:$tags, digest:$digest, is_latest:$is_latest}' \
> part.json
cat part.json
- uses: actions/upload-artifact@v4
with:
name: manifest-part-devcontainer-${{ matrix.tag.major }}
path: part.json
build-dind:
needs: [prepare-matrix, build]
runs-on: blacksmith-2vcpu-ubuntu-2404
strategy:
fail-fast: false
matrix: ${{fromJson(needs.prepare-matrix.outputs.devcontainer_matrix)}}
steps:
- uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Login to Forgejo
uses: docker/login-action@v4
with:
registry: code.bettercorp.dev
username: ${{ secrets.BC_CODE_USER }}
password: ${{ secrets.BC_CODE_PACKAGE_TOKEN }}
logout: true
- name: Parse version
id: parse_version
run: |
VERSION=${{ matrix.tag.version }}
PARSED_VERSION=${VERSION#v}
echo "PARSED_VERSION=$PARSED_VERSION" >> $GITHUB_OUTPUT
shell: bash
- name: Is Latest
id: is_latest
run: |
if [ "${{ matrix.tag.version }}" = "${{ needs.prepare-matrix.outputs.latest }}" ]; then
echo "IS_LATEST=true" >> $GITHUB_OUTPUT
echo "This is the latest version"
else
echo "IS_LATEST=false" >> $GITHUB_OUTPUT
echo "This is not the latest version"
fi
- name: Build and push dind image to Forgejo
id: build_image
uses: docker/build-push-action@v7
with:
context: .
file: Dockerfile.dind
push: true
build-args: |
BASE_IMAGE=${{ env.FORGEJO_IMAGE }}
NODE_MAJOR=${{ matrix.tag.major }}
NODE_VERSION=${{ steps.parse_version.outputs.PARSED_VERSION }}
tags: |
${{ env.FORGEJO_IMAGE }}:dind-${{steps.parse_version.outputs.PARSED_VERSION}}
${{ env.FORGEJO_IMAGE }}:dind-${{matrix.tag.major}}
${{ steps.is_latest.outputs.IS_LATEST == 'true' && format('{0}:dind-latest', env.FORGEJO_IMAGE) || '' }}
- name: Login to DockerHub
id: dockerhub_login
continue-on-error: true
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
logout: true
- name: Mirror the built image to DockerHub
id: mirror
if: steps.dockerhub_login.outcome == 'success'
continue-on-error: true
run: |
tags=(--tag "${{ env.DOCKERHUB_IMAGE }}:dind-${{ steps.parse_version.outputs.PARSED_VERSION }}"
--tag "${{ env.DOCKERHUB_IMAGE }}:dind-${{ matrix.tag.major }}")
if [ "${{ steps.is_latest.outputs.IS_LATEST }}" = true ]; then
tags+=(--tag "${{ env.DOCKERHUB_IMAGE }}:dind-latest")
fi
docker buildx imagetools create "${tags[@]}" "${{ env.FORGEJO_IMAGE }}@${{ steps.build_image.outputs.digest }}"
- name: Write manifest part
run: |
TAGS=$(jq -nc \
--arg v "${{ steps.parse_version.outputs.PARSED_VERSION }}" \
--arg m "${{ matrix.tag.major }}" \
--arg latest "${{ steps.is_latest.outputs.IS_LATEST }}" \
--arg dockerhub "${{ env.DOCKERHUB_IMAGE }}" \
--argjson mirrored "${{ steps.mirror.outcome == 'success' }}" \
--arg forgejo "${{ env.FORGEJO_IMAGE }}" \
'[($dockerhub + ":dind-" + $v), ($dockerhub + ":dind-" + $m), ($forgejo + ":dind-" + $v), ($forgejo + ":dind-" + $m)] + (if $latest == "true" then [($dockerhub + ":dind-latest"), ($forgejo + ":dind-latest")] else [] end) | if $mirrored then . else map(select(startswith($dockerhub + ":") | not)) end')
jq -n \
--arg version "${{ steps.parse_version.outputs.PARSED_VERSION }}" \
--argjson major ${{ matrix.tag.major }} \
--arg variant "dind" \
--argjson platforms '["linux/amd64"]' \
--argjson tags "$TAGS" \
--arg digest "${{ steps.build_image.outputs.digest }}" \
--argjson is_latest ${{ steps.is_latest.outputs.IS_LATEST }} \
'{version:$version, major:$major, variant:$variant, platforms:$platforms, tags:$tags, digest:$digest, is_latest:$is_latest}' \
> part.json
cat part.json
- uses: actions/upload-artifact@v4
with:
name: manifest-part-dind-${{ matrix.tag.major }}
path: part.json
publish-manifest:
needs: [prepare-matrix, build, build-dev, build-devcontainers, build-dind]
runs-on: blacksmith-2vcpu-ubuntu-2404
steps:
- uses: actions/download-artifact@v4
with:
pattern: manifest-part-*
merge-multiple: false
path: parts/
- name: Merge manifest parts
run: |
TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
LATEST_VERSION="${{ needs.prepare-matrix.outputs.latest }}"
LATEST_VERSION_STRIPPED="${LATEST_VERSION#v}"
jq -s \
--arg ts "$TIMESTAMP" \
--arg latest "$LATEST_VERSION_STRIPPED" \
--arg run_id "${{ github.run_id }}" \
--arg run_number "${{ github.run_number }}" \
--arg sha "${{ github.sha }}" \
'{
build: {
run_id: ($run_id | tonumber),
run_number: ($run_number | tonumber),
sha: $sha,
timestamp: $ts
},
latest: $latest,
builds: (
group_by(.version)
| map({
version: .[0].version,
major: .[0].major,
is_latest: .[0].is_latest,
platforms: (map(.platforms) | add | unique),
variants: (
map({(.variant): {tags: .tags, platforms: .platforms, digest: .digest}}) | add
)
})
| sort_by(.major) | reverse
)
}' parts/*/part.json > manifest.json
cat manifest.json
- uses: actions/upload-artifact@v4
with:
name: manifest
path: manifest.json