diff --git a/.github/workflows/create-production-pr.yaml b/.github/workflows/create-production-pr.yaml new file mode 100644 index 00000000..3791dbfb --- /dev/null +++ b/.github/workflows/create-production-pr.yaml @@ -0,0 +1,188 @@ +name: Create Production FBC PR + +on: + workflow_dispatch: + inputs: + release_stream: + description: 'Release stream' + required: true + type: choice + options: + - '1.2' + - '1.3' + +permissions: + contents: write + pull-requests: write + +# OCP version defaults by release (update when new OCP versions are supported) +env: + OPERATOR_NAME: rhtas-operator + OCP_VERSIONS_1_2: "v4.14,v4.15,v4.16,v4.17,v4.18" + OCP_VERSIONS_1_3: "v4.16,v4.17,v4.18,v4.19,v4.20" + +jobs: + create-production-pr: + runs-on: ubuntu-latest + steps: + - name: Checkout FBC main branch + uses: actions/checkout@v4 + with: + ref: main + path: fbc-main + + - name: Checkout FBC production branch + uses: actions/checkout@v4 + with: + ref: production + path: fbc-production + + - name: Install opm + uses: redhat-actions/openshift-tools-installer@v1 + with: + source: "github" + github_pat: ${{ github.token }} + opm: "1.61.0" + + - name: Log in to registry.redhat.io + uses: redhat-actions/podman-login@v1 + with: + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_PASSWORD }} + registry: registry.redhat.io + auth_file_path: /tmp/config.json + + - name: Derive version, channels, and OCP versions + id: derive + run: | + OPERATOR="${{ env.OPERATOR_NAME }}" + RELEASE_STREAM="${{ inputs.release_stream }}" + + echo "Operator: $OPERATOR" + echo "Release stream: $RELEASE_STREAM" + + VAR_NAME="OCP_VERSIONS_${RELEASE_STREAM//./_}" + OCP_VERSIONS="${!VAR_NAME}" + if [[ -z "$OCP_VERSIONS" ]]; then + echo "Unknown release stream: $RELEASE_STREAM" + echo "Please add ${VAR_NAME} to env section" + exit 1 + fi + echo "ocp_versions=$OCP_VERSIONS" >> $GITHUB_OUTPUT + echo "Derived OCP versions: $OCP_VERSIONS" + + FIRST_OCP=$(echo "$OCP_VERSIONS" | cut -d',' -f1) + GRAPH="fbc-main/${FIRST_OCP}/${OPERATOR}/graph.yaml" + echo "Using graph: $GRAPH" + + # Find latest version for this release stream (e.g., 1.2 -> 1.2.2) + LATEST_BUNDLE=$(yq e ".entries[] | select(.schema == \"olm.bundle\") | .name" "$GRAPH" \ + | grep "^${OPERATOR}\.v${RELEASE_STREAM}\." | sort -V | tail -1) + + if [[ -z "$LATEST_BUNDLE" ]]; then + echo "Error: No bundle found for ${OPERATOR} v${RELEASE_STREAM}.x in main branch" + exit 1 + fi + + BUNDLE_VERSION=$(echo "$LATEST_BUNDLE" | sed "s/^${OPERATOR}\.v//") + echo "Derived bundle version: $BUNDLE_VERSION" + echo "bundle_version=$BUNDLE_VERSION" >> $GITHUB_OUTPUT + echo "bundle_name=$LATEST_BUNDLE" >> $GITHUB_OUTPUT + + export LATEST_BUNDLE + BUNDLE_IMAGE=$(yq e ".entries[] | select(.schema == \"olm.bundle\" and .name == env(LATEST_BUNDLE)) | .image" "$GRAPH") + echo "Derived bundle image: $BUNDLE_IMAGE" + echo "bundle_image=$BUNDLE_IMAGE" >> $GITHUB_OUTPUT + + CHANNELS="stable-v${RELEASE_STREAM},stable" + echo "channels=$CHANNELS" >> $GITHUB_OUTPUT + echo "Derived channels: $CHANNELS" + + - name: Filter and prepare production content + run: | + cd fbc-main + + # Set auth file for skopeo + export AUTH_FILE="--authfile /tmp/config.json" + # Cache skopeo results across OCP versions + export SKOPEO_CACHE_FILE="/tmp/skopeo-cache" + + IFS=',' read -ra versions <<< "${{ steps.derive.outputs.ocp_versions }}" + for ocp_version in "${versions[@]}"; do + echo "==========================================" + echo "Processing OCP version: $ocp_version" + echo "==========================================" + + GRAPH="${ocp_version}/${{ env.OPERATOR_NAME }}/graph.yaml" + + if [[ ! -f "$GRAPH" ]]; then + echo "Warning: Graph file not found: $GRAPH, skipping..." + continue + fi + + # Filter main to keep only bundles that exist in registry.redhat.io + ./utils/filter_for_production.sh "$GRAPH" + + # Add the new bundle with derived channels + export BUNDLE_NAME="${{ steps.derive.outputs.bundle_name }}" + export BUNDLE_IMAGE="${{ steps.derive.outputs.bundle_image }}" + export CHANNELS="${{ steps.derive.outputs.channels }}" + export GRAPH + ./utils/configure_channels.sh + ./utils/configure_bundles.sh + + # Render catalog + export OCP_VERSION=$ocp_version + export FBC_DIR=${{ env.OPERATOR_NAME }} + export CATALOG_FILE="${ocp_version}/${{ env.OPERATOR_NAME }}/catalog/${{ env.OPERATOR_NAME }}/catalog.json" + ./utils/render_catalog.sh + done + + - name: Copy to production directory + run: | + IFS=',' read -ra versions <<< "${{ steps.derive.outputs.ocp_versions }}" + for ocp_version in "${versions[@]}"; do + if [[ -d "fbc-main/${ocp_version}/${{ env.OPERATOR_NAME }}" ]]; then + rm -rf "fbc-production/${ocp_version}/${{ env.OPERATOR_NAME }}" + mkdir -p "fbc-production/${ocp_version}" + cp -r "fbc-main/${ocp_version}/${{ env.OPERATOR_NAME }}" \ + "fbc-production/${ocp_version}/" + fi + done + + - name: Create PR + working-directory: fbc-production + run: | + git config user.name "RHTAS-build-bot" + git config user.email "RHTAS-build-bot@users.noreply.github.com" + + BRANCH="release-${{ env.OPERATOR_NAME }}-v${{ steps.derive.outputs.bundle_version }}" + git checkout -b "$BRANCH" + git add . + + # Check if there are changes to commit + if git diff --cached --quiet; then + echo "No changes to commit" + exit 0 + fi + + git commit -m ":package: Release ${{ env.OPERATOR_NAME }} v${{ steps.derive.outputs.bundle_version }}" + git push -u origin "$BRANCH" + + PR_BODY="## Automated Production Release PR + + ### Release Details + - **Operator**: ${{ env.OPERATOR_NAME }} + - **Release Stream**: ${{ inputs.release_stream }} + - **Version**: ${{ steps.derive.outputs.bundle_version }} + - **Bundle Image**: \`${{ steps.derive.outputs.bundle_image }}\` + - **OCP Versions**: ${{ steps.derive.outputs.ocp_versions }} + - **Channels**: ${{ steps.derive.outputs.channels }}" + + gh pr create \ + --base production \ + --head "$BRANCH" \ + --title ":package: Release ${{ env.OPERATOR_NAME }} v${{ steps.derive.outputs.bundle_version }}" \ + --body "$PR_BODY" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.tekton/fbc-v4-14-production-push.yaml b/.tekton/fbc-v4-14-production-push.yaml new file mode 100644 index 00000000..7069a660 --- /dev/null +++ b/.tekton/fbc-v4-14-production-push.yaml @@ -0,0 +1,46 @@ +apiVersion: tekton.dev/v1 +kind: PipelineRun +metadata: + annotations: + build.appstudio.openshift.io/repo: https://github.com/securesign/fbc?rev={{revision}} + build.appstudio.redhat.com/commit_sha: '{{revision}}' + build.appstudio.redhat.com/target_branch: '{{target_branch}}' + pipelinesascode.tekton.dev/max-keep-runs: "3" + pipelinesascode.tekton.dev/on-cel-expression: event == "push" && target_branch + == "production" && ( "v4.14/rhtas-operator/***".pathChanged() || ".tekton/fbc-v4-14-production-push.yaml".pathChanged() + || "trigger-konflux-builds.txt".pathChanged() ) + creationTimestamp: null + labels: + appstudio.openshift.io/application: fbc-v4-14 + appstudio.openshift.io/component: fbc-v4-14-production + pipelines.appstudio.openshift.io/type: build + name: fbc-v4-14-production-on-push + namespace: rhtas-tenant +spec: + params: + - name: dockerfile + value: catalog.Dockerfile + - name: git-url + value: '{{source_url}}' + - name: output-image + value: quay.io/securesign/fbc-v4-14-production:{{revision}} + - name: path-context + value: v4.14/rhtas-operator + - name: revision + value: '{{revision}}' + pipelineRef: + params: + - name: url + value: https://github.com/securesign/pipelines.git + - name: revision + value: main + - name: pathInRepo + value: pipelines/fbc-builder.yaml + resolver: git + taskRunTemplate: + serviceAccountName: build-pipeline-fbc-v4-14 + workspaces: + - name: git-auth + secret: + secretName: '{{ git_auth_secret }}' +status: {} diff --git a/.tekton/fbc-v4-15-production-push.yaml b/.tekton/fbc-v4-15-production-push.yaml new file mode 100644 index 00000000..08dc5adc --- /dev/null +++ b/.tekton/fbc-v4-15-production-push.yaml @@ -0,0 +1,46 @@ +apiVersion: tekton.dev/v1 +kind: PipelineRun +metadata: + annotations: + build.appstudio.openshift.io/repo: https://github.com/securesign/fbc?rev={{revision}} + build.appstudio.redhat.com/commit_sha: '{{revision}}' + build.appstudio.redhat.com/target_branch: '{{target_branch}}' + pipelinesascode.tekton.dev/max-keep-runs: "3" + pipelinesascode.tekton.dev/on-cel-expression: event == "push" && target_branch + == "production" && ( "v4.15/rhtas-operator/***".pathChanged() || ".tekton/fbc-v4-15-production-push.yaml".pathChanged() + || "trigger-konflux-builds.txt".pathChanged() ) + creationTimestamp: null + labels: + appstudio.openshift.io/application: fbc-v4-15 + appstudio.openshift.io/component: fbc-v4-15-production + pipelines.appstudio.openshift.io/type: build + name: fbc-v4-15-production-on-push + namespace: rhtas-tenant +spec: + params: + - name: dockerfile + value: catalog.Dockerfile + - name: git-url + value: '{{source_url}}' + - name: output-image + value: quay.io/securesign/fbc-v4-15-production:{{revision}} + - name: path-context + value: v4.15/rhtas-operator + - name: revision + value: '{{revision}}' + pipelineRef: + params: + - name: url + value: https://github.com/securesign/pipelines.git + - name: revision + value: main + - name: pathInRepo + value: pipelines/fbc-builder.yaml + resolver: git + taskRunTemplate: + serviceAccountName: build-pipeline-fbc-v4-15 + workspaces: + - name: git-auth + secret: + secretName: '{{ git_auth_secret }}' +status: {} diff --git a/.tekton/fbc-v4-16-production-push.yaml b/.tekton/fbc-v4-16-production-push.yaml new file mode 100644 index 00000000..9fb6a5f3 --- /dev/null +++ b/.tekton/fbc-v4-16-production-push.yaml @@ -0,0 +1,48 @@ +apiVersion: tekton.dev/v1 +kind: PipelineRun +metadata: + annotations: + build.appstudio.openshift.io/repo: https://github.com/securesign/fbc?rev={{revision}} + build.appstudio.redhat.com/commit_sha: '{{revision}}' + build.appstudio.redhat.com/target_branch: '{{target_branch}}' + pipelinesascode.tekton.dev/max-keep-runs: "3" + pipelinesascode.tekton.dev/on-cel-expression: event == "push" && target_branch + == "production" && ( "v4.16/rhtas-operator/***".pathChanged() || ".tekton/fbc-v4-16-production-push.yaml".pathChanged() + || "trigger-konflux-builds.txt".pathChanged() ) + creationTimestamp: null + labels: + appstudio.openshift.io/application: fbc-v4-16 + appstudio.openshift.io/component: fbc-v4-16-production + pipelines.appstudio.openshift.io/type: build + name: fbc-v4-16-production-on-push + namespace: rhtas-tenant +spec: + params: + - name: git-url + value: '{{source_url}}' + - name: revision + value: '{{revision}}' + - name: output-image + value: quay.io/securesign/fbc-v4-16-production:{{revision}} + - name: dockerfile + value: catalog.Dockerfile + - name: path-context + value: v4.16/rhtas-operator + - name: ocp-release-version + value: "v4.16" + pipelineRef: + params: + - name: url + value: https://github.com/securesign/pipelines.git + - name: revision + value: main + - name: pathInRepo + value: pipelines/fbc-builder.yaml + resolver: git + taskRunTemplate: + serviceAccountName: build-pipeline-fbc-v4-16 + workspaces: + - name: git-auth + secret: + secretName: '{{ git_auth_secret }}' +status: {} diff --git a/.tekton/fbc-v4-17-production-push.yaml b/.tekton/fbc-v4-17-production-push.yaml new file mode 100644 index 00000000..fe891dfe --- /dev/null +++ b/.tekton/fbc-v4-17-production-push.yaml @@ -0,0 +1,48 @@ +apiVersion: tekton.dev/v1 +kind: PipelineRun +metadata: + annotations: + build.appstudio.openshift.io/repo: https://github.com/securesign/fbc?rev={{revision}} + build.appstudio.redhat.com/commit_sha: '{{revision}}' + build.appstudio.redhat.com/target_branch: '{{target_branch}}' + pipelinesascode.tekton.dev/max-keep-runs: "3" + pipelinesascode.tekton.dev/on-cel-expression: event == "push" && target_branch + == "production" && ( "v4.17/rhtas-operator/***".pathChanged() || ".tekton/fbc-v4-17-production-push.yaml".pathChanged() + || "trigger-konflux-builds.txt".pathChanged() ) + creationTimestamp: null + labels: + appstudio.openshift.io/application: fbc-v4-17 + appstudio.openshift.io/component: fbc-v4-17-production + pipelines.appstudio.openshift.io/type: build + name: fbc-v4-17-production-on-push + namespace: rhtas-tenant +spec: + params: + - name: git-url + value: '{{source_url}}' + - name: revision + value: '{{revision}}' + - name: output-image + value: quay.io/securesign/fbc-v4-17-production:{{revision}} + - name: dockerfile + value: catalog.Dockerfile + - name: path-context + value: v4.17/rhtas-operator + - name: ocp-release-version + value: "v4.17" + pipelineRef: + params: + - name: url + value: https://github.com/securesign/pipelines.git + - name: revision + value: main + - name: pathInRepo + value: pipelines/fbc-builder.yaml + resolver: git + taskRunTemplate: + serviceAccountName: build-pipeline-fbc-v4-17 + workspaces: + - name: git-auth + secret: + secretName: '{{ git_auth_secret }}' +status: {} diff --git a/.tekton/fbc-v4-18-production-push.yaml b/.tekton/fbc-v4-18-production-push.yaml new file mode 100644 index 00000000..9a08adc0 --- /dev/null +++ b/.tekton/fbc-v4-18-production-push.yaml @@ -0,0 +1,49 @@ +apiVersion: tekton.dev/v1 +kind: PipelineRun +metadata: + annotations: + build.appstudio.openshift.io/repo: https://github.com/securesign/fbc?rev={{revision}} + build.appstudio.redhat.com/commit_sha: '{{revision}}' + build.appstudio.redhat.com/target_branch: '{{target_branch}}' + pipelinesascode.tekton.dev/cancel-in-progress: "false" + pipelinesascode.tekton.dev/max-keep-runs: "3" + pipelinesascode.tekton.dev/on-cel-expression: event == "push" && target_branch + == "production" && ( "v4.18/rhtas-operator/***".pathChanged() || ".tekton/fbc-v4-18-production-push.yaml".pathChanged() + || "trigger-konflux-builds.txt".pathChanged() ) + creationTimestamp: null + labels: + appstudio.openshift.io/application: fbc-v4-18 + appstudio.openshift.io/component: fbc-v4-18-production + pipelines.appstudio.openshift.io/type: build + name: fbc-v4-18-production-on-push + namespace: rhtas-tenant +spec: + params: + - name: git-url + value: '{{source_url}}' + - name: revision + value: '{{revision}}' + - name: output-image + value: quay.io/securesign/fbc-v4-18-production:{{revision}} + - name: dockerfile + value: catalog.Dockerfile + - name: path-context + value: v4.18/rhtas-operator + - name: ocp-release-version + value: "v4.18" + pipelineRef: + params: + - name: url + value: https://github.com/securesign/pipelines.git + - name: revision + value: main + - name: pathInRepo + value: pipelines/fbc-builder.yaml + resolver: git + taskRunTemplate: + serviceAccountName: build-pipeline-fbc-v4-18 + workspaces: + - name: git-auth + secret: + secretName: '{{ git_auth_secret }}' +status: {} diff --git a/.tekton/fbc-v4-19-production-push.yaml b/.tekton/fbc-v4-19-production-push.yaml new file mode 100644 index 00000000..6b187a9d --- /dev/null +++ b/.tekton/fbc-v4-19-production-push.yaml @@ -0,0 +1,52 @@ +apiVersion: tekton.dev/v1 +kind: PipelineRun +metadata: + annotations: + build.appstudio.openshift.io/repo: https://github.com/securesign/fbc?rev={{revision}} + build.appstudio.redhat.com/commit_sha: '{{revision}}' + build.appstudio.redhat.com/target_branch: '{{target_branch}}' + pipelinesascode.tekton.dev/cancel-in-progress: "false" + pipelinesascode.tekton.dev/max-keep-runs: "3" + pipelinesascode.tekton.dev/on-cel-expression: event == "push" && target_branch + == "production" && ( "v4.19/rhtas-operator/***".pathChanged() || ".tekton/fbc-v4-19-production-push.yaml".pathChanged() + ) + creationTimestamp: null + labels: + appstudio.openshift.io/application: fbc-v4-19 + appstudio.openshift.io/component: fbc-v4-19-production + pipelines.appstudio.openshift.io/type: build + name: fbc-v4-19-production-on-push + namespace: rhtas-tenant +spec: + params: + - name: git-url + value: '{{source_url}}' + - name: revision + value: '{{revision}}' + - name: output-image + value: quay.io/securesign/fbc-v4-19-production:{{revision}} + - name: build-platforms + value: + - linux/x86_64 + - name: dockerfile + value: catalog.Dockerfile + - name: path-context + value: v4.19/rhtas-operator + - name: ocp-release-version + value: "v4.19" + pipelineRef: + params: + - name: url + value: https://github.com/securesign/pipelines.git + - name: revision + value: main + - name: pathInRepo + value: pipelines/fbc-builder.yaml + resolver: git + taskRunTemplate: + serviceAccountName: build-pipeline-fbc-v4-19 + workspaces: + - name: git-auth + secret: + secretName: '{{ git_auth_secret }}' +status: {} diff --git a/.tekton/fbc-v4-20-production-push.yaml b/.tekton/fbc-v4-20-production-push.yaml new file mode 100644 index 00000000..181a7638 --- /dev/null +++ b/.tekton/fbc-v4-20-production-push.yaml @@ -0,0 +1,52 @@ +apiVersion: tekton.dev/v1 +kind: PipelineRun +metadata: + annotations: + build.appstudio.openshift.io/repo: https://github.com/securesign/fbc?rev={{revision}} + build.appstudio.redhat.com/commit_sha: '{{revision}}' + build.appstudio.redhat.com/target_branch: '{{target_branch}}' + pipelinesascode.tekton.dev/cancel-in-progress: "false" + pipelinesascode.tekton.dev/max-keep-runs: "3" + pipelinesascode.tekton.dev/on-cel-expression: event == "push" && target_branch + == "production" && ( "v4.20/rhtas-operator/***".pathChanged() || ".tekton/fbc-v4-20-production-push.yaml".pathChanged() + ) + creationTimestamp: null + labels: + appstudio.openshift.io/application: fbc-v4-20 + appstudio.openshift.io/component: fbc-v4-20-production + pipelines.appstudio.openshift.io/type: build + name: fbc-v4-20-production-on-push + namespace: rhtas-tenant +spec: + params: + - name: git-url + value: '{{source_url}}' + - name: revision + value: '{{revision}}' + - name: output-image + value: quay.io/securesign/fbc-v4-20-production:{{revision}} + - name: build-platforms + value: + - linux/x86_64 + - name: dockerfile + value: catalog.Dockerfile + - name: path-context + value: v4.20/rhtas-operator + - name: ocp-release-version + value: "v4.20" + pipelineRef: + params: + - name: url + value: https://github.com/securesign/pipelines.git + - name: revision + value: main + - name: pathInRepo + value: pipelines/fbc-builder.yaml + resolver: git + taskRunTemplate: + serviceAccountName: build-pipeline-fbc-v4-20 + workspaces: + - name: git-auth + secret: + secretName: '{{ git_auth_secret }}' +status: {} diff --git a/utils/filter_for_production.sh b/utils/filter_for_production.sh new file mode 100755 index 00000000..0f47659f --- /dev/null +++ b/utils/filter_for_production.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Filter graph.yaml to keep only bundles that exist in registry.redhat.io +# Usage: filter_for_production.sh +# +# Environment: +# SKOPEO_CMD - skopeo command (default: skopeo) +# AUTH_FILE - auth file flag for skopeo (e.g., "--authfile /path/to/config.json") +# SKOPEO_CACHE_FILE - cache file for skopeo results (optional) + +GRAPH="${1:-}" + +if [[ -z "$GRAPH" ]]; then + echo "Usage: filter_for_production.sh " + exit 1 +elif [[ ! -f "$GRAPH" ]]; then + echo "Error: Graph file not found: $GRAPH" + exit 1 +fi + +SKOPEO_CMD=${SKOPEO_CMD:-skopeo} +AUTH_FILE=${AUTH_FILE:-} +CACHE_FILE="${SKOPEO_CACHE_FILE:-${TMPDIR:-/tmp}/skopeo-cache.$$}" + +check_image_exists() { + local image="$1" + local cache_key="${image##*@}" + + # Check cache first + if grep -q "^${cache_key}=exists$" "$CACHE_FILE" 2>/dev/null; then + return 0 + elif grep -q "^${cache_key}=notfound$" "$CACHE_FILE" 2>/dev/null; then + return 1 + fi + + # Not cached, check registry + if ${SKOPEO_CMD} inspect --no-tags ${AUTH_FILE} "docker://${image}" &>/dev/null; then + echo "${cache_key}=exists" >> "$CACHE_FILE" + return 0 + else + echo "${cache_key}=notfound" >> "$CACHE_FILE" + return 1 + fi +} + +echo "Filtering graph for production: $GRAPH" +echo "Using cache: $CACHE_FILE" + +echo "Checking bundle image availability..." +BUNDLES=$(yq e '.entries[] | select(.schema == "olm.bundle") | .name + "|" + .image' "$GRAPH") + +while IFS='|' read -r BUNDLE IMAGE; do + [[ -z "$BUNDLE" || -z "$IMAGE" ]] && continue + + echo -n "Checking $BUNDLE... " + + if check_image_exists "$IMAGE"; then + echo "EXISTS" + else + echo "NOT FOUND - removing" + export BUNDLE + + # Remove bundle entry + yq -i 'del(.entries[] | select(.schema == "olm.bundle" and .name == env(BUNDLE)))' "$GRAPH" + # Remove from channel entries + yq -i '(.entries[] | select(.schema == "olm.channel").entries) |= map(select(.name != env(BUNDLE)))' "$GRAPH" + # Remove from skips arrays + yq -i '(.entries[] | select(.schema == "olm.channel").entries[].skips) |= map(select(. != env(BUNDLE)))' "$GRAPH" + # Remove from replaces references + yq -i 'del(.entries[] | select(.schema == "olm.channel").entries[] | select(.replaces == env(BUNDLE)).replaces)' "$GRAPH" + fi +done <<< "$BUNDLES" + +echo "Removing empty channels..." +yq -i 'del(.entries[] | select(.schema == "olm.channel" and (.entries | length) == 0))' "$GRAPH" + +echo "" +echo "Filtering complete: $GRAPH"