Create reusable runner image build action
This commit is contained in:
159
action.yml
Normal file
159
action.yml
Normal file
@@ -0,0 +1,159 @@
|
||||
name: Build and publish runner image
|
||||
description: Build, verify, and publish a runner image to a private Gitea registry.
|
||||
|
||||
inputs:
|
||||
registry:
|
||||
description: Registry hostname, for example mahgit.ir.
|
||||
required: true
|
||||
image:
|
||||
description: Fully qualified image name without a tag.
|
||||
required: true
|
||||
base-image:
|
||||
description: Base image used by the default Dockerfile.
|
||||
required: false
|
||||
default: mahgit.ir/meghdadfadaee/build-runner-image-base:node-20-bookworm
|
||||
context:
|
||||
description: Docker build context relative to the checked-out repository.
|
||||
required: false
|
||||
default: .
|
||||
requirements-file:
|
||||
description: Requirements lock path relative to the Docker build context.
|
||||
required: false
|
||||
default: requirements.lock
|
||||
dockerfile:
|
||||
description: Optional caller Dockerfile path; defaults to the action Dockerfile.
|
||||
required: false
|
||||
default: ""
|
||||
source-url:
|
||||
description: Repository URL stored in the OCI source label.
|
||||
required: true
|
||||
registry-username:
|
||||
description: Registry username.
|
||||
required: true
|
||||
registry-token:
|
||||
description: Registry token with package Read and Write permission.
|
||||
required: true
|
||||
verify-commands:
|
||||
description: Shell commands that verify the candidate image using IMAGE_REF.
|
||||
required: false
|
||||
default: ""
|
||||
|
||||
outputs:
|
||||
image:
|
||||
description: Fully qualified image name without a tag.
|
||||
value: ${{ steps.publish.outputs.image }}
|
||||
revision:
|
||||
description: Twelve-character Git revision used as the immutable tag.
|
||||
value: ${{ steps.publish.outputs.revision }}
|
||||
revision_tag:
|
||||
description: Fully qualified immutable image reference.
|
||||
value: ${{ steps.publish.outputs.revision_tag }}
|
||||
latest_tag:
|
||||
description: Fully qualified latest image reference.
|
||||
value: ${{ steps.publish.outputs.latest_tag }}
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Install Docker client
|
||||
shell: sh
|
||||
run: |
|
||||
set -eu
|
||||
if command -v docker >/dev/null 2>&1; then
|
||||
docker version --format 'Docker client {{.Client.Version}}, server {{.Server.Version}}'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! command -v apt-get >/dev/null 2>&1; then
|
||||
echo "Docker is unavailable and this job image does not provide apt-get"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for attempt in 1 2 3; do
|
||||
if apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
||||
--no-install-recommends docker.io; then
|
||||
break
|
||||
fi
|
||||
if [ "$attempt" -eq 3 ]; then
|
||||
echo "Unable to install the Docker client after 3 attempts"
|
||||
exit 1
|
||||
fi
|
||||
sleep "$((attempt * 5))"
|
||||
done
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
docker version --format 'Docker client {{.Client.Version}}, server {{.Server.Version}}'
|
||||
|
||||
- name: Build, verify, and publish image
|
||||
id: publish
|
||||
shell: sh
|
||||
env:
|
||||
INPUT_REGISTRY: ${{ inputs.registry }}
|
||||
INPUT_IMAGE: ${{ inputs.image }}
|
||||
INPUT_BASE_IMAGE: ${{ inputs.base-image }}
|
||||
INPUT_CONTEXT: ${{ inputs.context }}
|
||||
INPUT_REQUIREMENTS_FILE: ${{ inputs.requirements-file }}
|
||||
INPUT_DOCKERFILE: ${{ inputs.dockerfile }}
|
||||
INPUT_SOURCE_URL: ${{ inputs.source-url }}
|
||||
INPUT_REGISTRY_USERNAME: ${{ inputs.registry-username }}
|
||||
INPUT_REGISTRY_TOKEN: ${{ inputs.registry-token }}
|
||||
INPUT_VERIFY_COMMANDS: ${{ inputs.verify-commands }}
|
||||
ACTION_PATH: ${{ gitea.action_path }}
|
||||
run: |
|
||||
set -eu
|
||||
: "${INPUT_REGISTRY:?registry is required}"
|
||||
: "${INPUT_IMAGE:?image is required}"
|
||||
: "${INPUT_BASE_IMAGE:?base-image is required}"
|
||||
: "${INPUT_CONTEXT:?context is required}"
|
||||
: "${INPUT_REQUIREMENTS_FILE:?requirements-file is required}"
|
||||
: "${INPUT_SOURCE_URL:?source-url is required}"
|
||||
: "${INPUT_REGISTRY_USERNAME:?registry-username is required}"
|
||||
: "${INPUT_REGISTRY_TOKEN:?registry-token is required}"
|
||||
|
||||
revision="$(git rev-parse --short=12 HEAD)"
|
||||
candidate="$INPUT_IMAGE:$revision"
|
||||
latest="$INPUT_IMAGE:latest"
|
||||
dockerfile="$INPUT_DOCKERFILE"
|
||||
if [ -z "$dockerfile" ]; then
|
||||
dockerfile="$ACTION_PATH/runner-image/Dockerfile"
|
||||
fi
|
||||
if [ ! -f "$dockerfile" ]; then
|
||||
echo "Dockerfile does not exist: $dockerfile"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cleanup() {
|
||||
docker logout "$INPUT_REGISTRY" >/dev/null 2>&1 || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
printf '%s' "$INPUT_REGISTRY_TOKEN" | docker login "$INPUT_REGISTRY" \
|
||||
--username "$INPUT_REGISTRY_USERNAME" \
|
||||
--password-stdin
|
||||
|
||||
docker build \
|
||||
--pull \
|
||||
--label "org.opencontainers.image.revision=$revision" \
|
||||
--label "org.opencontainers.image.source=$INPUT_SOURCE_URL" \
|
||||
--build-arg "BASE_IMAGE=$INPUT_BASE_IMAGE" \
|
||||
--build-arg "REQUIREMENTS_FILE=$INPUT_REQUIREMENTS_FILE" \
|
||||
--file "$dockerfile" \
|
||||
--tag "$candidate" \
|
||||
"$INPUT_CONTEXT"
|
||||
|
||||
if [ -n "$INPUT_VERIFY_COMMANDS" ]; then
|
||||
export IMAGE_REF="$candidate"
|
||||
sh -eu -c "$INPUT_VERIFY_COMMANDS"
|
||||
fi
|
||||
|
||||
docker push "$candidate"
|
||||
docker tag "$candidate" "$latest"
|
||||
docker push "$latest"
|
||||
|
||||
if [ -n "${GITHUB_OUTPUT:-}" ]; then
|
||||
{
|
||||
echo "image=$INPUT_IMAGE"
|
||||
echo "revision=$revision"
|
||||
echo "revision_tag=$candidate"
|
||||
echo "latest_tag=$latest"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
Reference in New Issue
Block a user