diff --git a/.github/workflows/build-and-release.yaml b/.github/workflows/build-and-release.yaml index e6885554..7eb5bf56 100644 --- a/.github/workflows/build-and-release.yaml +++ b/.github/workflows/build-and-release.yaml @@ -22,24 +22,67 @@ on: permissions: contents: write packages: write + security-events: write jobs: + scan-dependencies: + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + + - uses: actions/dependency-review-action@56339e523c0409420f6c2c9a2f4292bbb3c07dd3 # v4.8.0 + # this action requires a base and head + # ensure that trunk changes require pull request checks to pass + if: ${{ github.event_name == 'pull_request' }} + + scan-codeql: + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + + - uses: github/codeql-action/init@e296a935590eb16afc0c0108289f68c87e2a89a5 # v4.30.7 + with: + languages: actions,go + - uses: github/codeql-action/analyze@e296a935590eb16afc0c0108289f68c87e2a89a5 # v4.30.7 + with: + output: codeql-sarif + - name: Check success or failure + # github/codeql-action/analyze doesn't fail the pipeline when it finds vulnerabilities + run: | + set -euo pipefail + shopt -s nullglob + files=(codeql-sarif/*.sarif) + if [ ${#files[@]} -eq 0 ]; then + echo "No SARIF files produced by CodeQL analyze." + exit 1 + fi + # Count non-note results across all SARIF files + count="$(jq -s 'map(.runs[].results // []) | flatten | map(select(.level != "note")) | length' "${files[@]}")" + echo "Non-note CodeQL results: ${count}" + if [ "${count}" -gt 0 ]; then + echo "::error::CodeQL produced ${count} alerts (warning/error)." + exit 1 + fi + build-go: + needs: [ scan-dependencies, scan-codeql ] runs-on: ubuntu-latest strategy: matrix: arch: ${{ fromJson(inputs.architectures) }} steps: - name: Check out code - uses: actions/checkout@v5 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Install Go - uses: actions/setup-go@v5 + uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0 with: go-version-file: go.mod - name: Run linter - uses: golangci/golangci-lint-action@v8 + uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0 - name: Run tests run: |- @@ -64,73 +107,110 @@ jobs: ./cmd/multigres-operator - name: Upload artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: multigres-operator-${{matrix.arch}} path: dist/* + if-no-files-found: error + retention-days: 7 - build-push-container: + build-scan-push-container: needs: [ build-go ] runs-on: ubuntu-latest steps: - name: Check out code - uses: actions/checkout@v5 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0 + + - name: Set up Docker for multi-platform + uses: docker/setup-docker-action@b60f85385d03ac8acfca6d9996982511d8620a19 # v4.3.0 + with: + daemon-config: | + { + "features": { + "containerd-snapshotter": true + } + } - name: Setup Docker buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 + + - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + with: + pattern: multigres-operator-* + path: dist/ - name: Log into registry - uses: docker/login-action@v3 + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract container metadata - id: meta - uses: docker/metadata-action@v5 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - images: ghcr.io/${{ github.repository }} - tags: | - type=ref,event=branch,prefix= - type=ref,event=tag,prefix= - type=sha,format=short,prefix= - type=sha,format=long,prefix= - - - uses: actions/download-artifact@v5 - with: - pattern: multigres-operator-* - path: dist/ - - - name: Build and push container image - id: build-and-push - uses: docker/build-push-action@v5 + - name: Build container image + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 with: context: . file: Containerfile platforms: linux/${{ join(fromJson(inputs.architectures), ',linux/') }} - push: ${{ inputs.push-container-image }} - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} + load: true + push: false + tags: "ghcr.io/${{ github.repository }}:${{ github.sha }}" provenance: false cache-from: type=gha cache-to: type=gha,mode=max + outputs: type=oci,dest=container-image.tar + + - name: Push to registry (sha) + run: | + IMAGE="ghcr.io/${{ github.repository }}" + docker push "$IMAGE:${{ github.sha }}" + + # grype requires that the container image be pushed already because + # the scanner runs in a container with a different local registry + - name: Scan image with grype + id: scan + uses: anchore/scan-action@f6601287cdb1efc985d6b765bbf99cb4c0ac29d8 # v7.0.0 + continue-on-error: true + with: + cache-db: true + image: "ghcr.io/${{ github.repository }}:${{ github.sha }}" + output-file: grype.sarif + severity-cutoff: critical # TODO: lower this once vulns are fixed + - name: Upload SARIF file + uses: github/codeql-action/upload-sarif@e296a935590eb16afc0c0108289f68c87e2a89a5 # v4.30.7 + with: + sarif_file: grype.sarif + - name: Check success or failure + if: ${{ steps.scan.outcome == 'failure' }} + run: exit 1 + + - name: Push to registry (proper) + if: ${{ inputs.push-container-image }} + run: | + IMAGE="ghcr.io/${{ github.repository }}" + if [ "${{ github.ref }}" = "refs/heads/main" ]; then + docker tag "$IMAGE:${{ github.sha }}" "$IMAGE:latest" + docker push "$IMAGE:latest" + fi + if [ "${{ github.ref_type }}" = "tag" ]; then + docker tag "$IMAGE:${{ github.sha }}" "$IMAGE:${{ github.ref_name }}" + docker push "$IMAGE:${{ github.ref_name }}" + fi create-release: - needs: [ build-go ] + needs: [ build-scan-push-container ] runs-on: ubuntu-latest if: ${{ inputs.create-release }} steps: - - uses: actions/download-artifact@v5 + - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 with: pattern: "*" path: dist/ - name: Release - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@aec2ec56f94eb8180ceec724245f64ef008b89f5 # 2.4.0 with: files: dist/** \ No newline at end of file diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 6d0254ac..2090b376 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -8,6 +8,7 @@ on: permissions: contents: write packages: write + security-events: write jobs: run: diff --git a/.github/workflows/pull-request.yaml b/.github/workflows/pull-request.yaml index c0438db6..e8ef1d26 100644 --- a/.github/workflows/pull-request.yaml +++ b/.github/workflows/pull-request.yaml @@ -6,6 +6,7 @@ on: permissions: contents: write packages: write + security-events: write jobs: run: diff --git a/.github/workflows/tags.yaml b/.github/workflows/tags.yaml index 5c73186c..7f42afde 100644 --- a/.github/workflows/tags.yaml +++ b/.github/workflows/tags.yaml @@ -8,6 +8,7 @@ on: permissions: contents: write packages: write + security-events: write jobs: run: