Skip to content

v1.11.5

v1.11.5 #436

# Publish flant/shell-operator image on hub.docker.com.
# Build tag from release name when release is published.
# Build 'latest' tag when release is published and marked as 'latest'.
name: Publish release image
on:
workflow_dispatch:
release:
types: [published]
env:
QEMU_PLATFORMS: arm64,arm
BUILDX_PLATFORMS: "linux/amd64,linux/arm64,linux/arm/v7"
DOCKER_HUB_REPO: flant/shell-operator
GHCR_IO_REPO: ghcr.io/flant/shell-operator
jobs:
check:
name: Check
runs-on: ubuntu-latest
outputs:
run_publish: ${{ steps.check.outputs.run_publish }}
image_tag: ${{ steps.check.outputs.image_tag }}
additional_tag: ${{ steps.check.outputs.additional_tag }}
steps:
- uses: actions/github-script@v7
id: check
with:
script: |
const SKIP_LATEST_LABEL_NAME = 'skip/image/latest';
const event = context.payload;
const eventName = context.eventName;
let runPublish = false;
let imageTag = '';
let additionalTag = ''; // Also push additional tag when building a released tag.
// Check ref name for manual running.
if (eventName === 'workflow_dispatch') {
if (event.ref !== 'refs/heads/main') {
return core.setFailed(`Detect manual execution for '${event.ref}'. Use only default branch. Skip 'publish latest image'.`);
}
console.log(`Detect manual execution for default branch. Run 'publish latest image'.`);
runPublish = true;
imageTag = 'latest';
}
// Check for release is published.
if (eventName === 'release' && event.action === 'published') {
runPublish = true;
imageTag = event.release.tag_name;
// Fetch the "latest" release as defined by GitHub
const { data: latestRelease } = await github.rest.repos.getLatestRelease({
owner: context.repo.owner,
repo: context.repo.repo,
});
let isLatest = latestRelease.tag_name === imageTag;
console.log(`DEBUG: latestRelease.tag_name=${latestRelease.tag_name} imageTag=${imageTag} is_latest=${isLatest}`);
// If the release is the "latest" release, build the "latest" tag.
if (isLatest) {
console.log(`Detect current release is the "latest" release. Run 'publish latest image'.`);
additionalTag = 'latest';
}
}
console.log(`Outputs: run_publish=${runPublish} image_tag=${imageTag} additional_tag=${additionalTag}`);
if (!runPublish) {
console.log(`DEBUG: eventName=${eventName} action=${event.action} ref=${event.ref} merged=${event.pull_request && event.pull_request.merged} prLabels=${JSON.stringify(event.pull_request && event.pull_request.labels)}`)
return console.log(`Skip 'publish latest image'.`);
}
core.setOutput('run_publish', runPublish.toString());
core.setOutput('image_tag', imageTag);
core.setOutput('additional_tag', additionalTag);
publish_image:
name: Build and publish
runs-on: [ubuntu-latest]
needs:
- check
if: needs.check.outputs.run_publish == 'true'
steps:
- uses: actions/checkout@v4
- name: Prepare environment
env:
ADDITIONAL_TAG: ${{ needs.check.outputs.additional_tag }}
IMAGE_TAG: ${{ needs.check.outputs.image_tag }}
run: |
: Setup DOCKER_HUB_IMAGE_NAME, DOCKER_HUB_IMAGE_NAME and APP_VERSION for image build
APP_VERSION=${IMAGE_TAG}
if [[ $APP_VERSION == "latest" ]] ; then
APP_VERSION=${GITHUB_REF#refs/heads/}-${GITHUB_SHA::8}-$(date +'%Y.%m.%d_%H:%M:%S')
fi
DOCKER_HUB_IMAGE_NAME="${DOCKER_HUB_REPO}:${IMAGE_TAG}"
GHCR_IO_IMAGE_NAME="${GHCR_IO_REPO}:${IMAGE_TAG}"
if [[ -n $ADDITIONAL_TAG ]] ; then
ADDITIONAL_DOCKER_HUB_IMAGE_NAME="${DOCKER_HUB_REPO}:${ADDITIONAL_TAG}"
ADDITIONAL_GHCR_IO_IMAGE_NAME="${GHCR_IO_REPO}:${ADDITIONAL_TAG}"
fi
echo "APP_VERSION=${APP_VERSION}" >> ${GITHUB_ENV}
echo "DOCKER_HUB_IMAGE_NAME=${DOCKER_HUB_IMAGE_NAME}" >> ${GITHUB_ENV}
echo "ADDITIONAL_DOCKER_HUB_IMAGE_NAME=${ADDITIONAL_DOCKER_HUB_IMAGE_NAME}" >> ${GITHUB_ENV}
echo "GHCR_IO_IMAGE_NAME=${GHCR_IO_IMAGE_NAME}" >> ${GITHUB_ENV}
echo "ADDITIONAL_GHCR_IO_IMAGE_NAME=${ADDITIONAL_GHCR_IO_IMAGE_NAME}" >> ${GITHUB_ENV}
echo "========================================="
echo "APP_VERSION = $APP_VERSION"
echo "DOCKER_HUB_IMAGE_NAME = $DOCKER_HUB_IMAGE_NAME"
echo "GHCR_IO_IMAGE_NAME = $GHCR_IO_IMAGE_NAME"
[ -n $ADDITIONAL_GHCR_IO_IMAGE_NAME ] && \
echo "ADDITIONAL_DOCKER_HUB_IMAGE_NAME = $ADDITIONAL_DOCKER_HUB_IMAGE_NAME" && \
echo "ADDITIONAL_GHCR_IO_IMAGE_NAME = $ADDITIONAL_GHCR_IO_IMAGE_NAME"
echo "========================================="
- name: Set up QEMU
uses: docker/setup-qemu-action@v3.6.0
with:
platforms: "${{ env.QEMU_PLATFORMS }}"
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v3
with:
version: latest
- name: Login to Github Container Registry
uses: docker/login-action@v3.4.0
with:
registry: ghcr.io
username: ${{ secrets.GHCR_IO_USER }}
password: ${{ secrets.GHCR_IO_PASS }}
- name: Build and push multi-arch image
run: |
echo "Build and push $FINAL_IMAGE_NAME with version '$APP_VERSION'."
[ -n "$ADDITIONAL_GHCR_IO_IMAGE_NAME" ] && echo "Also push $ADDITIONAL_GHCR_IO_IMAGE_NAME."
echo
docker buildx build \
--platform $BUILDX_PLATFORMS \
--build-arg appVersion=$APP_VERSION \
$( [ -n "$ADDITIONAL_GHCR_IO_IMAGE_NAME" ] && echo "--tag $ADDITIONAL_GHCR_IO_IMAGE_NAME" ) \
--tag $GHCR_IO_IMAGE_NAME \
--push \
.
- name: Inspect binaries
run: |
# Image for one arhitecture has digest in config field.
# Image with multiple manifests has digest in each manifest.
manifests=$(docker buildx imagetools inspect "${GHCR_IO_IMAGE_NAME}" --raw)
if grep manifests <<<"${manifests}" 2>&1 >/dev/null ; then
jq -r '.manifests[]? | .digest + " " + .platform.os + "/" + .platform.architecture' <<<"${manifests}"
else
echo $(echo -n "${manifests}" | openssl dgst -sha256 | sed s/^.stdin.*\ //) ' linux/amd64'
fi \
| while read digest platform ; do
if [[ ${BUILDX_PLATFORMS} != *"${platform}"* ]] ; then
echo "====================================="
echo "Ignore image for non-runnable platform ${platform}"
echo " ${image}"
echo "====================================="
continue
fi
image=${GHCR_IO_IMAGE_NAME}@${digest}
echo "====================================="
echo "Inspect image for platform ${platform}"
echo " ${image}"
echo "====================================="
docker run --rm --platform ${platform} --entrypoint sh ${image} -c \
'apk add file > /dev/null; file /bin/kubectl; file /bin/busybox; file /shell-operator'
done
- name: Copy image to Docker Hub
env:
DOCKER_USER: ${{ secrets.DOCKER_USER }}
DOCKER_PASS: ${{ secrets.DOCKER_PASS }}
run: |
echo "Download crane tool ..."
CRANE_VERSION=$(curl -s "https://api.github.com/repos/google/go-containerregistry/releases/latest" | jq -r '.tag_name')
CRANE_OS=Linux # or Darwin, Windows
CRANE_ARCH=x86_64 # or arm64, x86_64, armv6, i386, s390x, riscv64
echo "Crane version: ${CRANE_VERSION}, OS: ${CRANE_OS}, ARCH: ${CRANE_ARCH}"
curl -sL "https://github.com/google/go-containerregistry/releases/download/${CRANE_VERSION}/go-containerregistry_${CRANE_OS}_${CRANE_ARCH}.tar.gz" > go-containerregistry.tar.gz
echo "Extract crane tool ..."
tar -zxvf go-containerregistry.tar.gz -C /usr/local/bin/ crane
export PATH=$PATH:/usr/local/bin
echo "Copy ${GHCR_IO_IMAGE_NAME} to ${DOCKER_HUB_IMAGE_NAME} ..."
crane auth login -u ${DOCKER_USER} -p ${DOCKER_PASS} docker.io && \
crane copy ${GHCR_IO_IMAGE_NAME} ${DOCKER_HUB_IMAGE_NAME}
echo "Copy completed."
if [ -n "$ADDITIONAL_DOCKER_HUB_IMAGE_NAME" ] ; then
echo "Copy ${ADDITIONAL_GHCR_IO_IMAGE_NAME} to ${ADDITIONAL_DOCKER_HUB_IMAGE_NAME} ..."
crane auth login -u ${DOCKER_USER} -p ${DOCKER_PASS} docker.io && \
crane copy ${ADDITIONAL_GHCR_IO_IMAGE_NAME} ${ADDITIONAL_DOCKER_HUB_IMAGE_NAME}
echo "Copy completed."
fi