fix: test coverage gaps, data quality alerts, dashboard panels, and S… #109
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 | |
| on: | |
| push: | |
| branches: [main] | |
| workflow_dispatch: {} | |
| concurrency: | |
| group: release-${{ github.ref }} | |
| cancel-in-progress: false | |
| permissions: | |
| contents: read | |
| env: | |
| GO_VERSION: "1.26" | |
| 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 | |
| if: 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: | |
| app-id: ${{ secrets.APP_ID }} | |
| private-key: ${{ secrets.APP_PRIVATE_KEY }} | |
| - uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 # v4 | |
| 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, OR on manual workflow_dispatch | |
| # with a tag ref. 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/'))) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| environment: release | |
| permissions: | |
| attestations: write | |
| contents: write | |
| packages: write | |
| id-token: write | |
| outputs: | |
| hashes: ${{ steps.hash.outputs.hashes }} | |
| 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 workflow_dispatch: use the ref. | |
| TAG="${{ needs.release-please.outputs.tag_name || github.ref_name }}" | |
| echo "name=${TAG}" >> "$GITHUB_OUTPUT" | |
| echo "Releasing: ${TAG}" | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ steps.tag.outputs.name }} | |
| - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| cache: true | |
| - name: Login to GHCR | |
| uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Login to Docker Hub | |
| uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4 | |
| - name: Capture build date | |
| 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 | |
| echo "Deleting existing asset: $asset" | |
| gh release delete-asset "$TAG" "$asset" -y || true | |
| done | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Run GoReleaser | |
| uses: goreleaser/goreleaser-action@5daf1e915a5f0af01ddbcd89a43b8061ff4f1a89 # v7.2.2 | |
| with: | |
| version: "~> v2" | |
| args: release --clean | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Generate provenance subjects | |
| id: hash | |
| shell: bash -Eeuo pipefail {0} | |
| run: | | |
| echo "hashes=$(cat goreleaser-dist/checksums.txt | base64 -w0)" >> "$GITHUB_OUTPUT" | |
| - name: Build and push multi-arch image | |
| uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 | |
| id: build | |
| with: | |
| context: . | |
| platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/ppc64le,linux/s390x | |
| push: true | |
| tags: | | |
| ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.tag.outputs.name }} | |
| ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest | |
| docker.io/attuneio/attune:${{ steps.tag.outputs.name }} | |
| docker.io/attuneio/attune:latest | |
| build-args: | | |
| VERSION=${{ steps.tag.outputs.name }} | |
| COMMIT=${{ github.sha }} | |
| DATE=${{ steps.build_date.outputs.date }} | |
| 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: Install cosign | |
| uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 | |
| - name: Sign GHCR image | |
| shell: bash -Eeuo pipefail -x {0} | |
| run: | | |
| cosign sign --yes \ | |
| ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }} | |
| - name: Sign Docker Hub image | |
| shell: bash -Eeuo pipefail -x {0} | |
| run: | | |
| cosign sign --yes \ | |
| docker.io/attuneio/attune@${{ steps.build.outputs.digest }} | |
| - name: Attest container image | |
| uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2 | |
| with: | |
| subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} | |
| subject-digest: ${{ steps.build.outputs.digest }} | |
| push-to-registry: true | |
| - name: Generate SBOM | |
| 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 | |
| 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: Generate install manifest and CRDs bundle | |
| 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 | |
| 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 | |
| # 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 | |
| continue-on-error: true | |
| uses: rajatjindal/krew-release-bot@c970b8a8f6dbc2f2285a26e3ae160903b87002c3 # v0.0.51 | |
| with: | |
| krew_plugin_release_tag: ${{ steps.tag.outputs.name }} | |
| 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. | |
| if: ${{ !cancelled() && needs.release.result == 'success' }} | |
| 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 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: Login to Docker Hub (Helm) | |
| shell: bash -Eeuo pipefail -x {0} | |
| run: | | |
| echo "${{ secrets.DOCKERHUB_TOKEN }}" | helm registry login registry-1.docker.io \ | |
| --username ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin | |
| - 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 | |
| helm push attune-${VERSION}.tgz oci://registry-1.docker.io/attuneio | |
| - 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:${VERSION} | |
| - name: Push Artifact Hub metadata | |
| shell: bash -Eeuo pipefail -x {0} | |
| run: | | |
| # Install ORAS CLI | |
| ORAS_VERSION="1.3.2" | |
| curl -sLO "https://github.com/oras-project/oras/releases/download/v${ORAS_VERSION}/oras_${ORAS_VERSION}_linux_amd64.tar.gz" | |
| tar -xzf "oras_${ORAS_VERSION}_linux_amd64.tar.gz" oras | |
| chmod +x oras | |
| # 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 | |
| # SLSA Level 3 provenance for GoReleaser binary artifacts. | |
| # Runs as a reusable workflow (required for non-forgeable provenance). | |
| provenance: | |
| name: Binary Provenance | |
| needs: [release] | |
| if: ${{ !cancelled() && needs.release.result == 'success' }} | |
| permissions: | |
| actions: read | |
| id-token: write | |
| contents: write | |
| # NOTE: SLSA reusable workflows require tag refs (not SHA pins) for | |
| # self-verification of the builder binary. | |
| uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0 | |
| with: | |
| base64-subjects: "${{ needs.release.outputs.hashes }}" | |
| upload-assets: true | |
| # SLSA Level 3 provenance for the container image. | |
| container-provenance: | |
| name: Container Provenance | |
| needs: [release] | |
| if: ${{ !cancelled() && needs.release.result == 'success' }} | |
| permissions: | |
| actions: read | |
| id-token: write | |
| packages: write | |
| # NOTE: SLSA reusable workflows require tag refs (not SHA pins) for | |
| # self-verification of the builder binary. | |
| uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0 | |
| with: | |
| image: ghcr.io/${{ github.repository }} | |
| digest: ${{ needs.release.outputs.digest }} | |
| registry-username: ${{ github.actor }} | |
| secrets: | |
| registry-password: ${{ secrets.GITHUB_TOKEN }} | |
| # Submit an OLM bundle PR to k8s-operatorhub/community-operators. | |
| # Uses continue-on-error so OperatorHub failures never block releases. | |
| operatorhub-pr: | |
| name: OperatorHub PR | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| needs: [release] | |
| if: ${{ !cancelled() && needs.release.result == 'success' }} | |
| continue-on-error: true | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 | |
| with: | |
| ref: ${{ needs.release.outputs.tag }} | |
| - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| cache: true | |
| - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 | |
| id: app-token | |
| with: | |
| app-id: ${{ secrets.APP_ID }} | |
| private-key: ${{ secrets.APP_PRIVATE_KEY }} | |
| owner: attune-io | |
| repositories: community-operators | |
| - name: Generate OLM bundle and create PR | |
| env: | |
| VERSION: ${{ needs.release.outputs.tag }} | |
| GH_TOKEN: ${{ steps.app-token.outputs.token }} | |
| 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] | |
| if: ${{ !cancelled() && needs.release.result == 'success' }} | |
| steps: | |
| - uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4 | |
| with: | |
| egress-policy: audit | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 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." |