chore(main): release wordpress 5.5.9 (#668) #627
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release Charts to OCI Registry | |
| # Triggered by per-chart release tags (e.g. wordpress-v4.5.0) created by | |
| # release-please when a release PR is merged. The chart name + version are parsed | |
| # from the tag; the chart is packaged, pushed to ghcr.io/slybase/charts, signed | |
| # with Cosign and its ArtifactHub metadata is uploaded via ORAS. | |
| # | |
| # PR validation (helm lint/template) lives in pr-chart-validate.yml. | |
| on: | |
| push: | |
| tags: | |
| - "*-v*" | |
| workflow_dispatch: | |
| inputs: | |
| chart: | |
| description: "Chart to publish" | |
| required: true | |
| default: "wg-easy" | |
| type: choice | |
| options: | |
| - wg-easy | |
| - wireguard | |
| - wordpress | |
| version: | |
| description: "Version to publish (defaults to the chart's Chart.yaml version)" | |
| required: false | |
| type: string | |
| dry_run: | |
| description: "Package only — skip push/sign/publish" | |
| required: false | |
| default: true | |
| type: boolean | |
| force: | |
| description: "Force push even if the version already exists" | |
| required: false | |
| default: false | |
| type: boolean | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | |
| HELM_VERSION: v3.18.3 | |
| ORAS_VERSION: 1.2.0 | |
| SYFT_VERSION: 1.45.1 | |
| TRIVY_VERSION: 0.71.0 | |
| # Tag-triggered releases publish live when this is "true". | |
| # Set to "false" to revert to dry-run mode (Phase 0 safety net). | |
| RELEASE_ENABLED: "true" | |
| jobs: | |
| oci-release: | |
| name: Release to OCI Registry | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| id-token: write | |
| attestations: write | |
| pull-requests: write | |
| steps: | |
| - name: Resolve release context | |
| id: ctx | |
| shell: bash | |
| env: | |
| EVENT_NAME: ${{ github.event_name }} | |
| REF_NAME: ${{ github.ref_name }} | |
| INPUT_CHART: ${{ github.event.inputs.chart }} | |
| INPUT_VERSION: ${{ github.event.inputs.version }} | |
| INPUT_DRY_RUN: ${{ github.event.inputs.dry_run }} | |
| INPUT_FORCE: ${{ github.event.inputs.force }} | |
| run: | | |
| set -euo pipefail | |
| if [ "$EVENT_NAME" = "workflow_dispatch" ]; then | |
| chart_name="$INPUT_CHART" | |
| chart_version="$INPUT_VERSION" # may be empty -> resolved from Chart.yaml later | |
| dry_run="$INPUT_DRY_RUN" | |
| force="$INPUT_FORCE" | |
| checkout_ref="${{ github.ref }}" | |
| else | |
| # Tag push, e.g. "wordpress-v4.5.0" -> name=wordpress version=4.5.0 | |
| tag="$REF_NAME" | |
| chart_name="${tag%-v*}" | |
| chart_version="${tag##*-v}" | |
| if [ "$RELEASE_ENABLED" = "true" ]; then dry_run="false"; else dry_run="true"; fi | |
| force="false" | |
| checkout_ref="$tag" | |
| fi | |
| chart_dir="charts/${chart_name}" | |
| { | |
| echo "chart_name=$chart_name" | |
| echo "chart_version=$chart_version" | |
| echo "chart_dir=$chart_dir" | |
| echo "dry_run=$dry_run" | |
| echo "force=$force" | |
| echo "checkout_ref=$checkout_ref" | |
| } >> "$GITHUB_OUTPUT" | |
| echo "Chart: $chart_name" | |
| echo "Version: ${chart_version:-<from Chart.yaml>}" | |
| echo "Dry run: $dry_run (RELEASE_ENABLED=$RELEASE_ENABLED)" | |
| - name: Checkout | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| ref: ${{ steps.ctx.outputs.checkout_ref }} | |
| fetch-depth: 1 | |
| - name: Install Helm | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| archive="helm-${HELM_VERSION}-linux-amd64.tar.gz" | |
| curl -fsSLO "https://get.helm.sh/${archive}" | |
| tar -zxf "$archive" | |
| sudo mv linux-amd64/helm /usr/local/bin/helm | |
| rm -rf "$archive" linux-amd64 | |
| helm version --short | |
| - name: Resolve chart version from Chart.yaml (if not provided) | |
| id: chart | |
| shell: bash | |
| env: | |
| CHART_DIR: ${{ steps.ctx.outputs.chart_dir }} | |
| CHART_NAME: ${{ steps.ctx.outputs.chart_name }} | |
| CHART_VERSION: ${{ steps.ctx.outputs.chart_version }} | |
| run: | | |
| set -euo pipefail | |
| if [ ! -f "$CHART_DIR/Chart.yaml" ]; then | |
| echo "::error::No Chart.yaml found at $CHART_DIR" | |
| exit 1 | |
| fi | |
| file_version=$(helm show chart "$CHART_DIR" | awk '/^version:/ { print $2 }') | |
| chart_version="${CHART_VERSION:-$file_version}" | |
| if [ -z "$chart_version" ]; then | |
| echo "::error::Could not resolve chart version" | |
| exit 1 | |
| fi | |
| # On tag pushes the tag version must match Chart.yaml (release-please keeps them in sync). | |
| if [ -n "$CHART_VERSION" ] && [ "$CHART_VERSION" != "$file_version" ]; then | |
| echo "::warning::Tag/input version ($CHART_VERSION) differs from Chart.yaml ($file_version)" | |
| fi | |
| { | |
| echo "chart_dir=$CHART_DIR" | |
| echo "chart_name=$CHART_NAME" | |
| echo "chart_version=$chart_version" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Install ORAS | |
| if: steps.ctx.outputs.dry_run != 'true' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| curl -fsSLO "https://github.com/oras-project/oras/releases/download/v${ORAS_VERSION}/oras_${ORAS_VERSION}_linux_amd64.tar.gz" | |
| mkdir -p oras-install/ | |
| tar -zxf oras_*.tar.gz -C oras-install/ | |
| sudo mv oras-install/oras /usr/local/bin/ | |
| rm -rf oras_*.tar.gz oras-install/ | |
| oras version | |
| - name: Install Cosign | |
| if: steps.ctx.outputs.dry_run != 'true' | |
| uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 | |
| - name: Generate GitHub App Token | |
| id: app-token | |
| if: steps.ctx.outputs.dry_run != 'true' | |
| uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3 | |
| with: | |
| client-id: ${{ secrets.SLYBASE_APP_ID }} | |
| private-key: ${{ secrets.SLYBASE_APP_PRIVATE_KEY }} | |
| owner: SlyBase | |
| continue-on-error: true | |
| - name: Resolve registry authentication | |
| id: registry-auth | |
| if: steps.ctx.outputs.dry_run != 'true' | |
| shell: bash | |
| env: | |
| APP_TOKEN: ${{ steps.app-token.outputs.token }} | |
| FALLBACK_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| # GitHub App installation tokens cannot write to organization-owned | |
| # GHCR packages regardless of the App's declared permissions or the | |
| # package's "Manage Actions access" settings — this is a platform | |
| # limitation, not a config gap (see | |
| # https://github.com/orgs/community/discussions/78090 and | |
| # https://github.com/orgs/community/discussions/50180). Always use | |
| # GITHUB_TOKEN for the OCI push; keep APP_TOKEN generation only for | |
| # whatever else in this workflow may still need App identity. | |
| if [ -n "$FALLBACK_TOKEN" ]; then | |
| selected_token="$FALLBACK_TOKEN" | |
| token_source="github" | |
| else | |
| selected_token="$APP_TOKEN" | |
| token_source="app" | |
| fi | |
| echo "::add-mask::$selected_token" | |
| { | |
| echo "token=$selected_token" | |
| echo "source=$token_source" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Login to GitHub Container Registry | |
| if: steps.ctx.outputs.dry_run != 'true' | |
| uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ steps.registry-auth.outputs.token }} | |
| - name: Package chart | |
| id: package | |
| shell: bash | |
| env: | |
| CHART_DIR: ${{ steps.chart.outputs.chart_dir }} | |
| CHART_NAME: ${{ steps.chart.outputs.chart_name }} | |
| CHART_VERSION: ${{ steps.chart.outputs.chart_version }} | |
| run: | | |
| set -euo pipefail | |
| echo "Packaging chart $CHART_NAME version $CHART_VERSION" | |
| mkdir -p packages | |
| helm dependency build "$CHART_DIR" | |
| helm package "$CHART_DIR" --destination ./packages | |
| echo "package_file=./packages/${CHART_NAME}-${CHART_VERSION}.tgz" >> "$GITHUB_OUTPUT" | |
| ls -la ./packages | |
| - name: Install Syft | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin "v${SYFT_VERSION}" | |
| syft version | |
| - name: Generate chart SBOM | |
| id: sbom | |
| shell: bash | |
| env: | |
| CHART_NAME: ${{ steps.chart.outputs.chart_name }} | |
| CHART_VERSION: ${{ steps.chart.outputs.chart_version }} | |
| PACKAGE_FILE: ${{ steps.package.outputs.package_file }} | |
| run: | | |
| set -euo pipefail | |
| mkdir -p sbom | |
| sbom_file="sbom/${CHART_NAME}-${CHART_VERSION}.spdx.json" | |
| syft "$PACKAGE_FILE" -o "spdx-json=${sbom_file}" | |
| echo "sbom_file=${sbom_file}" >> "$GITHUB_OUTPUT" | |
| - name: Resolve referenced images | |
| id: images | |
| shell: bash | |
| env: | |
| CHART_DIR: ${{ steps.chart.outputs.chart_dir }} | |
| run: | | |
| set -euo pipefail | |
| python3 - "$CHART_DIR/Chart.yaml" > images.txt <<'EOF' | |
| import sys | |
| import yaml | |
| with open(sys.argv[1]) as f: | |
| chart = yaml.safe_load(f) | |
| images_yaml = (chart.get("annotations") or {}).get("artifacthub.io/images", "") | |
| for image in yaml.safe_load(images_yaml) or []: | |
| print(image["image"]) | |
| EOF | |
| echo "Referenced images:" | |
| cat images.txt | |
| - name: Install Trivy | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| curl -sSfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin "v${TRIVY_VERSION}" | |
| trivy version | |
| # Upstream images are not built by us, so CRITICAL findings only warn — | |
| # they must not block publishing the chart. | |
| - name: Trivy image scan (referenced images, non-blocking) | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| while IFS= read -r image; do | |
| [ -z "$image" ] && continue | |
| echo "::group::trivy image scan: ${image}" | |
| rc=0 | |
| trivy image --severity CRITICAL --exit-code 1 --ignore-unfixed --scanners vuln "$image" || rc=$? | |
| echo "::endgroup::" | |
| if [ "$rc" -ne 0 ]; then | |
| echo "::warning title=Trivy CRITICAL findings::${image} has CRITICAL vulnerabilities (upstream image, release not blocked)" | |
| echo "⚠️ Trivy found CRITICAL vulnerabilities in \`${image}\` (upstream image, release not blocked)" >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| done < images.txt | |
| - name: Dry run summary | |
| if: steps.ctx.outputs.dry_run == 'true' | |
| shell: bash | |
| env: | |
| CHART_NAME: ${{ steps.chart.outputs.chart_name }} | |
| CHART_VERSION: ${{ steps.chart.outputs.chart_version }} | |
| SBOM_FILE: ${{ steps.sbom.outputs.sbom_file }} | |
| run: | | |
| echo "🧪 DRY RUN — chart packaged but NOT published." | |
| echo "Would push: oci://ghcr.io/slybase/charts/${CHART_NAME}:${CHART_VERSION}" | |
| echo "Would sign + attach SBOM (${SBOM_FILE}) + attest provenance + upload ArtifactHub metadata." | |
| echo "Set RELEASE_ENABLED=true (tag push) or dry_run=false (manual) to publish." | |
| - name: Push chart to OCI registry | |
| if: steps.ctx.outputs.dry_run != 'true' | |
| id: push | |
| shell: bash | |
| env: | |
| CHART_NAME: ${{ steps.chart.outputs.chart_name }} | |
| CHART_VERSION: ${{ steps.chart.outputs.chart_version }} | |
| PACKAGE_FILE: ${{ steps.package.outputs.package_file }} | |
| run: | | |
| set -euo pipefail | |
| echo "Pushing to OCI registry: oci://ghcr.io/slybase/charts" | |
| echo "Chart file: $PACKAGE_FILE" | |
| set -x | |
| push_output=$(helm push "$PACKAGE_FILE" "oci://ghcr.io/slybase/charts" 2>&1) || { | |
| echo "Helm push failed with exit code $?" | |
| echo "$push_output" | |
| exit 1 | |
| } | |
| set +x | |
| echo "$push_output" | |
| digest=$(echo "$push_output" | grep -o 'sha256:[a-f0-9]\{64\}' | head -1) | |
| if [ -z "$digest" ]; then | |
| echo "Failed to extract digest from push output" | |
| exit 1 | |
| fi | |
| chart_ref="ghcr.io/slybase/charts/${CHART_NAME}@${digest}" | |
| { | |
| echo "chart_digest=$digest" | |
| echo "chart_ref=$chart_ref" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Sign chart with Cosign | |
| if: steps.ctx.outputs.dry_run != 'true' && steps.push.outputs.chart_ref != '' | |
| run: | | |
| chart_ref="${{ steps.push.outputs.chart_ref }}" | |
| cosign sign --yes "$chart_ref" | |
| - name: Verify chart signature | |
| if: steps.ctx.outputs.dry_run != 'true' && steps.push.outputs.chart_ref != '' | |
| run: | | |
| chart_ref="${{ steps.push.outputs.chart_ref }}" | |
| cosign verify "$chart_ref" \ | |
| --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \ | |
| --certificate-identity-regexp "^https://github.com/${{ github.repository }}/.github/workflows/oci-release.yaml@refs/(tags|heads)/.+$" | |
| - name: Attach chart SBOM as OCI referrer | |
| if: steps.ctx.outputs.dry_run != 'true' && steps.push.outputs.chart_ref != '' | |
| shell: bash | |
| env: | |
| SBOM_FILE: ${{ steps.sbom.outputs.sbom_file }} | |
| run: | | |
| set -euo pipefail | |
| chart_ref="${{ steps.push.outputs.chart_ref }}" | |
| oras attach --artifact-type "application/spdx+json" \ | |
| "$chart_ref" \ | |
| "${SBOM_FILE}:application/spdx+json" | |
| - name: Generate provenance attestation for chart artifact | |
| if: steps.ctx.outputs.dry_run != 'true' && steps.push.outputs.chart_ref != '' | |
| uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 | |
| with: | |
| subject-name: ghcr.io/slybase/charts/${{ steps.chart.outputs.chart_name }} | |
| subject-digest: ${{ steps.push.outputs.chart_digest }} | |
| push-to-registry: true | |
| - name: Output chart information | |
| if: steps.ctx.outputs.dry_run != 'true' | |
| run: | | |
| chart_name="${{ steps.chart.outputs.chart_name }}" | |
| chart_version="${{ steps.chart.outputs.chart_version }}" | |
| chart_ref="${{ steps.push.outputs.chart_ref }}" | |
| echo "📦 Chart: $chart_name" | |
| echo "🏷️ Version: $chart_version" | |
| echo "📍 Reference: $chart_ref" | |
| echo "✅ Signed and verified with Cosign" | |
| echo "✅ SBOM attached (application/spdx+json) and provenance attested" | |
| echo "" | |
| echo "To use this chart with Helm:" | |
| echo "helm install my-release oci://ghcr.io/slybase/charts/$chart_name --version $chart_version" | |
| - name: Upload ArtifactHub metadata for this chart | |
| if: steps.ctx.outputs.dry_run != 'true' | |
| shell: bash | |
| env: | |
| CHART_NAME: ${{ steps.chart.outputs.chart_name }} | |
| run: | | |
| set -euo pipefail | |
| if [ -f "charts/$CHART_NAME/artifacthub-repo.yml" ]; then | |
| metadata_file="charts/$CHART_NAME/artifacthub-repo.yml" | |
| else | |
| metadata_file="artifacthub-repo.yml" | |
| fi | |
| tmp_config="$(mktemp)" | |
| tmp_manifest="$(mktemp)" | |
| cleanup() { | |
| rm -f "$tmp_config" "$tmp_manifest" | |
| } | |
| trap cleanup EXIT | |
| printf '{}' > "$tmp_config" | |
| oras push \ | |
| "ghcr.io/slybase/charts/$CHART_NAME:artifacthub.io" \ | |
| --config "$tmp_config:application/vnd.cncf.artifacthub.config.v1+yaml" \ | |
| "$metadata_file:application/vnd.cncf.artifacthub.repository-metadata.layer.v1.yaml" | |
| oras manifest fetch "ghcr.io/slybase/charts/$CHART_NAME:artifacthub.io" > "$tmp_manifest" | |
| jq -e '.config.size > 0 and .config.mediaType == "application/vnd.cncf.artifacthub.config.v1+yaml" and (.layers | length) == 1' "$tmp_manifest" | |
| # Only source of "a release actually shipped" notification: pr-auto-assign.yml | |
| # deliberately does not assign release-please PRs, so without this nobody | |
| # would hear about a completed release bump. | |
| - name: Notify merged release PR | |
| if: steps.ctx.outputs.dry_run != 'true' | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| CHART_NAME: ${{ steps.chart.outputs.chart_name }} | |
| CHART_VERSION: ${{ steps.chart.outputs.chart_version }} | |
| run: | | |
| set -euo pipefail | |
| branch="release-please--branches--main--components--${CHART_NAME}" | |
| pr=$(gh pr list --state merged --search "head:${branch}" \ | |
| --json number,mergedAt --limit 1 --jq 'sort_by(.mergedAt) | reverse | .[0].number // empty') | |
| if [ -z "$pr" ]; then | |
| echo "::warning::could not find merged release PR for $branch; skipping notify" | |
| exit 0 | |
| fi | |
| gh pr comment "$pr" --body "🚀 @slydlake Released **${CHART_NAME} v${CHART_VERSION}** to \`ghcr.io/slybase/charts\`." |