Repository: cri-o/packaging
Pull request #241: Add support for publishing CRI-O as OCI artifacts
Author: login: saschagrunert
Labels: name: kind/ci
name: approved
description: Indicates a PR has been approved by an approver from all required OWNERS files.
name: dco-signoff: yes
description: Indicates the PR's author has DCO signed all their commits.
name: lgtm
description: Indicates that a PR is ready to be merged.
name: release-note-none
Created: 2025-04-23T09:16:28Z | Merged: 2025-04-23T14:55:39Z


#### What type of PR is this?


/kind ci


#### What this PR does / why we need it:
We now use the GitHub registry `github.com/cri-o/bundle` to publish the created bundles as OCI artifacts.

#### Which issue(s) this PR fixes:

None

#### Special notes for your reviewer:

Gave it a test run within this repository in https://github.com/cri-o/packaging/actions/runs/14614207404
The artifacts are available in: https://github.com/cri-o/packaging/pkgs/container/bundle

Example for `main`: https://explore.ggcr.dev/?image=ghcr.io/cri-o/bundle:main
(also referenced by the tag `v1.33.0-dev`, `23bec10a74342c4913d5e5abedc80dff98d673da` and `23bec10`)

```bash
# check the signature of the manifest list
cosign verify \
  --certificate-identity https://github.com/cri-o/packaging/.github/workflows/obs.yml@refs/heads/oci-artifacts \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  ghcr.io/cri-o/bundle:main
…

# pull the content, the latest oras beta will allow to omit `--platform linux/amd64`
oras pull --platform linux/amd64 ghcr.io/cri-o/bundle:main
…

# check the attachments
oras discover --platform linux/amd64 ghcr.io/cri-o/bundle:main
ghcr.io/cri-o/bundle@sha256:b05b8731de428fc161031a608184d3aa178de40aae51e8a5cf7a0e27eed3f61f
└── application/vnd.cncf.spdx.file.v1
    └── sha256:7c056bfb9e1c867953f726ac9fb3fc010ccae7157cb74101b975f8c6ada07d88

# validate the signature of the SBOM attachment
cosign verify \
  --certificate-identity https://github.com/cri-o/packaging/.github/workflows/obs.yml@refs/heads/oci-artifacts \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  $(oras discover --platform linux/amd64 --format json ghcr.io/cri-o/bundle:main | jq -r '.manifests[0].reference')
…

# pull the attached SBOM
oras pull $(oras discover --platform linux/amd64 --format json ghcr.io/cri-o/bundle:main | jq -r '.manifests[0].reference')
…

# validate the SBOM
bom validate -e sbom.spdx -d cri-o
…
```

#### Does this PR introduce a user-facing change?

<!--
No releases for this repository.
-->

```release-note
None
```



Diff:
diff --git a/.github/workflows/obs.yml b/.github/workflows/obs.yml
index ef6faa6..f752233 100644
--- a/.github/workflows/obs.yml
+++ b/.github/workflows/obs.yml
@@ -179,6 +179,61 @@ jobs:
             build/bundle/*.sig
             build/bundle/*.cert
 
+  oci-artifacts-publish:
+    name: oci-artifacts / publish / ${{ inputs.revision || 'main' }}
+    runs-on: ubuntu-latest
+    needs:
+      - vars
+      - bundle-test
+    permissions:
+      contents: read
+      id-token: write
+      packages: write
+    steps:
+      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+      - uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
+        if: ${{ inputs.skip-bundles == false && github.event_name != 'pull_request' }}
+        with:
+          registry: ghcr.io/cri-o
+          username: cri-o
+          password: ${{ secrets.GITHUB_TOKEN }}
+      - uses: sigstore/cosign-installer@3454372f43399081ed03b604cb2d021dabca52bb # v3.8.2
+        if: ${{ inputs.skip-bundles == false && github.event_name != 'pull_request' }}
+      - uses: oras-project/setup-oras@5c0b487ce3fe0ce3ab0d034e63669e426e294e4d # v1.2.2
+        if: ${{ inputs.skip-bundles == false && github.event_name != 'pull_request' }}
+        with:
+          url: https://github.com/oras-project/oras/releases/download/v1.3.0-beta.2/oras_1.3.0-beta.2_linux_amd64.tar.gz
+          checksum: 3f4258fc0e8a97a2ad07ba01f2d132b3702b48d9d0ab58c5b3321c904a8a1c03
+      - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
+        if: ${{ inputs.skip-bundles == false && github.event_name != 'pull_request' }}
+        with:
+          name: bundles-amd64
+          path: build/bundle
+      - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
+        if: ${{ inputs.skip-bundles == false && github.event_name != 'pull_request' }}
+        with:
+          name: bundles-arm64
+          path: build/bundle
+      - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
+        if: ${{ inputs.skip-bundles == false && github.event_name != 'pull_request' }}
+        with:
+          name: bundles-ppc64le
+          path: build/bundle
+      - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
+        if: ${{ inputs.skip-bundles == false && github.event_name != 'pull_request' }}
+        with:
+          name: bundles-s390x
+          path: build/bundle
+      - run: scripts/oci-artifacts
+        if: ${{ inputs.skip-bundles == false && github.event_name != 'pull_request' }}
+        env:
+          ARCHIVE_PATH: ${{ needs.vars.outputs.archive_path }}
+          COMMIT: ${{ needs.vars.outputs.commit }}
+          GIT_ROOT: ${{ needs.vars.outputs.git_root }}
+          PROJECT_TYPE: ${{ needs.vars.outputs.project_type }}
+          PROJECT_VERSION: ${{ needs.vars.outputs.project_version }}
+          VERSION: ${{ needs.vars.outputs.version }}
+
   stage:
     runs-on: ubuntu-latest
     name: stage / ${{ inputs.revision || 'main' }}
@@ -186,6 +241,7 @@ jobs:
     needs:
       - vars
       - bundles-publish
+      - oci-artifacts-publish
     steps:
       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
       - run: scripts/obs
diff --git a/dependencies.yaml b/dependencies.yaml
index 4e78fff..599053d 100644
--- a/dependencies.yaml
+++ b/dependencies.yaml
@@ -105,11 +105,17 @@ dependencies:
   - name: bom
     version: v0.6.0
     refPaths:
-      - path: scripts/bundle/build
+      - path: scripts/helpers
         match: BOM_VERSION
       - path: .github/workflows/test.yml
         match: BOM_VERSION
 
+  - name: oras
+    version: v1.3.0-beta.2
+    refPaths:
+      - path: .github/workflows/obs.yml
+        match: oras/releases
+
   - name: debian base
     version: bookworm-v1.0.4
     refPaths:
diff --git a/scripts/bundle/build b/scripts/bundle/build
index c214fb9..7d102ec 100755
--- a/scripts/bundle/build
+++ b/scripts/bundle/build
@@ -148,18 +148,12 @@ for FILE in "$TMP_BIN"/*; do
     fi
 done
 
-# Install BOM
-BOM_VERSION=v0.6.0
-BOM="$GIT_ROOT/build/bom"
-curl_to "$BOM" \
-    "https://github.com/kubernetes-sigs/bom/releases/download/$BOM_VERSION/bom-amd64-linux"
-chmod +x "$BOM"
+install_bom
 
 # Create the SBOM
 pushd "$ARCHIVE_PATH"
 SPDX_FILE="$ARCHIVE.spdx"
-$BOM version
-$BOM generate \
+bom generate \
     -l Apache-2.0 \
     --name CRI-O \
     --namespace "https://storage.googleapis.com/cri-o/artifacts/$SPDX_FILE" \
@@ -179,8 +173,8 @@ rm -rf "$TMPDIR"
 echo "Testing archive"
 tar xf "$ARCHIVE"
 SPDX_PATH="$ARCHIVE_PATH/$SPDX_FILE"
-$BOM document outline "$SPDX_PATH"
-$BOM validate -e "$SPDX_PATH" -d "$CRIODIR"
+bom document outline "$SPDX_PATH"
+bom validate -e "$SPDX_PATH" -d "$CRIODIR"
 pushd "$TMPDIR"
 export DESTDIR=test/
 ./install
diff --git a/scripts/helpers b/scripts/helpers
index 3108a7e..717d52a 100755
--- a/scripts/helpers
+++ b/scripts/helpers
@@ -29,10 +29,18 @@ install_osc() {
 
 install_krel() {
     KREL_VERSION=v0.18.0
-    BINARY=krel
-    curl_retry https://github.com/kubernetes/release/releases/download/$KREL_VERSION/$BINARY-amd64-linux -o $BINARY
-    chmod +x $BINARY
-    sudo cp $BINARY /usr/local/bin
-    rm $BINARY
-    $BINARY version
+    install_binary https://github.com/kubernetes/release/releases/download/$KREL_VERSION/krel-amd64-linux krel
+}
+
+install_bom() {
+    BOM_VERSION=v0.6.0
+    install_binary https://github.com/kubernetes-sigs/bom/releases/download/$BOM_VERSION/bom-amd64-linux bom
+}
+
+install_binary() {
+    curl_retry "$1" -o "$2"
+    chmod +x "$2"
+    sudo cp "$2" /usr/local/bin
+    rm "$2"
+    "$2" version
 }
diff --git a/scripts/oci-artifacts b/scripts/oci-artifacts
new file mode 100755
index 0000000..4bc8d41
--- /dev/null
+++ b/scripts/oci-artifacts
@@ -0,0 +1,93 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+source "$(dirname "${BASH_SOURCE[0]}")"/vars
+
+REGISTRY=ghcr.io/cri-o
+ARTIFACT=$REGISTRY/bundle
+
+ARCHES=(amd64 arm64 ppc64le s390x)
+BUNDLE_ARTIFACT_TYPE=application/vnd.cncf.cri-o.bundle.v1
+SBOM_ARTIFACT_TYPE=application/vnd.cncf.spdx.file.v1
+SBOM_FILE=sbom.spdx
+
+install_bom
+
+pushd "$ARCHIVE_PATH"
+
+VERSION=v$VERSION
+ANNOTATION_VERSION="org.cncf.cri-o.version=$VERSION"
+ANNOTATION_COMMIT="org.cncf.cri-o.commit=$COMMIT"
+ANNOTATION_BRANCH="org.cncf.cri-o.branch=$PROJECT_VERSION"
+
+ARCHIVE_ID=$COMMIT
+TAG=${COMMIT:0:7}
+if [[ $PROJECT_TYPE == stable ]]; then
+    ARCHIVE_ID="$VERSION"
+    TAG="$VERSION"
+fi
+
+ARTIFACTS=()
+for ARCH in "${ARCHES[@]}"; do
+    TARBALL_DIR=cri-o
+    TARBALL="$TARBALL_DIR.$ARCH.$ARCHIVE_ID.tar.gz"
+
+    mkdir "$ARCH"
+    mv "$TARBALL" "$ARCH"
+    mv "$TARBALL.spdx" "$ARCH/$SBOM_FILE"
+    pushd "$ARCH"
+
+    tar xf "$TARBALL"
+    bom validate -e "$SBOM_FILE" -d "$TARBALL_DIR"
+
+    ARGS=()
+    while IFS= read -r -d '' -u 9; do
+        MIME=$(file -b --mime-type "$REPLY")
+        ARGS+=("$REPLY:$MIME")
+    done 9< <(find "$TARBALL_DIR" -type f -exec printf '%s\0' {} +)
+
+    TARGET_ARTIFACT="$ARTIFACT:$TAG-$ARCH"
+    ARTIFACTS+=("$TARGET_ARTIFACT")
+
+    # shellcheck disable=SC2068
+    ARTIFACT_REF=$(oras push \
+        -a "$ANNOTATION_VERSION" \
+        -a "$ANNOTATION_COMMIT" \
+        -a "$ANNOTATION_BRANCH" \
+        --artifact-type "$BUNDLE_ARTIFACT_TYPE" \
+        --artifact-platform "linux/$ARCH" \
+        --format json \
+        "$TARGET_ARTIFACT,$COMMIT-$ARCH,$VERSION-$ARCH,$PROJECT_VERSION-$ARCH" \
+        ${ARGS[@]} | jq -r .reference)
+    cosign sign -y "$ARTIFACT_REF"
+
+    popd
+done
+
+# Multi-arch: https://github.com/oras-project/oras/blob/main/docs/proposals/multi-arch-image-mgmt.md
+# shellcheck disable=SC2068
+oras manifest index create \
+    -a "$ANNOTATION_VERSION" \
+    -a "$ANNOTATION_COMMIT" \
+    -a "$ANNOTATION_BRANCH" \
+    "$ARTIFACT:$TAG,$COMMIT,$VERSION,$PROJECT_VERSION" \
+    ${ARTIFACTS[@]} | tee -a out
+MANIFEST_REF=$(sed -n 's/Digest: //p' out)
+cosign sign -y "$ARTIFACT@$MANIFEST_REF"
+
+for ARCH in "${ARCHES[@]}"; do
+    pushd "$ARCH"
+    SBOM_REF=$(
+        oras attach \
+            -a "$ANNOTATION_VERSION" \
+            -a "$ANNOTATION_COMMIT" \
+            -a "$ANNOTATION_BRANCH" \
+            --platform "linux/$ARCH" \
+            --artifact-type "$SBOM_ARTIFACT_TYPE" \
+            "$ARTIFACT:$TAG" \
+            "$SBOM_FILE:application/spdx" \
+            --format json | jq -r .reference
+    )
+    popd
+    cosign sign -y "$SBOM_REF"
+done


Reviews:
user: login: haircommander
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T14:51:14Z
user: login: haircommander
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T14:51:38Z
user: login: saschagrunert
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-24T06:02:49Z

Review comments:
diff_hunk: @@ -29,10 +29,18 @@ install_osc() {
 
 install_krel() {
     KREL_VERSION=v0.18.0
-    BINARY=krel
-    curl_retry https://github.com/kubernetes/release/releases/download/$KREL_VERSION/$BINARY-amd64-linux -o $BINARY
-    chmod +x $BINARY
-    sudo cp $BINARY /usr/local/bin
-    rm $BINARY
-    $BINARY version
+    install_binary https://github.com/kubernetes/release/releases/download/$KREL_VERSION/krel-amd64-linux krel
+}
+
+install_bom() {
+    BOM_VERSION=v0.6.0
path: scripts/helpers
user: login: haircommander
body: should this be in dependencies.yaml?
created_at: 2025-04-23T14:51:14Z
author_association: MEMBER
diff_hunk: @@ -29,10 +29,18 @@ install_osc() {
 
 install_krel() {
     KREL_VERSION=v0.18.0
-    BINARY=krel
-    curl_retry https://github.com/kubernetes/release/releases/download/$KREL_VERSION/$BINARY-amd64-linux -o $BINARY
-    chmod +x $BINARY
-    sudo cp $BINARY /usr/local/bin
-    rm $BINARY
-    $BINARY version
+    install_binary https://github.com/kubernetes/release/releases/download/$KREL_VERSION/krel-amd64-linux krel
+}
+
+install_bom() {
+    BOM_VERSION=v0.6.0
path: scripts/helpers
user: login: haircommander
body: nevermind! it is :upside_down_face: 
created_at: 2025-04-23T14:51:38Z
author_association: MEMBER
diff_hunk: @@ -29,10 +29,18 @@ install_osc() {
 
 install_krel() {
     KREL_VERSION=v0.18.0
-    BINARY=krel
-    curl_retry https://github.com/kubernetes/release/releases/download/$KREL_VERSION/$BINARY-amd64-linux -o $BINARY
-    chmod +x $BINARY
-    sudo cp $BINARY /usr/local/bin
-    rm $BINARY
-    $BINARY version
+    install_binary https://github.com/kubernetes/release/releases/download/$KREL_VERSION/krel-amd64-linux krel
+}
+
+install_bom() {
+    BOM_VERSION=v0.6.0
path: scripts/helpers
user: login: saschagrunert
body: Yep :+1: 
created_at: 2025-04-24T06:02:49Z
author_association: MEMBER

PR comments:
user: login: openshift-ci[bot]
created_at: 2025-04-23T09:16:36Z
body: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/cri-o/packaging/pull/241#" title="Author self-approved">saschagrunert</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=cri-o%2Fpackaging).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/cri-o/packaging/blob/main/OWNERS)~~ [saschagrunert]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
author_association: CONTRIBUTOR
user: login: saschagrunert
created_at: 2025-04-23T11:08:39Z
body: @cri-o/cri-o-maintainers PTAL
author_association: MEMBER
user: login: haircommander
created_at: 2025-04-23T14:52:52Z
body: /lgtm
author_association: MEMBER