Files
images/hack/select-images.sh
T
rmcguireandClaude Opus 5 1878df96ac
Lint / lint (push) Failing after 34s
Build Images / ${{ fromJSON(needs.select.outputs.images) }} (push) Canceled after 7m37s
Build Images / Select images (push) Successful in 17s
Initial commit: image registry structure and node-agent
Set up this repo as a base image registry. Each image is self-contained in
its own directory under images/ (Dockerfile + README.md + optional test.sh);
CI discovers them by glob, so adding an image needs no workflow changes.
template/ is the skeleton to copy.

The Makefile is the single entry point for both local work and CI, so a green
`make all` locally means a green pipeline.

Workflows:
  * lint.yaml  — layout check, hadolint, shellcheck
  * build.yaml — diffs against the base commit to build only the images that
    changed, smoke-tests each one before anything is published, then pushes.
    Releases are per-image tags (<image>/vX.Y.Z); main publishes :edge.

First image, node-agent: node:22-alpine plus a GNU userland (Alpine ships
BusyBox, whose applets take narrower flags than scripts and models expect),
helm, kubectl, jq, yq, bind-tools, curl, git, ripgrep, fd and friends.
Nothing is version-pinned — rebuilding is how upstream updates land, and the
published tag is what pins things for consumers.

Its smoke test asserts the deployment contract as well as tool presence: the
image must work non-root, with a read-only root filesystem and all
capabilities dropped, which is how ToolHive runs it.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-08-09 11:40:29 -04:00

97 lines
2.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# Decide which images CI should build. Prints GITHUB_OUTPUT lines:
#
# images=["node-agent",...] JSON array consumed by the build job's matrix
# any=true|false whether there is anything to build at all
#
# Rules, first match wins:
# 1. workflow_dispatch naming one image -> that image
# 2. tag push (<image>/vX.Y.Z) -> the image named in the tag
# 3. shared build plumbing changed -> every image
# 4. anything else -> images with changed files
#
# When the diff base is unknown (first push, force-push, shallow clone) this builds
# everything. Rebuilding too much is the safe direction to fail.
set -euo pipefail
cd "$(dirname "$0")/.."
# A change to any of these can affect how every image is built.
SHARED_PATHS='^(hack/|Makefile|\.hadolint\.yaml|\.gitea/workflows/)'
all_images() {
local dockerfile
for dockerfile in images/*/Dockerfile; do
# Guards against the glob staying literal when there are no images.
[ -f "$dockerfile" ] || continue
basename "$(dirname "$dockerfile")"
done
}
# Names on stdin -> ["a","b"]. Built by hand so the runner needs no jq.
as_json() {
local out='' name
while IFS= read -r name; do
[ -n "$name" ] || continue
out="${out:+$out,}\"$name\""
done
printf '[%s]' "$out"
}
emit() {
local names=$1 reason=$2 any=false
[ -n "$names" ] && any=true
echo "selected (${reason}): ${names:-<none>}" >&2
printf 'images=%s\n' "$(printf '%s\n' "$names" | as_json)"
printf 'any=%s\n' "$any"
exit 0
}
require_image() {
[ -f "images/$1/Dockerfile" ] || {
echo "no such image: images/$1/Dockerfile does not exist" >&2
exit 1
}
}
# 1. Explicit request via workflow_dispatch.
case "${DISPATCH_IMAGE:-}" in
'') ;;
all) emit "$(all_images)" 'workflow_dispatch: all' ;;
*)
require_image "$DISPATCH_IMAGE"
emit "$DISPATCH_IMAGE" "workflow_dispatch: $DISPATCH_IMAGE"
;;
esac
# 2. Release tag: <image>/vX.Y.Z.
if [ "${GITHUB_REF_TYPE:-}" = tag ]; then
image=${GITHUB_REF_NAME%/*}
require_image "$image"
emit "$image" "tag ${GITHUB_REF_NAME}"
fi
# 3 and 4 both need a usable diff base.
base=${BASE_SHA:-}
if [ -z "$base" ] || [[ $base =~ ^0+$ ]] || ! git cat-file -e "${base}^{commit}" 2>/dev/null; then
emit "$(all_images)" 'diff base unavailable, building everything'
fi
changed=$(git diff --name-only "$base" HEAD)
if printf '%s\n' "$changed" | grep -qE "$SHARED_PATHS"; then
emit "$(all_images)" 'shared build plumbing changed'
fi
# Map changed paths back to image names, dropping any that no longer exist so a
# deleted image directory does not fail the build.
selected=$(
printf '%s\n' "$changed" |
sed -n 's#^images/\([^/]*\)/.*#\1#p' |
sort -u |
while IFS= read -r i; do
[ -f "images/$i/Dockerfile" ] && echo "$i"
done
)
emit "$selected" 'changed paths'