diff --git a/.github/workflows/update-go-version.yaml b/.github/workflows/update-go-version.yaml new file mode 100644 index 0000000..85db479 --- /dev/null +++ b/.github/workflows/update-go-version.yaml @@ -0,0 +1,179 @@ +name: Update Go Version + +on: + workflow_call: + inputs: + version-schema: + description: > + Version schema key matching a file in go-versions/ (e.g. "ocp-4.21"). + The workflow fetches go-versions/.yaml from medik8s/.github + to determine the Go version and CI operator image. + required: true + type: string + dry-run: + description: "If true, only show what would change without creating a PR" + required: false + default: false + type: boolean + +permissions: + contents: write + pull-requests: write + +jobs: + update-go: + name: Update Go version + runs-on: ubuntu-24.04 + steps: + - name: Checkout operator repo + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Fetch version config from medik8s/.github + id: config + env: + VERSION_SCHEMA: ${{ inputs.version-schema }} + run: | + CONFIG_URL="https://raw.githubusercontent.com/medik8s/.github/main/go-versions/${VERSION_SCHEMA}.yaml" + HTTP_CODE=$(curl -sL -w "%{http_code}" -o /tmp/go-version.yaml "${CONFIG_URL}") + if [ "${HTTP_CODE}" != "200" ]; then + echo "::error::Version schema '${VERSION_SCHEMA}' not found at ${CONFIG_URL} (HTTP ${HTTP_CODE})" + exit 1 + fi + + # Parse simple key: "value" YAML without yq (not pre-installed on runners) + GO_VERSION=$(grep '^go:' /tmp/go-version.yaml | sed 's/^go: *"\?\([^"]*\)"\?/\1/') + CI_IMAGE=$(grep '^ci-operator-image:' /tmp/go-version.yaml | sed 's/^ci-operator-image: *"\?\([^"]*\)"\?/\1/') + + if [ -z "${GO_VERSION}" ] || [ -z "${CI_IMAGE}" ]; then + echo "::error::Failed to parse version config — go='${GO_VERSION}' ci-image='${CI_IMAGE}'" + cat /tmp/go-version.yaml + exit 1 + fi + + echo "go=${GO_VERSION}" >> "${GITHUB_OUTPUT}" + echo "ci-image=${CI_IMAGE}" >> "${GITHUB_OUTPUT}" + echo "Go version: ${GO_VERSION}" + echo "CI image: ${CI_IMAGE}" + + - name: Check if update needed + id: check + env: + TARGET_GO: ${{ steps.config.outputs.go }} + TARGET_CI_IMAGE: ${{ steps.config.outputs.ci-image }} + run: | + CURRENT_GO=$(grep '^go ' go.mod | awk '{print $2}') + HAS_TOOLCHAIN=$(grep -c '^toolchain ' go.mod || true) + + # Detect if go.mod needs changes: + # - Version mismatch (e.g. 1.24 -> 1.25) + # - Needs normalization (e.g. 1.25.0 -> 1.25) + # - Has toolchain directive that should be removed + if [ "${CURRENT_GO}" != "${TARGET_GO}" ] || [ "${HAS_TOOLCHAIN}" -gt 0 ]; then + echo "go-update=true" >> "${GITHUB_OUTPUT}" + echo "Go update needed: '${CURRENT_GO}' -> '${TARGET_GO}' (toolchain present: ${HAS_TOOLCHAIN})" + else + echo "go-update=false" >> "${GITHUB_OUTPUT}" + echo "Go version already at ${TARGET_GO}, no toolchain directive" + fi + + if [ -f .ci-operator.yaml ]; then + CURRENT_IMAGE=$(grep '^ *tag:' .ci-operator.yaml | head -1 | sed 's/.*tag: *"\?\([^"]*\)"\?/\1/') + if [ "${CURRENT_IMAGE}" = "${TARGET_CI_IMAGE}" ]; then + echo "CI image already at ${TARGET_CI_IMAGE}" + echo "ci-update=false" >> "${GITHUB_OUTPUT}" + else + echo "CI image update needed: ${CURRENT_IMAGE} -> ${TARGET_CI_IMAGE}" + echo "ci-update=true" >> "${GITHUB_OUTPUT}" + fi + else + echo "No .ci-operator.yaml found, skipping CI image update" + echo "ci-update=false" >> "${GITHUB_OUTPUT}" + fi + + - name: Set up Go + uses: actions/setup-go@v6 + with: + go-version: "${{ steps.config.outputs.go }}" + + - name: Update go.mod + if: steps.check.outputs.go-update == 'true' + env: + TARGET_GO: ${{ steps.config.outputs.go }} + run: | + # Set Go minor version only (e.g. "1.25", not "1.25.0") + go mod edit -go="${TARGET_GO}" + + # Remove toolchain directive — patch versions are handled by the builder + sed -i '/^toolchain /d' go.mod + + - name: Update dependencies + run: | + # Always run tidy + vendor to pick up dependency security patches, + # even when the Go version itself hasn't changed + go mod tidy + + if [ -d vendor ]; then + go mod vendor + fi + + - name: Update .ci-operator.yaml + if: steps.check.outputs.ci-update == 'true' + env: + TARGET_CI_IMAGE: ${{ steps.config.outputs.ci-image }} + run: | + sed -i "s|^\( tag: \).*|\1${TARGET_CI_IMAGE}|" .ci-operator.yaml + + - name: Check for changes + id: changes + run: | + if [ -z "$(git status --porcelain)" ]; then + echo "No file changes — everything is up to date" + echo "has-changes=false" >> "${GITHUB_OUTPUT}" + else + echo "has-changes=true" >> "${GITHUB_OUTPUT}" + git diff --stat + fi + + - name: Verify build + if: steps.changes.outputs.has-changes == 'true' + run: | + if grep -q '^build:' Makefile 2>/dev/null; then + make build + else + go build ./... + fi + + - name: Show changes (dry run) + if: inputs.dry-run && steps.changes.outputs.has-changes == 'true' + run: | + echo "=== Dry run — would create PR with these changes ===" + git diff --stat + echo "---" + git diff go.mod + echo "---" + git diff .ci-operator.yaml 2>/dev/null || true + + - name: Create Pull Request + if: "!inputs.dry-run && steps.changes.outputs.has-changes == 'true'" + uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7.0.11 + with: + token: ${{ github.token }} + commit-message: "chore: update Go to ${{ steps.config.outputs.go }} and dependencies (${{ inputs.version-schema }})" + title: "chore: update Go to ${{ steps.config.outputs.go }} and dependencies (${{ inputs.version-schema }})" + branch: automated/go-version-update-${{ inputs.version-schema }} + delete-branch: true + body: | + Automated Go version and dependency update based on schema `${{ inputs.version-schema }}`. + + **Changes:** + - Go version: `${{ steps.config.outputs.go }}` + - CI operator image: `${{ steps.config.outputs.ci-image }}` + - Toolchain directive removed (patch versions handled by builder image) + - Dependencies updated via `go mod tidy` + `go mod vendor` + + This workflow runs weekly to pick up dependency security patches (CVE fixes) + even when the Go minor version hasn't changed. + + **Version schema:** [`go-versions/${{ inputs.version-schema }}.yaml`](https://github.com/medik8s/.github/blob/main/go-versions/${{ inputs.version-schema }}.yaml) diff --git a/go-versions/README.md b/go-versions/README.md new file mode 100644 index 0000000..ee999b3 --- /dev/null +++ b/go-versions/README.md @@ -0,0 +1,47 @@ +# Go Version Schemas + +Each YAML file defines a Go version schema tied to an OCP release. Operator +repos reference a schema by name when calling the +[`update-go-version`](../.github/workflows/update-go-version.yaml) reusable +workflow. + +## Design principles + +- **`go.mod` tracks the minor version only** (e.g. `go 1.25`, not `go 1.25.0`). +- **No `toolchain` directive** — patch-level updates (`1.25.x`) are handled by + updating the CI builder image, not `go.mod`. +- **Minor version bumps** (e.g. `1.25` → `1.26`) require an explicit `go.mod` + update, which is what this workflow automates. + +## Schema format + +```yaml +# go-versions/ocp-4.21.yaml +go: "1.25" # Go minor version +ci-operator-image: "rhel-9-release-golang-1.25-openshift-4.21" # CI image tag +``` + +## Adding a new schema + +When a new OCP version requires a Go bump: + +1. Create `go-versions/ocp-X.Y.yaml` with the new values +2. Each operator repo updates its caller workflow to reference the new schema +3. On the next scheduled run (or manual dispatch), the workflow creates a PR + +## Usage in operator repos + +```yaml +# .github/workflows/go-update.yaml +name: Go Version Update +on: + schedule: + - cron: '0 8 * * 1' + workflow_dispatch: + +jobs: + update: + uses: medik8s/.github/.github/workflows/update-go-version.yaml@main + with: + version-schema: ocp-4.21 +``` diff --git a/go-versions/ocp-4.19.yaml b/go-versions/ocp-4.19.yaml new file mode 100644 index 0000000..a921c09 --- /dev/null +++ b/go-versions/ocp-4.19.yaml @@ -0,0 +1,4 @@ +# Go version schema for OCP 4.19 +# Used by: older release branches (e.g. FAR release-0.5, SNR release-0.10) +go: "1.23" +ci-operator-image: "rhel-9-release-golang-1.23-openshift-4.19" diff --git a/go-versions/ocp-4.20.yaml b/go-versions/ocp-4.20.yaml new file mode 100644 index 0000000..bf23a81 --- /dev/null +++ b/go-versions/ocp-4.20.yaml @@ -0,0 +1,4 @@ +# Go version schema for OCP 4.20 +# Used by: older release branches (e.g. FAR release-0.6, release-0.7) +go: "1.24" +ci-operator-image: "rhel-9-release-golang-1.24-openshift-4.20" diff --git a/go-versions/ocp-4.21.yaml b/go-versions/ocp-4.21.yaml new file mode 100644 index 0000000..e53246b --- /dev/null +++ b/go-versions/ocp-4.21.yaml @@ -0,0 +1,4 @@ +# Go version schema for OCP 4.21 (RHWA 25.9) +# Used by: release branches targeting OCP 4.21 +go: "1.25" +ci-operator-image: "rhel-9-release-golang-1.25-openshift-4.21" diff --git a/go-versions/ocp-4.22.yaml b/go-versions/ocp-4.22.yaml new file mode 100644 index 0000000..b3d44e3 --- /dev/null +++ b/go-versions/ocp-4.22.yaml @@ -0,0 +1,4 @@ +# Go version schema for OCP 4.22 / OCP 5.0 +# Used by: main branches targeting OCP 4.22+ +go: "1.26" +ci-operator-image: "rhel-9-release-golang-1.26-openshift-5.0"