Skip to content
Closed
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
240 changes: 64 additions & 176 deletions template/.github/workflows/build.yaml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ on:
env:
OPERATOR_NAME: "{[ operator.name }]"
RUST_NIGHTLY_TOOLCHAIN_VERSION: "{[ rust_nightly_version }]"
NIX_PKG_MANAGER_VERSION: "{[ nix_pkg_manager_version }]"
RUST_TOOLCHAIN_VERSION: "{[ rust_version }]"
HADOLINT_VERSION: "{[ hadolint_version }]"
PYTHON_VERSION: "{[ python_version }]"
Expand Down Expand Up @@ -98,35 +97,23 @@ jobs:
- name: Run cargo-udeps
run: cargo udeps --workspace --all-targets

build-container-image:
name: Build/Publish ${{ matrix.runner.arch }} Image
operator-version:
name: Determine Operator Version
if: (github.event_name != 'merge_group') && needs.detect-changes.outputs.detected == 'true'
needs: [detect-changes]
runs-on: ubuntu-latest
permissions:
id-token: write
strategy:
fail-fast: false
matrix:
runner:
- { name: "ubuntu-latest", arch: "amd64" }
- { name: "ubicloud-standard-8-arm", arch: "arm64" }
runs-on: ${{ matrix.runner.name }}
contents: read
outputs:
operator-version: ${{ steps.version.outputs.OPERATOR_VERSION }}
steps:
- name: Install host dependencies
uses: awalsh128/cache-apt-pkgs-action@2c09a5e66da6c8016428a2172bd76e5e4f14bb17 # v1.5.3
with:
packages: protobuf-compiler krb5-user libkrb5-dev libclang-dev liblzma-dev libssl-dev pkg-config apt-transport-https
version: ${{ matrix.runner.name }}

- name: Checkout Repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
submodules: recursive

- name: Update/Extract Operator Version
- name: Extract Operator Version
id: version
env:
PR_BASE_REF: ${{ github.event.pull_request.base.ref }}
Expand All @@ -138,6 +125,8 @@ jobs:
set -euo pipefail
[ -n "$GITHUB_DEBUG" ] && set -x

# `cargo metadata --no-deps` only parses the manifests, so the
# toolchain preinstalled on the runner is sufficient here.
CURRENT_VERSION=$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[] | select(.name == "stackable-{[ operator.name }]") | .version')

if [ "$GITHUB_EVENT_NAME" == 'pull_request' ]; then
Expand All @@ -152,153 +141,62 @@ jobs:
NEW_VERSION="$CURRENT_VERSION"
fi

sed -i "s/version = \"${CURRENT_VERSION}\"/version = \"${NEW_VERSION}\"/" Cargo.toml
# The version is only reported here. Stamping it into Cargo.toml
# happens inside the builder workflow via its cargo-workspace-version
# input, because caller-supplied shell must not run on the builder's
# runners.
echo "OPERATOR_VERSION=$NEW_VERSION" | tee -a "$GITHUB_OUTPUT"

- name: Install Nix
uses: cachix/install-nix-action@fc6e360bedc9ee72d75e701397f0bb30dce77568 # v31.5.2

- name: Install Rust ${{ env.RUST_TOOLCHAIN_VERSION }} Toolchain
shell: bash
run: rustup toolchain install "${RUST_TOOLCHAIN_VERSION}"

- name: Build Container Image
id: build
uses: stackabletech/actions/build-container-image@dc83bb926cc464f0f32454e934777116bd1c7768 # v0.16.3
with:
image-name: ${{ env.OPERATOR_NAME }}
image-index-manifest-tag: ${{ steps.version.outputs.OPERATOR_VERSION }}
build-arguments: VERSION=${{ steps.version.outputs.OPERATOR_VERSION }}
container-file: docker/Dockerfile

- name: Publish Container Image to oci.stackable.tech
if: ${{ !github.event.pull_request.head.repo.fork }}
uses: stackabletech/actions/publish-image@dc83bb926cc464f0f32454e934777116bd1c7768 # v0.16.3
with:
image-registry-uri: oci.stackable.tech
image-registry-username: robot$sdp+github-action-build
image-registry-password: ${{ secrets.HARBOR_ROBOT_SDP_GITHUB_ACTION_BUILD_SECRET }}
image-repository: sdp/${{ env.OPERATOR_NAME }}
image-manifest-tag: ${{ steps.build.outputs.image-manifest-tag }}
source-image-uri: ${{ steps.build.outputs.image-manifest-uri }}

- name: Publish Container Image to quay.io
if: ${{ !github.event.pull_request.head.repo.fork }}
uses: stackabletech/actions/publish-image@dc83bb926cc464f0f32454e934777116bd1c7768 # v0.16.3
with:
image-registry-uri: quay.io
image-registry-username: stackable+robot_sdp_github_action_build
image-registry-password: ${{ secrets.QUAY_ROBOT_SDP_GITHUB_ACTION_BUILD_SECRET }}
image-repository: stackable/sdp/${{ env.OPERATOR_NAME }}
image-manifest-tag: ${{ steps.build.outputs.image-manifest-tag }}
source-image-uri: ${{ steps.build.outputs.image-manifest-uri }}

publish-index-manifest:
name: Publish/Sign ${{ needs.build-container-image.outputs.operator-version }} Index
if: |
(github.event_name != 'merge_group')
&& needs.detect-changes.outputs.detected == 'true'
&& !github.event.pull_request.head.repo.fork
needs:
- detect-changes
- build-container-image
permissions:
id-token: write
runs-on: ubuntu-latest
outputs:
oci-index-digest: ${{ steps.publish-oci.outputs.image-index-manifest-digest }}
quay-index-digest: ${{ steps.publish-quay.outputs.image-index-manifest-digest }}
steps:
- name: Checkout Repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false

- name: Publish and Sign Image Index to oci.stackable.tech
id: publish-oci
uses: stackabletech/actions/publish-image-index-manifest@dc83bb926cc464f0f32454e934777116bd1c7768 # v0.16.3
with:
image-registry-uri: oci.stackable.tech
image-registry-username: robot$sdp+github-action-build
image-registry-password: ${{ secrets.HARBOR_ROBOT_SDP_GITHUB_ACTION_BUILD_SECRET }}
image-repository: sdp/${{ env.OPERATOR_NAME }}
image-index-manifest-tag: ${{ needs.build-container-image.outputs.operator-version }}

- name: Publish and Sign Image Index to quay.io
id: publish-quay
uses: stackabletech/actions/publish-image-index-manifest@dc83bb926cc464f0f32454e934777116bd1c7768 # v0.16.3
with:
image-registry-uri: quay.io
image-registry-username: stackable+robot_sdp_github_action_build
image-registry-password: ${{ secrets.QUAY_ROBOT_SDP_GITHUB_ACTION_BUILD_SECRET }}
image-repository: stackable/sdp/${{ env.OPERATOR_NAME }}
image-index-manifest-tag: ${{ needs.build-container-image.outputs.operator-version }}

# Generate SLSA build provenance for the multi-arch image index and attach it
# to the published image in each registry. The reusable workflow signs the
# provenance with keyless signing (GitHub Actions as the OIDC identity) and
# pushes the attestation next to the image.
provenance-oci:
name: Generate Provenance for ${{ needs.build-container-image.outputs.operator-version }} (oci.stackable.tech)
if: |
(github.event_name != 'merge_group')
&& needs.detect-changes.outputs.detected == 'true'
&& !github.event.pull_request.head.repo.fork
needs:
- detect-changes
- build-container-image
- publish-index-manifest
permissions:
actions: read # detect the build workflow that generated the image
id-token: write # mint the OIDC token for keyless signing
packages: write # needed until https://github.com/slsa-framework/slsa-github-generator/issues/1257 is resolved
# MUST be referenced by a @vX.Y.Z tag (not a SHA), otherwise the reusable
# workflow cannot verify its own provenance.
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
with:
# The 'env' context is not available in job-level 'with' inputs of
# reusable workflow calls (unlike step-level 'with'), so OPERATOR_NAME
# can't be used here and the operator name is templated in directly.
image: oci.stackable.tech/sdp/{[ operator.name }]
digest: ${{ needs.publish-index-manifest.outputs.oci-index-digest }}
registry-username: robot$sdp+github-action-build
secrets:
registry-password: ${{ secrets.HARBOR_ROBOT_SDP_GITHUB_ACTION_BUILD_SECRET }}

provenance-quay:
name: Generate Provenance for ${{ needs.build-container-image.outputs.operator-version }} (quay.io)
if: |
(github.event_name != 'merge_group')
&& needs.detect-changes.outputs.detected == 'true'
&& !github.event.pull_request.head.repo.fork
needs:
- detect-changes
- build-container-image
- publish-index-manifest
# Building, publishing, signing and attesting all happen inside the shared
# builder workflow in stackabletech/actions. Running the build in a reusable
# workflow separate from this one is GitHub's documented mechanism for
# reaching SLSA v1.0 Build Level 3.
#
# Only source-level build parameters are passed, so this workflow cannot hand
# the builder a digest and obtain provenance for an image it did not build.
build-container-image:
name: Build/Publish/Attest ${{ needs.operator-version.outputs.operator-version }} Image
if: (github.event_name != 'merge_group') && needs.detect-changes.outputs.detected == 'true'
needs: [detect-changes, operator-version]
permissions:
actions: read # detect the build workflow that generated the image
id-token: write # mint the OIDC token for keyless signing
packages: write # needed until https://github.com/slsa-framework/slsa-github-generator/issues/1257 is resolved
# MUST be referenced by a @vX.Y.Z tag (not a SHA), otherwise the reusable
# workflow cannot verify its own provenance.
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
contents: read
id-token: write # cosign keyless signing and attestation OIDC identity
attestations: write # persist the attestation in the GitHub attestation store
artifact-metadata: write # create the artifact metadata storage record
# TODO: pin to a release tag once stackabletech/actions cuts one containing
# reusable_build_container_image.yaml
uses: stackabletech/actions/.github/workflows/reusable_build_container_image.yaml@main
with:
# The 'env' context is not available in job-level 'with' inputs of
# reusable workflow calls (unlike step-level 'with'), so OPERATOR_NAME
# can't be used here and the operator name is templated in directly.
image: quay.io/stackable/sdp/{[ operator.name }]
digest: ${{ needs.publish-index-manifest.outputs.quay-index-digest }}
registry-username: stackable+robot_sdp_github_action_build
image-name: "{[ operator.name }]"
image-index-manifest-tag: ${{ needs.operator-version.outputs.operator-version }}
container-file: docker/Dockerfile
build-arguments: VERSION=${{ needs.operator-version.outputs.operator-version }}
cargo-workspace-version: ${{ needs.operator-version.outputs.operator-version }}
arm64-runner: ubicloud-standard-8-arm
oci-repository: sdp/{[ operator.name }]
oci-username: robot$sdp+github-action-build
quay-repository: stackable/sdp/{[ operator.name }]
quay-username: stackable+robot_sdp_github_action_build
publish: ${{ !github.event.pull_request.head.repo.fork }}
secrets:
registry-password: ${{ secrets.QUAY_ROBOT_SDP_GITHUB_ACTION_BUILD_SECRET }}
oci-password: ${{ secrets.HARBOR_ROBOT_SDP_GITHUB_ACTION_BUILD_SECRET }}
quay-password: ${{ secrets.QUAY_ROBOT_SDP_GITHUB_ACTION_BUILD_SECRET }}

publish-helm-chart:
name: Package/Publish ${{ needs.build-container-image.outputs.operator-version }} Helm Chart
name: Package/Publish ${{ needs.operator-version.outputs.operator-version }} Helm Chart
if: |
(github.event_name != 'merge_group')
&& needs.detect-changes.outputs.detected == 'true'
# build-container-image is a dependency even though no output of it is used
# here: the chart's appVersion points at the image tag, so publishing a
# chart for an image that failed to build would advertise something that
# does not exist.
needs:
- detect-changes
- operator-version
- build-container-image
permissions:
id-token: write
Expand All @@ -318,8 +216,8 @@ jobs:
chart-registry-password: ${{ secrets.HARBOR_ROBOT_SDP_CHARTS_GITHUB_ACTION_BUILD_SECRET }}
chart-repository: sdp-charts
chart-directory: deploy/helm/${{ env.OPERATOR_NAME }}
chart-version: ${{ needs.build-container-image.outputs.operator-version }}
app-version: ${{ needs.build-container-image.outputs.operator-version }}
chart-version: ${{ needs.operator-version.outputs.operator-version }}
app-version: ${{ needs.operator-version.outputs.operator-version }}
publish-and-sign: ${{ !github.event.pull_request.head.repo.fork }}

- name: Package, Publish, and Sign Helm Chart to quay.io
Expand All @@ -330,21 +228,21 @@ jobs:
chart-registry-password: ${{ secrets.QUAY_ROBOT_SDP_CHARTS_GITHUB_ACTION_BUILD_SECRET }}
chart-repository: stackable/sdp-charts
chart-directory: deploy/helm/${{ env.OPERATOR_NAME }}
chart-version: ${{ needs.build-container-image.outputs.operator-version }}
app-version: ${{ needs.build-container-image.outputs.operator-version }}
chart-version: ${{ needs.operator-version.outputs.operator-version }}
app-version: ${{ needs.operator-version.outputs.operator-version }}
publish-and-sign: ${{ !github.event.pull_request.head.repo.fork }}
helm-version: v3.17.4 # This is currently the latest version which supports pushing to quay.io

openshift-preflight-check:
name: Run OpenShift Preflight Check for ${{ needs.build-container-image.outputs.operator-version }}-${{ matrix.arch }}
name: Run OpenShift Preflight Check for ${{ needs.operator-version.outputs.operator-version }}-${{ matrix.arch }}
if: |
(github.event_name != 'merge_group')
&& needs.detect-changes.outputs.detected == 'true'
&& !github.event.pull_request.head.repo.fork
needs:
- detect-changes
- operator-version
- build-container-image
- publish-index-manifest
strategy:
fail-fast: false
matrix:
Expand All @@ -356,13 +254,13 @@ jobs:
- name: Run OpenShift Preflight Check for oci.stackable.tech
uses: stackabletech/actions/run-openshift-preflight@dc83bb926cc464f0f32454e934777116bd1c7768 # v0.16.3
with:
image-index-uri: oci.stackable.tech/sdp/${{ env.OPERATOR_NAME }}:${{ needs.build-container-image.outputs.operator-version }}
image-index-uri: oci.stackable.tech/sdp/${{ env.OPERATOR_NAME }}:${{ needs.operator-version.outputs.operator-version }}
image-architecture: ${{ matrix.arch }}

- name: Run OpenShift Preflight Check for quay.io
uses: stackabletech/actions/run-openshift-preflight@dc83bb926cc464f0f32454e934777116bd1c7768 # v0.16.3
with:
image-index-uri: quay.io/stackable/sdp/${{ env.OPERATOR_NAME }}:${{ needs.build-container-image.outputs.operator-version }}
image-index-uri: quay.io/stackable/sdp/${{ env.OPERATOR_NAME }}:${{ needs.operator-version.outputs.operator-version }}
image-architecture: ${{ matrix.arch }}

# This job is a required check in GitHub Settings for this repository.
Expand All @@ -377,17 +275,15 @@ jobs:
# would *skip* this job, and GitHub treats a skipped required check as
# passing - making a broken build mergeable.
if: always()
# List every leaf job directly. A transitive failure (e.g. a failed
# publish-index-manifest that skips openshift-preflight-check) does not
# List every job directly. A transitive failure (e.g. a failed
# build-container-image that skips openshift-preflight-check) does not
# surface as `failure` in `needs.*.result` unless the failing job is a
# direct dependency.
needs:
- detect-changes
- cargo-udeps
- operator-version
- build-container-image
- publish-index-manifest
- provenance-oci
- provenance-quay
- publish-helm-chart
- openshift-preflight-check
runs-on: ubuntu-latest
Expand Down Expand Up @@ -418,9 +314,6 @@ jobs:
needs:
- detect-changes
- build-container-image
- publish-index-manifest
- provenance-oci
- provenance-quay
- publish-helm-chart
runs-on: ubuntu-latest
steps:
Expand All @@ -433,17 +326,12 @@ jobs:
uses: stackabletech/actions/send-slack-notification@dc83bb926cc464f0f32454e934777116bd1c7768 # v0.16.3
with:
publish-helm-chart-result: ${{ needs.publish-helm-chart.result }}
publish-manifests-result: ${{ needs.publish-index-manifest.result }}
# Building, publishing the index manifest and generating provenance
# all happen inside the builder workflow, so the three notification
# fields collapse into its single job result.
publish-manifests-result: ${{ needs.build-container-image.result }}
build-result: ${{ needs.build-container-image.result }}
# Provenance is generated per registry, but the notification only has a
# single field for it, so the two results are collapsed into the worst
# one. 'failure' must be reported verbatim, otherwise the notification
# is not marked as failed.
generate-provenance-result: >-
${{ (needs.provenance-oci.result == 'failure' || needs.provenance-quay.result == 'failure') && 'failure'
|| (needs.provenance-oci.result == 'cancelled' || needs.provenance-quay.result == 'cancelled') && 'cancelled'
|| (needs.provenance-oci.result == 'skipped' || needs.provenance-quay.result == 'skipped') && 'skipped'
|| 'success' }}
generate-provenance-result: ${{ needs.build-container-image.result }}
slack-token: ${{ secrets.SLACK_CONTAINER_IMAGE_TOKEN }}
channel-id: C07UG6JH44F # notifications-container-images
type: container-image-build