Skip to content

chore: remove release notes override (#344) #223

chore: remove release notes override (#344)

chore: remove release notes override (#344) #223

Workflow file for this run

name: Release
on:
push:
branches: [main]
workflow_dispatch:
inputs:
tag:
description: "Re-release an existing tag (e.g., v0.1.12). Leave empty for normal release-please flow."
required: false
type: string
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: read
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
# Release Please runs on every push to main. When a release PR is merged,
# it creates a GitHub Release + tag and sets release_created=true, which
# triggers the downstream release jobs in this same workflow run.
# This avoids the GITHUB_TOKEN limitation where tags created by Actions
# do not trigger separate tag-push workflows.
release-please:
name: Release Please
# Skip release-please when manually re-releasing an existing tag.
if: inputs.tag == '' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
pull-requests: write
outputs:
release_created: ${{ steps.release.outputs.release_created }}
tag_name: ${{ steps.release.outputs.tag_name }}
steps:
- uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
# Use the GitHub App token so that release-please PR pushes
# trigger pull_request events (GITHUB_TOKEN suppresses them,
# which prevents the CI Gate status check from appearing).
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
id: app-token
with:
client-id: ${{ vars.APP_CLIENT_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0
id: release
with:
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
token: ${{ steps.app-token.outputs.token }}
release:
name: Release
needs: [release-please]
# Run when release-please created a release, on manual workflow_dispatch
# with a tag ref, or when re-releasing via the tag input.
# always() is needed because release-please is skipped on
# workflow_dispatch, which would otherwise skip this job too.
if: |-
always() &&
!failure() &&
!cancelled() &&
(needs.release-please.outputs.release_created == 'true' ||
(github.event_name == 'workflow_dispatch' && startsWith(github.ref, 'refs/tags/')) ||
(github.event_name == 'workflow_dispatch' && inputs.tag != ''))
runs-on: ubuntu-latest
timeout-minutes: 30
environment: release
permissions:
attestations: write
contents: write
packages: write
pull-requests: write
id-token: write
outputs:
digest: ${{ steps.build.outputs.digest }}
tag: ${{ steps.tag.outputs.name }}
steps:
- uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
- name: Resolve release tag
id: tag
shell: bash
run: |
# On release-please: use its output. On re-release: use the input tag.
# On workflow_dispatch with tag ref: use ref_name.
TAG="${{ needs.release-please.outputs.tag_name || inputs.tag || github.ref_name }}"
echo "name=${TAG}" >> "$GITHUB_OUTPUT"
echo "Releasing: ${TAG}"
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
fetch-depth: 0
ref: ${{ steps.tag.outputs.name }}
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
with:
go-version-file: go.mod
cache: true
- name: Check if Docker build is possible
id: docker-check
run: |
if [ -n "${{ inputs.tag }}" ]; then
echo "enabled=false" >> "$GITHUB_OUTPUT"
echo "::warning::Skipping Docker build on re-release to preserve OLM bundle digest"
elif [ -f Dockerfile.release ]; then
echo "enabled=true" >> "$GITHUB_OUTPUT"
else
echo "enabled=false" >> "$GITHUB_OUTPUT"
echo "::warning::Dockerfile.release not found at this tag; skipping Docker image build"
fi
- name: Login to GHCR
if: steps.docker-check.outputs.enabled == 'true'
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Docker Hub
if: steps.docker-check.outputs.enabled == 'true'
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Docker Buildx
if: steps.docker-check.outputs.enabled == 'true'
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4
- name: Capture build date
if: steps.docker-check.outputs.enabled == 'true'
id: build_date
run: echo "date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_OUTPUT"
- name: Clean existing release assets (idempotent re-runs)
shell: bash
run: |
TAG="${{ steps.tag.outputs.name }}"
for asset in $(gh release view "$TAG" --json assets --jq '.assets[].name' 2>/dev/null || true); do
if [ -n "${{ inputs.tag }}" ]; then
# Re-release: only delete GoReleaser-produced assets so install.yaml,
# crds.yaml, SBOM, and provenance are preserved.
case "$asset" in
kubectl-attune_*.tar.gz|checksums.txt|checksums.txt.sig|checksums.txt.pem)
echo "Deleting GoReleaser asset: $asset"
gh release delete-asset "$TAG" "$asset" -y || true
;;
*)
echo "Preserving: $asset"
;;
esac
else
echo "Deleting existing asset: $asset"
gh release delete-asset "$TAG" "$asset" -y || true
fi
done
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install cosign
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@5daf1e915a5f0af01ddbcd89a43b8061ff4f1a89 # v7.2.2
with:
version: "~> v2"
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Apply custom release notes
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.tag.outputs.name }}
run: |
if [ -f RELEASE_NOTES.md ]; then
echo "Custom release notes found, updating release body..."
gh release edit "$TAG" --notes-file RELEASE_NOTES.md
else
echo "No custom release notes, using auto-generated notes"
fi
- name: Extract legacy .sig and .pem from Sigstore bundle
shell: bash -Eeuo pipefail {0}
run: |
# GoReleaser now signs with --bundle, producing a .sigstore.json.
# Extract separate .sig and .pem files for backward compatibility
# with downstream verification scripts.
BUNDLE="goreleaser-dist/checksums.txt.sigstore.json"
if [ -f "$BUNDLE" ] && [ ! -f "goreleaser-dist/checksums.txt.sig" ]; then
echo "Extracting .sig and .pem from Sigstore bundle"
jq -r '.messageSignature.signature' "$BUNDLE" \
> goreleaser-dist/checksums.txt.sig
jq -r '.verificationMaterial.certificate.rawBytes' "$BUNDLE" \
| base64 -d | openssl x509 -inform DER -outform PEM \
> goreleaser-dist/checksums.txt.pem
gh release upload "${{ steps.tag.outputs.name }}" \
goreleaser-dist/checksums.txt.sig \
goreleaser-dist/checksums.txt.pem \
--repo "${{ github.repository }}" --clobber
elif [ -f "goreleaser-dist/checksums.txt.sig" ]; then
echo "Legacy .sig already exists (produced by GoReleaser directly)"
else
echo "No bundle or signatures found; fallback step will handle this"
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Sign checksums (fallback for older tags without signs config)
if: inputs.tag != ''
shell: bash
run: |
# If GoReleaser's .goreleaser.yaml at this tag lacks any signs
# config, neither .sigstore.json nor .sig will exist. Sign manually.
if [ ! -f goreleaser-dist/checksums.txt.sig ] && [ ! -f goreleaser-dist/checksums.txt.sigstore.json ]; then
echo "GoReleaser did not produce signatures; signing checksums manually"
cosign sign-blob --yes \
--bundle goreleaser-dist/checksums.txt.bundle \
goreleaser-dist/checksums.txt
jq -r '.messageSignature.signature' goreleaser-dist/checksums.txt.bundle \
> goreleaser-dist/checksums.txt.sig
jq -r '.verificationMaterial.certificate.rawBytes' goreleaser-dist/checksums.txt.bundle \
| base64 -d | openssl x509 -inform DER -outform PEM \
> goreleaser-dist/checksums.txt.pem
gh release upload "${{ steps.tag.outputs.name }}" \
goreleaser-dist/checksums.txt.sig \
goreleaser-dist/checksums.txt.pem \
--repo "${{ github.repository }}" --clobber
else
echo "GoReleaser already produced signatures"
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Attest binary archives
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
with:
subject-path: goreleaser-dist/*.tar.gz
- name: Attest checksums
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
with:
subject-path: goreleaser-dist/checksums.txt
- name: Upload attestation bundles to release
shell: bash -Eeuo pipefail {0}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.tag.outputs.name }}
run: |
mkdir -p attestation-bundles
for asset in goreleaser-dist/*.tar.gz goreleaser-dist/checksums.txt; do
[ -f "$asset" ] || continue
name=$(basename "$asset")
tmpdir=$(mktemp -d)
pushd "$tmpdir" > /dev/null
if gh attestation download "$OLDPWD/$asset" \
--repo "${{ github.repository }}" 2>/dev/null; then
for bundle in *.jsonl; do
[ -f "$bundle" ] || continue
cp "$bundle" "$OLDPWD/attestation-bundles/${name}.intoto.jsonl"
break
done
fi
popd > /dev/null
rm -rf "$tmpdir"
done
if ls attestation-bundles/*.intoto.jsonl 1>/dev/null 2>&1; then
gh release upload "$TAG" attestation-bundles/*.intoto.jsonl --clobber
fi
- name: Prepare release image context
if: steps.docker-check.outputs.enabled == 'true'
shell: bash -Eeuo pipefail {0}
run: |
# Map GoReleaser output dirs to Docker TARGETARCH layout.
# GoReleaser names dirs like manager_linux_{arch}_{variant},
# e.g. manager_linux_amd64_v1, manager_linux_arm_7, manager_linux_s390x.
for dir in goreleaser-dist/manager_linux_*/; do
arch=$(basename "$dir" | sed 's/^manager_linux_//' | sed 's/_.*//')
mkdir -p ".release-image/${arch}"
cp "${dir}manager" ".release-image/${arch}/manager"
done
# Verify all target platforms have a binary
for arch in amd64 arm64 arm ppc64le s390x; do
if [ ! -f ".release-image/${arch}/manager" ]; then
echo "ERROR: missing manager binary for ${arch}" >&2
exit 1
fi
done
- name: Compute image tags
if: steps.docker-check.outputs.enabled == 'true'
id: image-tags
shell: bash
run: |
TAG="${{ steps.tag.outputs.name }}"
NL=$'\n'
TAGS="${REGISTRY}/${IMAGE_NAME}:${TAG}${NL}docker.io/attuneio/attune:${TAG}"
# Skip :latest on re-releases to avoid overwriting a newer release
if [ -z "${{ inputs.tag }}" ]; then
TAGS="${TAGS}${NL}${REGISTRY}/${IMAGE_NAME}:latest${NL}docker.io/attuneio/attune:latest"
fi
{
echo "tags<<EOF"
echo "$TAGS"
echo "EOF"
} >> "$GITHUB_OUTPUT"
- name: Build and push multi-arch image
if: steps.docker-check.outputs.enabled == 'true'
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
id: build
with:
context: .release-image
file: Dockerfile.release
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/ppc64le,linux/s390x
push: true
tags: ${{ steps.image-tags.outputs.tags }}
labels: |
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.version=${{ steps.tag.outputs.name }}
- name: Sign GHCR image
if: steps.docker-check.outputs.enabled == 'true'
shell: bash -Eeuo pipefail -x {0}
run: |
cosign sign --yes \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }}
- name: Sign Docker Hub image
if: steps.docker-check.outputs.enabled == 'true'
shell: bash -Eeuo pipefail -x {0}
run: |
cosign sign --yes \
docker.io/attuneio/attune@${{ steps.build.outputs.digest }}
- name: Attest container image
if: steps.docker-check.outputs.enabled == 'true'
uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
with:
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
subject-digest: ${{ steps.build.outputs.digest }}
push-to-registry: true
- name: Generate SBOM
if: steps.docker-check.outputs.enabled == 'true'
uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0
with:
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }}
format: spdx-json
output-file: sbom.spdx.json
- name: Trivy scan released image
if: steps.docker-check.outputs.enabled == 'true'
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }}
severity: HIGH,CRITICAL
exit-code: 1
- name: Sign SBOM
if: steps.docker-check.outputs.enabled == 'true'
shell: bash -Eeuo pipefail -x {0}
run: |
cosign sign-blob --yes \
--bundle sbom.spdx.json.bundle \
sbom.spdx.json
- name: Generate install manifest and CRDs bundle
if: steps.docker-check.outputs.enabled == 'true'
shell: bash -Eeuo pipefail -x {0}
run: |
make build-installer IMG=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.tag.outputs.name }}
make build-crds
- name: Attach install manifest, CRDs, and SBOM to release
if: steps.docker-check.outputs.enabled == 'true'
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3
with:
tag_name: ${{ steps.tag.outputs.name }}
fail_on_unmatched_files: true
files: |
dist/install.yaml
dist/crds.yaml
sbom.spdx.json
sbom.spdx.json.bundle
# Krew manifest update runs last with continue-on-error so a Krew
# failure never blocks Docker images, signatures, or provenance.
- name: Update Krew plugin manifest
if: inputs.tag == ''
continue-on-error: true
uses: rajatjindal/krew-release-bot@c970b8a8f6dbc2f2285a26e3ae160903b87002c3 # v0.0.51
with:
krew_plugin_release_tag: ${{ steps.tag.outputs.name }}
# Clean up RELEASE_NOTES.md from main after the release is published.
# Branch protection blocks direct pushes, so we create a short-lived PR.
# Uses the GitHub App token so the PR triggers CI and auto-approve.
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
id: cleanup-token
with:
client-id: ${{ vars.APP_CLIENT_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Clean up release notes file
env:
GH_TOKEN: ${{ steps.cleanup-token.outputs.token }}
TAG: ${{ steps.tag.outputs.name }}
run: |
if git ls-tree HEAD --name-only | grep -q '^RELEASE_NOTES.md$'; then
BRANCH="chore/cleanup-release-notes-${TAG}"
gh api "repos/${{ github.repository }}/git/refs" \
-f ref="refs/heads/$BRANCH" \
-f sha="$(git rev-parse HEAD)"
FILE_SHA=$(gh api \
"repos/${{ github.repository }}/contents/RELEASE_NOTES.md?ref=$BRANCH" \
--jq '.sha')
gh api --method DELETE \
"repos/${{ github.repository }}/contents/RELEASE_NOTES.md" \
-f message="chore: remove release notes override" \
-f sha="$FILE_SHA" \
-f branch="$BRANCH"
PR_URL=$(gh pr create --base main --head "$BRANCH" \
--title "chore: remove release notes override" \
--body "Auto-cleanup after ${TAG} release.")
gh pr merge "$PR_URL" --auto --squash
fi
helm-release:
name: Helm Chart Release
runs-on: ubuntu-latest
timeout-minutes: 10
needs: [release]
# Explicit condition required: release-please is skipped on workflow_dispatch,
# and GitHub Actions propagates skips transitively through the needs chain.
# Without this, downstream jobs are skipped even when release succeeded.
# Skip on re-releases (inputs.tag set) since re-releases only fix release
# artifacts; Helm chart versions are immutable once published.
if: ${{ !cancelled() && needs.release.result == 'success' && inputs.tag == '' }}
permissions:
contents: read
packages: write
id-token: write
steps:
- uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
ref: ${{ needs.release.outputs.tag }}
- uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5
with:
version: v4.1.4
- name: Login to GHCR (Helm)
shell: bash -Eeuo pipefail -x {0}
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | helm registry login ${{ env.REGISTRY }} \
--username ${{ github.actor }} --password-stdin
- name: Login to GHCR (Docker/cosign)
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Docker Hub (Docker/cosign)
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Package and push Helm chart
shell: bash -Eeuo pipefail -x {0}
run: |
# Update chart version to match tag
VERSION="${{ needs.release.outputs.tag }}"
VERSION="${VERSION#v}" # Strip 'v' prefix
sed -i "s/^version:.*/version: ${VERSION}/" charts/attune/Chart.yaml
sed -i "s/^appVersion:.*/appVersion: ${VERSION}/" charts/attune/Chart.yaml
helm package charts/attune
helm push attune-${VERSION}.tgz oci://${{ env.REGISTRY }}/${{ github.repository_owner }}/charts
- uses: oras-project/setup-oras@f8710a5536385a773dd14a148cf030021e293aef # v2 (includes 1.3.2)
with:
version: "1.3.2"
- name: Copy Helm chart to Docker Hub
shell: bash -Eeuo pipefail -x {0}
run: |
# helm push appends the chart name, creating a 3-level path
# (attuneio/attune-chart/attune) that Docker Hub rejects.
# Use oras cp to copy from GHCR to the exact 2-level path.
VERSION="${{ needs.release.outputs.tag }}"
VERSION="${VERSION#v}"
echo "${{ secrets.DOCKERHUB_TOKEN }}" | oras login registry-1.docker.io \
--username "${{ secrets.DOCKERHUB_USERNAME }}" --password-stdin
oras cp \
${{ env.REGISTRY }}/${{ github.repository_owner }}/charts/attune:${VERSION} \
registry-1.docker.io/attuneio/attune-chart:${VERSION}
- name: Install cosign
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
- name: Sign Helm chart (GHCR)
shell: bash -Eeuo pipefail -x {0}
run: |
VERSION="${{ needs.release.outputs.tag }}"
VERSION="${VERSION#v}"
cosign sign --yes \
${{ env.REGISTRY }}/${{ github.repository_owner }}/charts/attune:${VERSION}
- name: Sign Helm chart (Docker Hub)
shell: bash -Eeuo pipefail -x {0}
run: |
VERSION="${{ needs.release.outputs.tag }}"
VERSION="${VERSION#v}"
cosign sign --yes \
docker.io/attuneio/attune-chart:${VERSION}
- name: Push Artifact Hub metadata
shell: bash -Eeuo pipefail -x {0}
run: |
# Push artifacthub-repo.yml as OCI artifact for Verified Publisher status
echo "${{ secrets.GITHUB_TOKEN }}" | oras login ${{ env.REGISTRY }} \
--username ${{ github.actor }} --password-stdin
oras push \
${{ env.REGISTRY }}/${{ github.repository_owner }}/charts/attune:artifacthub.io \
--config /dev/null:application/vnd.cncf.artifacthub.config.v1+yaml \
artifacthub-repo.yml:application/vnd.cncf.artifacthub.repository-metadata.layer.v1.yaml
# Submit OLM bundle PRs to OperatorHub repos.
# Step-level continue-on-error keeps the job green while exposing the
# actual outcome via outputs for the release-notify job.
operatorhub-pr:
name: OperatorHub PR
runs-on: ubuntu-latest
timeout-minutes: 15
needs: [release]
# Skip on re-releases; OLM bundles were already submitted for this version.
if: ${{ !cancelled() && needs.release.result == 'success' && inputs.tag == '' }}
permissions:
contents: read
outputs:
outcome: ${{ steps.create-pr.outcome }}
steps:
- uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
ref: ${{ needs.release.outputs.tag }}
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
with:
go-version-file: go.mod
cache: true
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
id: app-token
with:
client-id: ${{ vars.APP_CLIENT_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
owner: attune-io
repositories: community-operators,community-operators-prod
- name: Generate OLM bundle and create PRs
id: create-pr
continue-on-error: true
env:
VERSION: ${{ needs.release.outputs.tag }}
IMAGE_DIGEST: ${{ needs.release.outputs.digest }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
UPSTREAM_GH_TOKEN: ${{ secrets.OPERATORHUB_PAT }}
FORK_OWNER: attune-io
run: |
# Strip 'v' prefix from tag
export VERSION="${VERSION#v}"
hack/operatorhub-pr.sh
# Sync docker/README.md to the Docker Hub repository description.
dockerhub-readme:
name: Docker Hub README
runs-on: ubuntu-latest
timeout-minutes: 5
needs: [release]
# Skip on re-releases; the README content at the re-released tag may be
# older than what is currently on Docker Hub.
if: ${{ !cancelled() && needs.release.result == 'success' && inputs.tag == '' }}
steps:
- uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- name: Update Docker Hub description
uses: peter-evans/dockerhub-description@1b9a80c056b620d92cedb9d9b5a223409c68ddfa # v5.0.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
repository: attuneio/attune
readme-filepath: docker/README.md
short-description: "Safe, in-place Kubernetes pod resource right-sizing. VPA done right."
# Create a GitHub Issue if any optional post-release job failed.
# This prevents silent failures from going unnoticed.
release-notify:
name: Release Health Check
runs-on: ubuntu-latest
timeout-minutes: 5
needs: [release, operatorhub-pr, helm-release]
if: always() && needs.release.result == 'success'
permissions:
issues: write
steps:
- name: Check for failures and create issue
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPERATORHUB_OUTCOME: ${{ needs.operatorhub-pr.outputs.outcome }}
HELM_RESULT: ${{ needs.helm-release.result }}
TAG: ${{ needs.release.outputs.tag }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
FAILURES=""
if [ "${OPERATORHUB_OUTCOME}" = "failure" ]; then
FAILURES="${FAILURES}- OperatorHub PR creation failed\n"
fi
if [ "${HELM_RESULT}" = "failure" ]; then
FAILURES="${FAILURES}- Helm chart release failed\n"
fi
if [ -n "${FAILURES}" ]; then
BODY="The release ${TAG} succeeded, but some post-release jobs failed:\n\n${FAILURES}\n[View workflow run](${RUN_URL})\n\nThese failures do not affect the core release (container images, binaries, signatures) but may leave the OperatorHub listing or Helm chart out of date."
gh issue create \
--repo "${{ github.repository }}" \
--title "Release ${TAG}: post-release job failures" \
--label "bug" \
--body "$(echo -e "${BODY}")"
else
echo "All post-release jobs succeeded."
fi