diff --git a/.github/workflows/coverage-go.yml b/.github/workflows/coverage-go.yml new file mode 100644 index 0000000..517d731 --- /dev/null +++ b/.github/workflows/coverage-go.yml @@ -0,0 +1,68 @@ +# SPDX-FileCopyrightText: 2025 OpenCHAMI a Series of LF Projects, LLC +# SPDX-License-Identifier: MIT + +name: coverage-go + +# Reusable workflow that produces a Go coverage profile, writes the total +# to the job summary, and uploads the profile to Coveralls. +# +# The caller repo must be enrolled in Coveralls; the upload authenticates +# with the automatically-provided GITHUB_TOKEN. + +on: + workflow_call: + inputs: + go_version: + description: 'Go version to use (e.g. "1.26.7", "stable"). Mutually exclusive with go_version_file.' + required: false + type: string + go_version_file: + description: 'Path to a go.mod or .go-version file containing the Go version. Mutually exclusive with go_version.' + required: false + type: string + coverage-command: + description: 'Command that writes the coverage profile to `coverage-file`' + required: false + type: string + default: 'go test -coverprofile=coverage.out ./...' + coverage-file: + description: 'Path to the coverage profile written by `coverage-command`' + required: false + type: string + default: 'coverage.out' + +permissions: + contents: read # baseline for checkout + +jobs: + coverage-go: + name: coverage-go + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version: ${{ inputs.go_version || (inputs.go_version_file == '' && 'stable' || '') }} + go-version-file: ${{ inputs.go_version_file || '' }} + + - name: Run tests with coverage + env: + COVERAGE_COMMAND: ${{ inputs.coverage-command }} + run: eval "$COVERAGE_COMMAND" + + - name: Report total coverage + env: + COVERAGE_FILE: ${{ inputs.coverage-file }} + run: | + total=$(go tool cover -func="$COVERAGE_FILE" | awk '/^total:/ {print $3}') + echo "Total coverage: ${total}" | tee -a "$GITHUB_STEP_SUMMARY" + + - name: Upload coverage to Coveralls + uses: coverallsapp/github-action@8d6379e14d29928660c4ba802d8e85393440b329 # v2.3.8 + with: + file: ${{ inputs.coverage-file }} + format: golang + github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/lint-go.yml b/.github/workflows/lint-go.yml new file mode 100644 index 0000000..b7534dc --- /dev/null +++ b/.github/workflows/lint-go.yml @@ -0,0 +1,62 @@ +# SPDX-FileCopyrightText: 2025 OpenCHAMI a Series of LF Projects, LLC +# SPDX-License-Identifier: MIT + +name: lint-go + +# Reusable workflow that lints the caller's Go module. Runs golangci-lint, +# and separately verifies go.mod/go.sum are tidy by running the tidy +# command and failing on a dirty diff. +# +# Pair with test-go for the unit-test half of the Go CI wave. + +on: + workflow_call: + inputs: + go_version: + description: 'Go version to use (e.g. "1.26.7", "stable"). Mutually exclusive with go_version_file.' + required: false + type: string + go_version_file: + description: 'Path to a go.mod or .go-version file containing the Go version. Mutually exclusive with go_version.' + required: false + type: string + golangci-lint-version: + description: 'golangci-lint release to run' + required: false + type: string + default: 'latest' + +permissions: + contents: read # baseline for checkout + +jobs: + golangci-lint: + name: golangci-lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version: ${{ inputs.go_version || (inputs.go_version_file == '' && 'stable' || '') }} + go-version-file: ${{ inputs.go_version_file || '' }} + - uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9.3.0 + with: + version: ${{ inputs.golangci-lint-version }} + + modules: + name: modules + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version: ${{ inputs.go_version || (inputs.go_version_file == '' && 'stable' || '') }} + go-version-file: ${{ inputs.go_version_file || '' }} + - name: Check go.mod and go.sum are tidy + run: | + go mod tidy + git diff --exit-code -- go.mod go.sum diff --git a/.github/workflows/reuse.yml b/.github/workflows/reuse.yml new file mode 100644 index 0000000..33a6712 --- /dev/null +++ b/.github/workflows/reuse.yml @@ -0,0 +1,34 @@ +# SPDX-FileCopyrightText: 2025 OpenCHAMI a Series of LF Projects, LLC +# SPDX-License-Identifier: MIT + +name: reuse + +# Reusable workflow that checks the caller repo for REUSE compliance — +# every file carries a copyright notice and an SPDX license identifier, +# and every referenced license is present under LICENSES/. + +on: + workflow_call: + inputs: + reuse_version: + type: string + required: false + default: '6.2.0' + description: 'Reuse CLI version to use (e.g. "6.2.0").' + +permissions: + contents: read # baseline for checkout + +jobs: + reuse: + name: reuse + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + - name: REUSE compliance check + env: + REUSE_VERSION: ${{ inputs.reuse_version }} + run: pipx run --backend pip --spec "reuse==$REUSE_VERSION" reuse lint --lines diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 0000000..3599233 --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,51 @@ +# SPDX-FileCopyrightText: 2025 OpenCHAMI a Series of LF Projects, LLC +# SPDX-License-Identifier: MIT + +name: scorecard + +# Reusable workflow that runs the OpenSSF Scorecard supply-chain analysis +# over the caller repo and uploads the SARIF to GitHub Advanced Security. +# +# On anything other than a pull_request the results are also published to +# the public OpenSSF dashboard, which is what backs the Scorecard badge. +# Callers should trigger this on the default branch, on pull_request, and +# on a schedule; running it on other branches scores an incomplete tree. + +on: + workflow_call: + +permissions: + contents: read # baseline for checkout + security-events: write # for SARIF upload to GHAS + id-token: write # for publishing results to the OpenSSF dashboard + +jobs: + scorecard: + name: scorecard + # Scorecard is a container action and needs a full-fat runner. + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + + - name: Run Scorecard analysis + uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4 + with: + results_file: results.sarif + results_format: sarif + publish_results: ${{ github.event_name != 'pull_request' }} + + - name: Upload SARIF artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: scorecard-sarif + path: results.sarif + retention-days: 5 + + - name: Upload SARIF to GHAS + if: github.event_name != 'pull_request' + uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 + with: + sarif_file: results.sarif + category: scorecard diff --git a/.github/workflows/test-go.yml b/.github/workflows/test-go.yml new file mode 100644 index 0000000..d30dd34 --- /dev/null +++ b/.github/workflows/test-go.yml @@ -0,0 +1,46 @@ +# SPDX-FileCopyrightText: 2025 OpenCHAMI a Series of LF Projects, LLC +# SPDX-License-Identifier: MIT + +name: test-go + +# Reusable workflow that runs the caller's Go unit tests. Fetches tags so +# tests that assert on version metadata derived from `git describe` behave +# the same as they do locally. + +on: + workflow_call: + inputs: + go_version: + description: 'Go version to use (e.g. "1.26.7", "stable"). Mutually exclusive with go_version_file.' + required: false + type: string + go_version_file: + description: 'Path to a go.mod or .go-version file containing the Go version. Mutually exclusive with go_version.' + required: false + type: string + test-command: + description: 'Command that runs the tests' + required: false + type: string + default: 'go test -race ./...' + +permissions: + contents: read # baseline for checkout + +jobs: + test-go: + name: test-go + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + fetch-tags: true + persist-credentials: false + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version: ${{ inputs.go_version || (inputs.go_version_file == '' && 'stable' || '') }} + go-version-file: ${{ inputs.go_version_file || '' }} + - name: Run tests + env: + TEST_COMMAND: ${{ inputs.test-command }} + run: eval "$TEST_COMMAND" diff --git a/README.md b/README.md index eee0089..a7bbe1b 100644 --- a/README.md +++ b/README.md @@ -23,9 +23,14 @@ Reusable GitHub Actions for CI/CD. - `.github/workflows/release-signed-artifacts.yml`: Publishes a GitHub Release with signed RPMs and public keys - `.github/workflows/publish-release.yml`: Publishes the draft GitHub Release for a tag - `.github/workflows/lint-workflows.yml`: Reusable workflow that lints workflow files (actionlint + zizmor) +- `.github/workflows/lint-go.yml`: Reusable workflow that runs golangci-lint and checks go.mod/go.sum are tidy +- `.github/workflows/test-go.yml`: Reusable workflow that runs Go unit tests +- `.github/workflows/coverage-go.yml`: Reusable workflow that reports Go coverage and uploads it to Coveralls +- `.github/workflows/reuse.yml`: Reusable workflow that checks REUSE copyright/licensing compliance - `.github/workflows/govulncheck.yml`: Reusable workflow that scans Go modules for known CVEs - `.github/workflows/dependency-review.yml`: Reusable workflow that gates PRs introducing CVE-flagged deps - `.github/workflows/trivy-image-scan.yml`: Reusable workflow that scans built container images for CVEs +- `.github/workflows/scorecard.yml`: Reusable workflow that runs the OpenSSF Scorecard supply-chain analysis - `.github/workflows/pr-registry-cleanup.yml`: Deletes the GHCR container images a PR published, once it closes ## Versioning & Usage @@ -101,6 +106,83 @@ jobs: uses: OpenCHAMI/github-actions/.github/workflows/lint-workflows.yml@v3.8 ``` +### lint-go (Reusable Workflow) +Lints the caller's Go module. Runs `golangci-lint`, and separately verifies `go.mod`/`go.sum` are tidy by running the tidy command and failing on a dirty diff. Uses Go `stable` unless the caller sets `go_version` or `go_version_file`. `golangci-lint` tracks `latest` unless pinned. + +**Usage:** +```yaml +name: Lint +on: + pull_request: + push: + branches: [main] + +jobs: + lint: + uses: OpenCHAMI/github-actions/.github/workflows/lint-go.yml@v3.9 + # Optional overrides: + # with: + # golangci-lint-version: v2.13.2 + # go_version_file: go.mod + # tidy-command: make mod +``` + +### test-go (Reusable Workflow) +Runs the caller's Go unit tests. Fetches tags so tests asserting on `git describe` version metadata behave as they do locally. + +**Usage:** +```yaml +name: Test +on: + pull_request: + push: + branches: [main] + +jobs: + test: + uses: OpenCHAMI/github-actions/.github/workflows/test-go.yml@v3.9 + # Optional overrides: + # with: + # go_version_file: go.mod + # test-command: make test +``` + +### coverage-go (Reusable Workflow) +Produces a Go coverage profile, writes the total to the job summary, and uploads the profile to Coveralls using the automatically-provided `GITHUB_TOKEN`. The caller repo must be enrolled in Coveralls. + +**Usage:** +```yaml +name: Coverage +on: + pull_request: + push: + branches: [main] + +jobs: + coverage: + uses: OpenCHAMI/github-actions/.github/workflows/coverage-go.yml@v3.9 + # Optional overrides: + # with: + # go_version_file: go.mod + # coverage-command: make coverage +``` + +### reuse (Reusable Workflow) +Runs the [`reuse`](https://reuse.software) tool over the caller repo via `pipx` to check REUSE compliance: every file carries a copyright notice and an SPDX license identifier, and every referenced license is present under `LICENSES/`. Pins `reuse` 6.2.0. + +**Usage:** +```yaml +name: REUSE +on: + pull_request: + push: + branches: [main] + +jobs: + reuse: + uses: OpenCHAMI/github-actions/.github/workflows/reuse.yml@v3.9 +``` + ### govulncheck (Reusable Workflow) Runs the Go team's vulnerability scanner against the caller's module. Detects known CVEs in the import graph (direct and transitive). Reads the Go version from the caller's `go.mod` by default. @@ -155,6 +237,30 @@ jobs: image-ref: ghcr.io/openchami/foo:${{ github.sha }} ``` +### scorecard (Reusable Workflow) +Runs the OpenSSF Scorecard supply-chain analysis and uploads SARIF findings to GitHub Advanced Security. Outside of `pull_request` runs it also publishes to the public OpenSSF dashboard, which is what backs the Scorecard badge. Trigger it on the default branch, on PRs, and on a schedule — running it on other branches scores an incomplete tree. + +**Usage:** +```yaml +name: Scorecard +on: + branch_protection_rule: + pull_request: + push: + branches: [main] + schedule: + - cron: '39 5 * * 1' + +permissions: + contents: read + security-events: write + id-token: write + +jobs: + scorecard: + uses: OpenCHAMI/github-actions/.github/workflows/scorecard.yml@v3.9 +``` + ### build-publish-container-goreleaser (Reusable Workflow) Builds and publishes a container image via GoReleaser, with multi-arch builds, build provenance attestation, and PR snapshot support. Release builds (`is_pr_build: false`) pass GitHub's auto-generated release notes for the pushed tag to GoReleaser via `--release-notes`.