From ab192231d8bf0943dfa8c53e2deb940adde60180 Mon Sep 17 00:00:00 2001 From: Brian Long Date: Fri, 4 Sep 2026 14:02:15 -0400 Subject: [PATCH] initial commit --- .gitignore | 2 + README.md | 106 +++++++++++++++++++++++++++++++++++ cicd/build.sh | 35 ++++++++++++ cicd/push-auth.sh | 17 ++++++ cicd/push-sha.sh | 19 +++++++ cicd/push-version.sh | 32 +++++++++++ containerd/Containerfile | 25 +++++++++ containerd/configure-curl.sh | 41 ++++++++++++++ containerd/configure-git.sh | 10 ++++ containerd/entrypoint.sh | 23 ++++++++ 10 files changed, 310 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100755 cicd/build.sh create mode 100755 cicd/push-auth.sh create mode 100755 cicd/push-sha.sh create mode 100755 cicd/push-version.sh create mode 100644 containerd/Containerfile create mode 100644 containerd/configure-curl.sh create mode 100644 containerd/configure-git.sh create mode 100644 containerd/entrypoint.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f7eba32 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# IDEA +.idea diff --git a/README.md b/README.md new file mode 100644 index 0000000..2649f9d --- /dev/null +++ b/README.md @@ -0,0 +1,106 @@ +# Base Runner Image + +This is a base container image for CI/CD processes. + +## Description + +Based on the Debian Slim series of images, this image provides common tools for continuous integration/delivery. It is only for build, not runtime purposes. These are used by GitHub, GitLab, and Gitea runners to perform builds, releases, and related tasks. + +## Features + +### Inherited + +This image provides everything [Debian Slim](https://github.com/linuxcontainers/debian-slim) provides. + +### Tools + +This following tools are installed and available with this image. + +`tar` +: TAR extraction or creation + +`gzip` +: GZIP or `tar.gz` extraction or creation + +`zip`/`unzip` +: ZIP extraction or creation + +`curl` +: HTTP service calls, including downloads + +`jq` +: JSON parsing and modification + +`xmlstarlet` +: XML parsing and modification + +`git` +: Git source control + +### Extensible `ENTRYPOINT` + +This base image implements a universal `ENTRYPOINT` script that executes all executable scripts in `/opt/containerd` that end with `.sh`. This provides the ability for all extending container images to include their own entrypoint script without overriding or shadowing any ancestral entrypoint. + +This is nothing to the `CMD` directive, but if specified by an extending image, it will be respected. + +## Tags + +A version labeled `latest` always exists. + +| Tag | Description | +| -------- | ----------- | +| `latest` | The latest version | +| `X` | The latest version within the specified major version `X`. | +| `X.Y` | The latest version within the specified minor version `X.Y`. | +| `X.Y.Z` | A specific version. | + +## Configuration + +### Environment Variables + +| Name | Required | Default | Purpose | +| ------------------------ |:--------:| --------------- | ------- | +| `GIT_CONFIG_USER_NAME` | No | | Initialize Git to use this `user.name`. | +| `GIT_CONFIG_USER_EMAIL` | No | | Initialize Git to use this `user.email`. | +| `CURL_CONFIG_DIR` | No | `/opt/etc/curl` | Expected location of Curl configuration files. | +| `CURL_CONFIG_IDS` | No | | Create Curl configuration file(s) using values provided in corresponding environment variables. | +| `CURL_CONFIG_*_USERNAME` | No | | An HTTP BASIC username for a corresponding Curl configuration. | +| `CURL_CONFIG_*_PASSWORD` | No | | An HTTP BASIC password for a corresponding Curl configuration. | + +`CURL_CONFIG_IDS` +: The expected format: `: [: [: ...]]`. The key is used in place of the `*` in the environment variables above. The ID is used in the configuration filename. + +#### Examples + +Here are sample environment variable definitions: + +```sh +GIT_CONFIG_USER_NAME=John Doe +GIT_CONFIG_USER_EMAIL=john.doe@inteligr8.com +CURL_CONFIG_IDS="biz1:BIZ1 biz2:BIZ2" +CURL_CONFIG_BIZ1_USERNAME=john.doe +CURL_CONFIG_BIZ1_PASSWORD= +CURL_CONFIG_BIZ2_USERNAME=jane.doe +CURL_CONFIG_BIZ2_PASSWORD= +``` + +Here is the corresponding usage of `curl` with the configuration: + +```sh +curl --config ${CURL_CONFIG_DIR}/biz1.cfg ... +curl --config ${CURL_CONFIG_DIR}/biz2.cfg ... +``` + +## Usage + +This is expected to be extended or used directly for simple CI/CD runners. + +## Contributing + +You are welcome to copy the repository, but please open an issue first to avoid fragmentation. + +- [Open Issue](https://git.inteligr8.com/inteligr8/base-runner-image/issues) + +## References + +- [Debian Slim](https://github.com/linuxcontainers/debian-slim) \ No newline at end of file diff --git a/cicd/build.sh b/cicd/build.sh new file mode 100755 index 0000000..7dbca30 --- /dev/null +++ b/cicd/build.sh @@ -0,0 +1,35 @@ +#!/bin/bash +set -e + +[[ -v ${CONTAINERD_REGISTRY_HOST} ]] || CONTAINERD_REGISTRY_HOST=docker.inteligr8.com +[[ -v ${CONTAINERD_IMAGE_NAMESPACE} ]] || CONTAINERD_IMAGE_NAMESPACE=inteligr8/ +[[ -v ${CONTAINERD_IMAGE_NAME} ]] || CONTAINERD_IMAGE_NAME=base-runner +[[ -v ${CONTAINERD_CMD} ]] || CONTAINERD_CMD=buildah +[[ -v ${CONTAINERD_BUILD_FORMAT} ]] || CONTAINERD_BUILD_FORMAT=docker +[[ -v ${DEBIAN_VERSION} ]] || DEBIAN_VERSION=13 + +if [[ -v ${CONTAINERD_DEBUG} ]]; then + SKOPEO_OPTS="${SKOPEO_OPTS} --debug" + CONTAINERD_CMD_OPTS="${CONTAINERD_CMD_OPTS} --log-level=debug" +fi + +GIT_COMMIT_SHORT_SHA=$(git rev-parse --short HEAD) +CONTAINERD_IMAGE_REMOTE_NAME=${CONTAINERD_REGISTRY_HOST}/${CONTAINERD_IMAGE_NAMESPACE}${CONTAINERD_IMAGE_NAME} + +set +e +skopeo ${SKOPEO_OPTS} inspect docker://${CONTAINERD_IMAGE_REMOTE_NAME}:sha${GIT_COMMIT_SHORT_SHA} +SKOPEO_INSPECT_RC=$? +set -e + +if [[ "${INSPECT_RC}" == "0" ]]; then + echo "Container image already built; skipping rebuild" +else + echo "Building container image" + ${CONTAINERD_CMD} ${CONTAINERD_CMD_OPTS} build \ + --format=${CONTAINERD_BUILD_FORMAT} \ + ${CONTAINERD_BUILD_OPTS} \ + --build-arg DEBIAN_VERSION=${DEBIAN_VERSION} \ + --build-arg CONTAINERD_REGISTRY_HOST=${CONTAINERD_REGISTRY_HOST} \ + --build-arg CONTAINERD_IMAGE_NAMESPACE=${CONTAINERD_IMAGE_NAMESPACE} \ + --tag ${CONTAINERD_IMAGE_REMOTE_NAME}:sha${GIT_COMMIT_SHORT_SHA} +fi diff --git a/cicd/push-auth.sh b/cicd/push-auth.sh new file mode 100755 index 0000000..96055b0 --- /dev/null +++ b/cicd/push-auth.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -e + +[[ -v ${CONTAINERD_REGISTRY_HOST} ]] || CONTAINERD_REGISTRY_HOST=docker.inteligr8.com +[[ -v ${CONTAINERD_CMD} ]] || CONTAINERD_CMD=buildah + +if [[ -v ${CONTAINERD_DEBUG} ]]; then + CONTAINERD_CMD_OPTS="${CONTAINERD_CMD_OPTS} --log-level=debug" +fi + +if [[ -v ${CONTAINERD_REGISTRY_USERNAME} && -v ${CONTAINERD_REGISTRY_PASSWORD} ]]; then + echo "Authenticating with container registry" + echo "${CONTAINERD_REGISTRY_PASSWORD}" | ${CONTAINERD_CMD} ${CONTAINERD_CMD_OPTS} login \ + ${CONTAINERD_REGISTRY_HOST} \ + -u ${CONTAINERD_REGISTRY_USERNAME} \ + --password-stdin +fi diff --git a/cicd/push-sha.sh b/cicd/push-sha.sh new file mode 100755 index 0000000..7e7290e --- /dev/null +++ b/cicd/push-sha.sh @@ -0,0 +1,19 @@ +#!/bin/bash +set -e + +[[ -v ${CONTAINERD_REGISTRY_HOST} ]] || CONTAINERD_REGISTRY_HOST=docker.inteligr8.com +[[ -v ${CONTAINERD_IMAGE_NAMESPACE} ]] || CONTAINERD_IMAGE_NAMESPACE=inteligr8/ +[[ -v ${CONTAINERD_IMAGE_NAME} ]] || CONTAINERD_IMAGE_NAME=base-runner +[[ -v ${CONTAINERD_CMD} ]] || CONTAINERD_CMD=buildah + +if [[ -v ${CONTAINERD_DEBUG} ]]; then + CONTAINERD_CMD_OPTS="${CONTAINERD_CMD_OPTS} --log-level=debug" +fi + +GIT_COMMIT_SHORT_SHA=$(git rev-parse --short HEAD) +CONTAINERD_IMAGE_REMOTE_NAME=${CONTAINERD_REGISTRY_HOST}/${CONTAINERD_IMAGE_NAMESPACE}${CONTAINERD_IMAGE_NAME} + +echo "Pushing container image to registry with commit SHA: ${GIT_COMMIT_SHORT_SHA}" +${CONTAINERD_CMD} ${CONTAINERD_CMD_OPTS} push \ + ${CONTAINERD_PUSH_OPTS} \ + ${CONTAINERD_IMAGE_REMOTE_NAME}:sha${GIT_COMMIT_SHORT_SHA} diff --git a/cicd/push-version.sh b/cicd/push-version.sh new file mode 100755 index 0000000..054dfbb --- /dev/null +++ b/cicd/push-version.sh @@ -0,0 +1,32 @@ +#!/bin/bash +set -e + +[[ -v ${CONTAINERD_REGISTRY_HOST} ]] || CONTAINERD_REGISTRY_HOST=docker.inteligr8.com +[[ -v ${CONTAINERD_IMAGE_NAMESPACE} ]] || CONTAINERD_IMAGE_NAMESPACE=inteligr8/ +[[ -v ${CONTAINERD_IMAGE_NAME} ]] || CONTAINERD_IMAGE_NAME=base-runner +[[ -v ${CONTAINERD_CMD} ]] || CONTAINERD_CMD=buildah + +if [[ -v ${CONTAINERD_DEBUG} ]]; then + CONTAINERD_CMD_OPTS="${CONTAINERD_CMD_OPTS} --log-level=debug" +fi + +if [[ ! -z $1 ]]; then + CONTAINERD_IMAGE_TAG=$1 +elif [[ ! -z ${VERSION} ]]; then + CONTAINERD_IMAGE_TAG=${VERSION} +fi + +GIT_COMMIT_SHORT_SHA=$(git rev-parse --short HEAD) +CONTAINERD_IMAGE_REMOTE_NAME=${CONTAINERD_REGISTRY_HOST}/${CONTAINERD_IMAGE_NAMESPACE}${CONTAINERD_IMAGE_NAME} + +echo "Synchronizing container image from registry for SHA: ${GIT_COMMIT_SHORT_SHA}" +${CONTAINERD_CMD} ${CONTAINERD_CMD_OPTS} pull \ + ${CONTAINERD_IMAGE_REMOTE_NAME}:sha${GIT_COMMIT_SHORT_SHA} +${CONTAINERD_CMD} ${CONTAINERD_CMD_OPTS} tag \ + ${CONTAINERD_IMAGE_REMOTE_NAME}:sha${GIT_COMMIT_SHORT_SHA} + ${CONTAINERD_IMAGE_REMOTE_NAME}:${CONTAINERD_IMAGE_TAG} + +echo "Tagging container image to registry with version: ${CONTAINERD_IMAGE_TAG}" +${CONTAINERD_CMD} ${CONTAINERD_CMD_OPTS} push \ + ${CONTAINERD_PUSH_OPTS} \ + ${CONTAINERD_IMAGE_REMOTE_NAME}:${CONTAINERD_IMAGE_TAG} diff --git a/containerd/Containerfile b/containerd/Containerfile new file mode 100644 index 0000000..89ec20d --- /dev/null +++ b/containerd/Containerfile @@ -0,0 +1,25 @@ +ARG DEBIAN_VERSION=stable +FROM debian:${DEBIAN_VERSION}-slim + +# Container metadata +LABEL org.opencontainers.image.title="Base Runner" +LABEL org.opencontainers.image.description="CI/CD Runner image with common CLI utilities" +LABEL org.opencontainers.image.url="https://git.inteligr8.com/inteligr8/base-runner-image" +LABEL org.opencontainers.image.authors="Brian Long " +LABEL org.opencontainers.image.license="LGPL" + +# Common utilities +RUN apt update && \ + apt install -y tar gzip zip unzip curl jq xmlstarlet git + +# Support multiple entrypoint scripts +RUN mkdir -p /opt/containerd +ADD --chmod=755 entrypoint.sh /opt/containerd/entrypoint.sh +ADD --chmod=755 configure-git.sh /opt/containerd/00-configure-git.sh +ADD --chmod=755 configure-curl.sh /opt/containerd/00-configure-curl.sh + +# Support build-defined curl configuration +ENV CURL_CONFIG_DIR=/opt/etc/curl +RUN mkdir -p ${CURL_CONFIG_DIR} + +ENTRYPOINT ["/opt/containerd/entrypoint.sh"] diff --git a/containerd/configure-curl.sh b/containerd/configure-curl.sh new file mode 100644 index 0000000..36eb984 --- /dev/null +++ b/containerd/configure-curl.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# AI generated +urlencode() { + local LANG=C i x + for (( i = 0; i < ${#1}; i++ )); do + x="${1:i:1}" + [[ "$x" =~ [a-zA-Z0-9.~_-] ]] && echo -n "$x" || printf '%%%02X' "'$x" + done + echo +} + +if [[ ! -z ${CURL_CONFIG_IDS} ]]; then + mkdir -p ${CURL_CONFIG_DIR:-/opt/etc/curl} + + for CURL_CONFIG_PAIR in ${CURL_CONFIG_IDS}; do + if [[ ${CURL_CONFIG_PAIR} =~ ^([^:]+):(.+)$ ]]; then + CURL_CONFIG_ID=${BASH_REMATCH[0]} + CURL_CONFIG_ENVVAR_KEY=${BASH_REMATCH[1]} + CURL_CONFIG_ENVVAR_USERNAME="CURL_CONFIG_${CURL_CONFIG_ENVVAR_KEY}_USERNAME" + CURL_CONFIG_ENVVAR_PASSWORD="CURL_CONFIG_${CURL_CONFIG_ENVVAR_KEY}_PASSWORD" + + CURL_CONFIG_USERNAME=`urlencode ${!CURL_CONFIG_ENVVAR_USERNAME}` + CURL_CONFIG_PASSWORD=`urlencode ${!CURL_CONFIG_ENVVAR_PASSWORD}` + + if [[ -z ${CURL_CONFIG_USERNAME} ]]; then + echo "The curl configuration '${CURL_CONFIG_ID}' has no corresponding environment variable: ${CURL_CONFIG_ENVVAR_USERNAME}; ignoring" + elif [[ -z ${CURL_CONFIG_PASSWORD} ]]; then + echo "The curl configuration '${CURL_CONFIG_ID}' has no corresponding environment variable: ${CURL_CONFIG_ENVVAR_PASSWORD}; ignoring" + else + CURL_CONFIG_FILE=${CURL_CONFIG_DIR:-/opt/etc/curl}/${CURL_CONFIG_ID}.cfg + echo "user = ${CURL_CONFIG_USERNAME}:${CURL_CONFIG_PASSWORD}" > ${CURL_CONFIG_FILE} + + echo "Added CURL configuration file: ${CURL_CONFIG_FILE}" + fi + else + echo "The curl configuration format in 'CURL_CONFIG_IDS' is invalid: ${CURL_CONFIG_PAIR}" + exit 1 + fi + done +fi diff --git a/containerd/configure-git.sh b/containerd/configure-git.sh new file mode 100644 index 0000000..6748c41 --- /dev/null +++ b/containerd/configure-git.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -e + +if [[ ! -z ${GIT_CONFIG_USER_NAME} ]]; then + git config --global user.name "${GIT_CONFIG_USER_NAME}" +fi + +if [[ ! -z ${GIT_CONFIG_USER_EMAIL} ]]; then + git config --global user.email "${GIT_CONFIG_USER_EMAIL}" +fi diff --git a/containerd/entrypoint.sh b/containerd/entrypoint.sh new file mode 100644 index 0000000..b682937 --- /dev/null +++ b/containerd/entrypoint.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# any command returning non-zero, should end this script +set -e + +# cycle through *.sh in `opt/containerd` in alphabetical order; executing each one +CONTAINERD_FILES=( /opt/containerd/*.sh ) +if (( ${#CONTAINERD_FILES[@]} > 1 )); then + for (( CONTAINERD_FILEINDEX = 0; CONTAINERD_FILEINDEX < ${#CONTAINERD_FILES[@]}; CONTAINERD_FILEINDEX++ )); do + CONTAINERD_FILE=${CONTAINERD_FILES[${CONTAINERD_FILEINDEX}]} + if [[ ${CONTAINERD_FILE} != "/opt/containerd/entrypoint.sh" ]]; then + echo "Executing entrypoint: ${CONTAINERD_FILE}" + . ${CONTAINERD_FILE} + fi + done +fi + +# if there are arguments to the entrypoint, execute them to finish +# like a server startup command +if (( $# > 0 )); then + echo "Executing entrypoint ..." + exec "$@" +fi