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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this kind of doing what Konflux does already?
See for example:

We still need to setup some guidelines for auto-merge when these bumps pass all tests.

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"},
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should transition these images to use what we build from Konflux on every PR and merge.
We have a script that updates the CSV in the bundle-image manifest:

konfluxOperatorPullSpec = "quay.io/redhat-user-workloads/ocp-isc-tenant/compliance-operator-dev@sha256:ef4a592f20ef7665b98abd9e7f680f5b87baf7ccc8897ca5b60e78aa8bdfeab1"
konfluxContentPullSpec = "quay.io/redhat-user-workloads/ocp-isc-tenant/compliance-operator-content-dev@sha256:260ce52bc037253cbdeaaf2cc6f3bd55c30afeee989eac8bb52d9d0ed2a9b8f2"
konfluxOpenscapPullSpec = "quay.io/redhat-user-workloads/ocp-isc-tenant/compliance-operator-openscap-dev@sha256:463785a9048ce6e966118e1fe0181909e62f8aeef5fb5ffe146960cb5c9cbeb3"
konfluxMustGatherPullSpec = "quay.io/redhat-user-workloads/ocp-isc-tenant/compliance-operator-must-gather-dev@sha256:4a4685c02d92e856dfbc4333445271aaa71ecd90485b0b45de80cb150feef1c8"

I think it makes sense to keep using a moving tag like latest in the master branch, which is used for development. But I agree that on released versions they should be pinned to a specific image.

In the pipeline we set the moving tag to be the name of the branch, so in this case it would be:
e.g: quay.io/redhat-user-workloads/ocp-isc-tenant/compliance-operator-dev:master


https://github.com/ComplianceAsCode/content/blob/f8760a00e700af1b78b9ff18f9bc07b178dd7a59/.tekton/compliance-operator-content-dev-push.yaml#L551

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still need to do upstream release of CO 1.9.0, on release-1.9 branch.
We can pin the images from quay. io there.
What do you think?

}

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