Skip to content

Refactor admission webhook #280

Refactor admission webhook

Refactor admission webhook #280

# Publish flant/shell-operator image on hub.docker.com.
# Build 'latest' tag if started manually on default branch.
# Build 'latest' tag when PR is merged into the default branch.
# Build 'v*.*.*' and 'latest' tags on push released tag.
name: Publish release image
on:
workflow_dispatch:
pull_request:
types: [closed]
push:
tags:
- 'v*'
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 'skip' label on pull request merge.
if (eventName == 'pull_request' && event.action == 'closed') {
if (!event.pull_request.merged) {
return console.log(`PR ${event.number} not merged. Skip 'publish latest image'.`);
}
const hasSkipLabel = event.pull_request.labels.some(l => l.name === SKIP_LATEST_LABEL_NAME);
if (hasSkipLabel) {
return console.log(`Detect skip label '${SKIP_LATEST_LABEL_NAME}' on merged PR ${event.number}. Skip 'publish latest image'.`);
}
console.log(`No skip labels on merged PR ${event.number}. Run 'publish latest image'.`);
runPublish = true;
imageTag = 'latest';
}
// Check if tag is pushed.
if (eventName == 'push') {
if (!event.ref.startsWith('refs/tags/')) {
return console.log(`Detect non-release ref: ${event.ref}. Skip 'publish latest image'.`);
}
runPublish = true;
imageTag = context.ref.replace('refs/tags/', '');
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/[email protected]
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/[email protected]
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 "Copy ${GHCR_IO_IMAGE_NAME} to ${DOCKER_HUB_IMAGE_NAME} ..."
docker run \
-e "DOCKER_USER=${DOCKER_USER}" \
-e "DOCKER_PASS=${DOCKER_PASS}" \
-e "GHCR_IO_IMAGE_NAME=${GHCR_IO_IMAGE_NAME}" \
-e "DOCKER_HUB_IMAGE_NAME=${DOCKER_HUB_IMAGE_NAME}" \
quay.io/skopeo/stable:latest \
copy --all \
--dest-creds ${DOCKER_USER}:${DOCKER_PASS} \
docker://${GHCR_IO_IMAGE_NAME} \
docker://${DOCKER_HUB_IMAGE_NAME}
[ -n "$ADDITIONAL_DOCKER_HUB_IMAGE_NAME" ] && \
echo "Copy ${ADDITIONAL_GHCR_IO_IMAGE_NAME} to ${ADDITIONAL_DOCKER_HUB_IMAGE_NAME} ..." && \
docker run \
-e "DOCKER_USER=${DOCKER_USER}" \
-e "DOCKER_PASS=${DOCKER_PASS}" \
-e "GHCR_IO_IMAGE_NAME=${ADDITIONAL_GHCR_IO_IMAGE_NAME}" \
-e "DOCKER_HUB_IMAGE_NAME=${ADDITIONAL_DOCKER_HUB_IMAGE_NAME}" \
quay.io/skopeo/stable:latest \
copy --all \
--dest-creds ${DOCKER_USER}:${DOCKER_PASS} \
docker://${GHCR_IO_IMAGE_NAME} \
docker://${DOCKER_HUB_IMAGE_NAME}