Skip to content
Open
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
67 changes: 67 additions & 0 deletions .github/scripts/find-latest-image.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/usr/bin/env bash
set -euo pipefail

# Searches backwards through GitHub releases to find the newest release
# where all specified container images have been published.
#
# Usage: find-latest-image.sh <github_repo> <image1> [image2 ...]
# github_repo: GitHub repo in "owner/name" format
# image1..N: Full image references without tags (e.g., ghcr.io/org/name)
#
# Options (via environment variables):
# SEMVER_ONLY=true Skip non-semver tags (default: false)
# FALLBACK_TAG=<tag> Tag to return if no release has published images (default: exits 1)
#
# Outputs the matched tag to stdout. All logging goes to stderr.

GITHUB_REPO="$1"
shift
IMAGES=("$@")

if [ ${#IMAGES[@]} -eq 0 ]; then
echo "Usage: find-latest-image.sh <github_repo> <image1> [image2 ...]" >&2
exit 1
fi

SEMVER_ONLY="${SEMVER_ONLY:-false}"
FALLBACK_TAG="${FALLBACK_TAG:-}"

RELEASES=$(curl -sf --retry 3 --retry-delay 5 \
"https://api.github.com/repos/${GITHUB_REPO}/releases?per_page=20" \
| jq -r '.[].tag_name')

for RELEASE_TAG in $RELEASES; do
if [ "$SEMVER_ONLY" = "true" ]; then
if ! echo "$RELEASE_TAG" | grep -qE '^v[0-9]+\.[0-9]+\.[0-9]+'; then
echo "Skipping non-semver tag: $RELEASE_TAG" >&2
continue
fi
fi

echo "Checking images for ${GITHUB_REPO} release ${RELEASE_TAG}..." >&2
ALL_FOUND=true
for IMAGE in "${IMAGES[@]}"; do
if ! skopeo inspect --override-arch amd64 --override-os linux \
"docker://${IMAGE}:${RELEASE_TAG}" > /dev/null 2>&1; then
ALL_FOUND=false
break
fi
done

if [ "$ALL_FOUND" = "true" ]; then
echo "Found images for ${GITHUB_REPO} release ${RELEASE_TAG}" >&2
echo "$RELEASE_TAG"
exit 0
else
echo "Images not found for ${RELEASE_TAG}, trying next..." >&2
fi
done

if [ -n "$FALLBACK_TAG" ]; then
echo "No versioned images found, using fallback: ${FALLBACK_TAG}" >&2
echo "$FALLBACK_TAG"
exit 0
fi

echo "No release with published images found for ${GITHUB_REPO}" >&2
exit 1
110 changes: 110 additions & 0 deletions .github/workflows/update-image-versions.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
name: Update image versions

on:
schedule:
- cron: '17 6 * * *'
workflow_dispatch: {}

permissions:
contents: write
pull-requests: write

env:
REGISTRY: ghcr.io/complianceascode
IMAGES_FILE: pkg/utils/images.go

jobs:
update-images:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install skopeo
run: |
sudo apt-get update
sudo apt-get install -y skopeo

- name: Get latest compliance-operator release with published images
id: co-release
run: |
CO_TAG=$(SEMVER_ONLY=true .github/scripts/find-latest-image.sh \
ComplianceAsCode/compliance-operator \
"$REGISTRY/compliance-operator" \
"$REGISTRY/openscap-ocp")
echo "tag=$CO_TAG" >> "$GITHUB_OUTPUT"

- name: Get latest k8scontent revision
id: k8scontent-release
run: |
K8S_TAG=$(skopeo inspect --override-arch amd64 --override-os linux \
"docker://$REGISTRY/k8scontent:latest" \
| jq -r '.Labels["org.opencontainers.image.revision"]')

if [ -z "$K8S_TAG" ] || [ "$K8S_TAG" = "null" ]; then
echo "Failed to get k8scontent revision from :latest"
exit 1
fi

echo "Verifying k8scontent:${K8S_TAG} exists..."
skopeo inspect --override-arch amd64 --override-os linux \
"docker://$REGISTRY/k8scontent:${K8S_TAG}" > /dev/null

echo "tag=$K8S_TAG" >> "$GITHUB_OUTPUT"

- name: Check for version changes
id: check
run: |
CO_TAG="${{ steps.co-release.outputs.tag }}"
K8S_TAG="${{ steps.k8scontent-release.outputs.tag }}"

OPENSCAP_CUR=$(grep 'openscap-ocp:' "$IMAGES_FILE" | sed 's/.*openscap-ocp:\([^"]*\).*/\1/')
OPERATOR_CUR=$(grep 'compliance-operator:' "$IMAGES_FILE" | sed 's/.*compliance-operator:\([^"]*\).*/\1/')
K8S_CUR=$(grep 'k8scontent:' "$IMAGES_FILE" | sed 's/.*k8scontent:\([^"]*\).*/\1/')

echo "Current: openscap-ocp=$OPENSCAP_CUR compliance-operator=$OPERATOR_CUR k8scontent=$K8S_CUR"
echo "Resolved: openscap-ocp=$CO_TAG compliance-operator=$CO_TAG k8scontent=$K8S_TAG"

echo "openscap=$OPENSCAP_CUR" >> "$GITHUB_OUTPUT"
echo "operator=$OPERATOR_CUR" >> "$GITHUB_OUTPUT"
echo "k8scontent=$K8S_CUR" >> "$GITHUB_OUTPUT"

CHANGED="false"
if [ "$CO_TAG" != "$OPENSCAP_CUR" ] || [ "$CO_TAG" != "$OPERATOR_CUR" ] || [ "$K8S_TAG" != "$K8S_CUR" ]; then
CHANGED="true"
fi
echo "changed=$CHANGED" >> "$GITHUB_OUTPUT"

- name: Update image versions
if: steps.check.outputs.changed == 'true'
run: |
CO_TAG="${{ steps.co-release.outputs.tag }}"
K8S_TAG="${{ steps.k8scontent-release.outputs.tag }}"

sed -i 's|openscap-ocp:[^"]*|openscap-ocp:'"${CO_TAG}"'|' "$IMAGES_FILE"
sed -i 's|compliance-operator:[^"]*|compliance-operator:'"${CO_TAG}"'|' "$IMAGES_FILE"
sed -i 's|k8scontent:[^"]*|k8scontent:'"${K8S_TAG}"'|' "$IMAGES_FILE"

echo "Updated $IMAGES_FILE:"
grep -A2 'componentDefaults' "$IMAGES_FILE" | tail -5

- name: Create pull request
if: steps.check.outputs.changed == 'true'
uses: peter-evans/create-pull-request@v7
with:
commit-message: |
Update container image versions

openscap-ocp: ${{ steps.check.outputs.openscap }} -> ${{ steps.co-release.outputs.tag }}
compliance-operator: ${{ steps.check.outputs.operator }} -> ${{ steps.co-release.outputs.tag }}
k8scontent: ${{ steps.check.outputs.k8scontent }} -> ${{ steps.k8scontent-release.outputs.tag }}
title: "Update container image versions"
body: |
Automated update of pinned container image versions in `pkg/utils/images.go`.

| Image | Previous | New |
|-------|----------|-----|
| openscap-ocp | `${{ steps.check.outputs.openscap }}` | `${{ steps.co-release.outputs.tag }}` |
| compliance-operator | `${{ steps.check.outputs.operator }}` | `${{ steps.co-release.outputs.tag }}` |
| k8scontent | `${{ steps.check.outputs.k8scontent }}` | `${{ steps.k8scontent-release.outputs.tag }}` |
branch: automated/update-image-versions
delete-branch: true
6 changes: 3 additions & 3 deletions pkg/utils/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ var componentDefaults = []struct {
defaultImage string
envVar string
}{
{"ghcr.io/complianceascode/openscap-ocp:latest", "RELATED_IMAGE_OPENSCAP"},
{"ghcr.io/complianceascode/compliance-operator:latest", "RELATED_IMAGE_OPERATOR"},
{"ghcr.io/complianceascode/k8scontent:latest", "RELATED_IMAGE_PROFILE"},
{"ghcr.io/complianceascode/openscap-ocp:v1.7.0", "RELATED_IMAGE_OPENSCAP"},
{"ghcr.io/complianceascode/compliance-operator:v1.7.0", "RELATED_IMAGE_OPERATOR"},
{"ghcr.io/complianceascode/k8scontent:b01ffe68cc1320ee472408798bc56d83cfbfb1f7", "RELATED_IMAGE_PROFILE"},
}

// GetComponentImage returns a full image pull spec for a given component
Expand Down
Loading