Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .github/scripts/build_dev_image.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env bash
# Build a dev image.
#
# Tag format: <version>dev-<short sha>, e.g. 1.0.0-dev-ase241f
#
# Usage: build_dev_image.sh <image_name> [sha]
# sha defaults to HEAD.
#
# Env:
# REGISTRY, IMAGE_NAMESPACE required
# PUSH=true push; otherwise the image is loaded locally
# BUILD_CACHE=1 use the registry build cache
#
# Outputs:
# image_path full image reference
# dockerfile path to the image_name's Dockerfile
#
# Local run:
# REGISTRY=harbor.stfc.ac.uk PUSH=false IMAGE_NAMESPACE=stfc-cloud-staging \
# .github/scripts/build-release-image.sh service-a
set -euo pipefail

# shellcheck source=.github/scripts/_common.sh
source "$(dirname -- "${BASH_SOURCE[0]}")/utils.sh"

# shellcheck source=.github/scripts/build_image.sh
source "$(dirname -- "${BASH_SOURCE[0]}")/build_image.sh"


image_path=${1:?usage: build_dev_image.sh <image_path> [sha]}
sha=${2:-$(git rev-parse HEAD)}
require REGISTRY IMAGE_NAMESPACE

version=$(read_version "$image_path")
name=$(get_image_name "$image_path")
dockerfile=$(find_dockerfile "$image_path")
short=${sha:0:7}

image="$REGISTRY/$IMAGE_NAMESPACE/$name:${version}-dev-${short}"

output image "$image"
output dockerfile "$dockerfile"

build_image "$image_path" "$dockerfile" "$image"

info "built $image"
summary "dev image: \`$image\`"
46 changes: 46 additions & 0 deletions .github/scripts/build_image.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env bash

set -euo pipefail

# Build a image_path's image, tagging it with every ref passed.
#
# build_image.sh <context> <dockerfile> <tag> [tag...]
#
# Env:
#
# PUSH=true push to the registry; anything else loads into the local
# docker daemon instead, so a bare local run is always safe
#
# BUILD_CACHE=1 read (and, when pushing, write) a :buildcache tag alongside
# the image. Needs a docker-container buildx driver.
# docker/setup-buildx-action CI job builds this.
#
build_image() {
local context=$1 dockerfile=$2
shift 2
[ $# -gt 0 ] || fail "build_image: at least one tag is required"

local args=(buildx build "$context" --file "$dockerfile")
local tag
for tag in "$@"; do
args+=(--tag "$tag")
done

if [ "${BUILD_CACHE:-0}" = "1" ]; then
local cache_ref="${1%:*}:buildcache"
args+=(--cache-from "type=registry,ref=$cache_ref")
if [ "${PUSH:-false}" = "true" ]; then
args+=(--cache-to "type=registry,ref=$cache_ref,mode=max")
fi
fi

if [ "${PUSH:-false}" = "true" ]; then
args+=(--push)
else
args+=(--load)
info "PUSH is not 'true' — building locally, not pushing"
fi

info "+ docker ${args[*]}"
docker "${args[@]}"
}
59 changes: 59 additions & 0 deletions .github/scripts/build_release_image.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/usr/bin/env bash
set -euo pipefail

# Build a release image, tagged with latest version.
#
# If the git tag already exists the release has already happened,
# so this emits skip=true and does nothing rather than failing.
#
# Usage: build_release_image.sh <image_path>
#
# Env:
# REGISTRY, IMAGE_NAMESPACE required
# PUSH=true push; otherwise images are loaded locally
# BUILD_CACHE=1 use the registry build cache
#
# Outputs:
# skip "true" or "false"
# tag git tag to set (for CI), <image-name>-<version>
# image image reference
# latest :latest image reference
# dockerfile path to the image_path's Dockerfile
#
# Local run:
# REGISTRY=harbor.stfc.ac.uk PUSH=false IMAGE_NAMESPACE=stfc-cloud \
# .github/scripts/build-release-image.sh service-a

# shellcheck source=.github/scripts/utils
source "$(dirname -- "${BASH_SOURCE[0]}")/utils.sh"

# shellcheck source=.github/scripts/build_image.sh
source "$(dirname -- "${BASH_SOURCE[0]}")/build_image.sh"

image_path=${1:?usage: build-release-image.sh <image_path>}
require REGISTRY IMAGE_NAMESPACE

version=$(read_version "$image_path")
name=$(image_name "$image_path")
tag="$name-$version"

if git rev-parse -q --verify "refs/tags/$tag" > /dev/null; then
info "tag $tag already exists — nothing to release"
output skip true
exit 0
fi

dockerfile=$(find_dockerfile "$image_path")
image="$REGISTRY/$IMAGE_NAMESPACE/$name:$version"
latest="$REGISTRY/$IMAGE_NAMESPACE/$name:latest"

output skip false
output tag "$tag"
output dockerfile "$dockerfile"
output image "$image"
output latest "$latest"

build_image "$image_path" "$dockerfile" "$image" "$latest"

info "built $image"
summary "release image: \`$image\`"
29 changes: 29 additions & 0 deletions .github/scripts/bump_patch_version.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
set -euo pipefail

# Bump the patch version of a given version.txt
# Usage: bump_patch_version.sh <version.txt filepath>

version_file="${1:-}"

if [[ -z "$version_file" ]]; then
echo "Usage: $0 <path/to/version.txt>" >&2
exit 1
fi

if [[ ! -f "$version_file" ]]; then
echo "Error: file does not exist: $version_file" >&2
exit 1
fi

version=$(<"$version_file")


if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Error: $version_file must contain exactly one version in MAJOR.MINOR.PATCH format" >&2
exit 1
fi

IFS='.' read -r major minor patch <<< "$version"

printf '%s.%s.%s\n' "$major" "$minor" "$((patch + 1))" > "$version_file"
50 changes: 50 additions & 0 deletions .github/scripts/utils.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env bash
set -euo pipefail

# Shared helpers. Sourced by the other scripts, not run directly.

# allows these scripts to be used by github runners
# if run locally will go back to stdout and summaries discarded
: "${GITHUB_OUTPUT:=/dev/stdout}"
: "${GITHUB_STEP_SUMMARY:=/dev/null}"

# Emit a step output (key=value).
output() { printf '%s=%s\n' "$1" "$2" >> "$GITHUB_OUTPUT"; }

# Append a line to the job summary.
summary() { printf '%s\n' "$*" >> "$GITHUB_STEP_SUMMARY"; }

# Logging. All to stderr.
info() { printf '%s\n' "$*" >&2; }
warn() { printf '::warning::%s\n' "$*" >&2; }
fail() { printf '::error::%s\n' "$*" >&2; exit 1; }

# The image name for a image_path is just its directory name.
get_image_name() { basename "$1"; }

# Assert that the named variables are set and non-empty.
require() {
local var
for var in "$@"; do
[ -n "${!var:-}" ] || fail "$var is not set"
done
}

# Read and validate a image_path's version.txt. Prints the version.
read_version() {
local image_path=$1 file version
file="$image_path/version.txt"
[ -f "$file" ] || fail "$file not found"
version=$(tr -d '[:space:]' < "$file")
[[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] \
|| fail "$file is '$version', expected MAJOR.MINOR.PATCH"
printf '%s' "$version"
}

# Locate a image_path's Dockerfile, at any depth. Prints the path.
find_dockerfile() {
local image_path=$1 dockerfile
dockerfile=$(find "$image_path" -name Dockerfile | head -n1)
[ -n "$dockerfile" ] || fail "no Dockerfile found under $image_path"
printf '%s' "$dockerfile"
}
20 changes: 0 additions & 20 deletions .github/workflows/bin/version_increment.sh

This file was deleted.

111 changes: 0 additions & 111 deletions .github/workflows/build_images.yaml

This file was deleted.

Loading
Loading