From fc1468b2150e3a1b24cb1238599d10ec30a11ea7 Mon Sep 17 00:00:00 2001 From: Sean Tronsen Date: Mon, 5 Oct 2026 14:41:38 -0600 Subject: [PATCH 1/8] feat: create reusable workflow for actions/stale Signed-off-by: Sean Tronsen --- .github/workflows/stale.yml | 92 +++++++++++++++++++++++++++++++++++++ README.md | 33 +++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 .github/workflows/stale.yml diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000..adad710 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,92 @@ +# SPDX-FileCopyrightText: © 2026 OpenCHAMI a Series of LF Projects, LLC +# SPDX-License-Identifier: MIT + +name: stale + +# Reusable workflow that marks and closes inactive issues and PRs with the +# org-default actions/stale policy. Callers own the schedule (a reusable +# workflow can't schedule itself). + +on: + workflow_call: + inputs: + days-before-stale: + description: 'Days of inactivity before an issue or PR is marked stale' + required: false + type: number + default: 35 + days-before-close: + description: 'Days after being marked stale before an issue or PR is closed' + required: false + type: number + default: 7 + stale-issue-label: + description: 'Label applied to stale issues' + required: false + type: string + default: 'stale' + stale-pr-label: + description: 'Label applied to stale PRs' + required: false + type: string + default: 'stale' + exempt-issue-labels: + description: 'Comma-separated labels that exempt an issue from going stale' + required: false + type: string + default: 'pinned,security,help wanted' + exempt-pr-labels: + description: 'Comma-separated labels that exempt a PR from going stale' + required: false + type: string + default: 'pinned,security' + operations-per-run: + description: 'Maximum GitHub API operations per run' + required: false + type: number + default: 500 + stale-issue-message: + description: 'Comment posted when an issue is marked stale' + required: false + type: string + default: >- + This issue has been automatically marked as stale due to inactivity. + Please add a comment or label to keep it open; otherwise it will close in 7 days. + close-issue-message: + description: 'Comment posted when a stale issue is closed' + required: false + type: string + default: 'Closing due to inactivity. Feel free to reopen with new information.' + stale-pr-message: + description: 'Comment posted when a PR is marked stale' + required: false + type: string + default: 'This PR has been marked stale due to inactivity. Comment or push changes to keep it open.' + close-pr-message: + description: 'Comment posted when a stale PR is closed' + required: false + type: string + default: 'Closing due to inactivity. Please reopen when it’s ready again.' + +permissions: + issues: write # label, comment on, and close stale issues + pull-requests: write # label, comment on, and close stale PRs + +jobs: + stale: + runs-on: ubuntu-slim + steps: + - uses: actions/stale@4391f3da665fdf50b6810c1a66712fb9ba21aa93 # v11.0.0 + with: + days-before-stale: ${{ inputs.days-before-stale }} + days-before-close: ${{ inputs.days-before-close }} + stale-issue-label: ${{ inputs.stale-issue-label }} + stale-pr-label: ${{ inputs.stale-pr-label }} + exempt-issue-labels: ${{ inputs.exempt-issue-labels }} + exempt-pr-labels: ${{ inputs.exempt-pr-labels }} + operations-per-run: ${{ inputs.operations-per-run }} + remove-stale-when-updated: true + stale-issue-message: ${{ inputs.stale-issue-message }} + close-issue-message: ${{ inputs.close-issue-message }} + stale-pr-message: ${{ inputs.stale-pr-message }} + close-pr-message: ${{ inputs.close-pr-message }} diff --git a/README.md b/README.md index 239ff21..c848c81 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ Reusable GitHub Actions for CI/CD. - `.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 +- `.github/workflows/stale.yml`: Reusable workflow that marks and closes inactive issues and PRs (org-default policy) ### Workflow naming @@ -369,6 +370,38 @@ jobs: # tag-prefix: pr- ``` +### stale (Reusable Workflow) +Marks issues and PRs stale after a period of inactivity and closes them if nothing changes, using the org-default `actions/stale` policy (stale after 35 days, closed 7 days later; `pinned`/`security` items exempt). Updating an item removes the stale label. The caller owns the schedule. + +**Usage:** +```yaml +name: Stale +on: + schedule: + - cron: '23 3 * * *' # daily at 03:23 UTC + workflow_dispatch: + +jobs: + stale: + uses: OpenCHAMI/github-actions/.github/workflows/stale.yml@v4.0 + permissions: + issues: write + pull-requests: write + # Optional overrides: + # with: + # days-before-stale: 35 + # days-before-close: 7 + # stale-issue-label: stale + # stale-pr-label: stale + # exempt-issue-labels: pinned,security,help wanted + # exempt-pr-labels: pinned,security + # operations-per-run: 500 + # stale-issue-message: ... + # close-issue-message: ... + # stale-pr-message: ... + # close-pr-message: ... +``` + ## Actions ### gpg-configure-release-keys From 82b021c7e8af0c18c3021a2fb75d0f4dc2f0a88b Mon Sep 17 00:00:00 2001 From: Sean Tronsen Date: Mon, 5 Oct 2026 14:58:18 -0600 Subject: [PATCH 2/8] feat: add reusable workflow to check for drift with fabrica code generation Signed-off-by: Sean Tronsen --- .github/workflows/lint-codegen-fabrica.yml | 72 ++++++++++++++++++++++ README.md | 22 +++++++ 2 files changed, 94 insertions(+) create mode 100644 .github/workflows/lint-codegen-fabrica.yml diff --git a/.github/workflows/lint-codegen-fabrica.yml b/.github/workflows/lint-codegen-fabrica.yml new file mode 100644 index 0000000..50de828 --- /dev/null +++ b/.github/workflows/lint-codegen-fabrica.yml @@ -0,0 +1,72 @@ +# SPDX-FileCopyrightText: © 2026 OpenCHAMI a Series of LF Projects, LLC +# SPDX-License-Identifier: MIT + +name: lint-codegen-fabrica + +# Reusable workflow that fails when a Fabrica project's committed generated +# code has drifted, by running the caller's `make generate-check` with a +# Fabrica built from source (passed as LOCAL_FABRICA). Fabrica stamps its +# version into generated code and only a source build carries that version; +# a `go run` build reports "dev" and always fails the check. + +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 + fabrica-ref: + description: 'Fabrica tag, branch, or commit SHA to build (default: version in go.mod)' + required: false + type: string + default: '' + +permissions: + contents: read # baseline for checkout + +jobs: + lint-codegen-fabrica: + runs-on: ubuntu-slim + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 + with: + go-version: ${{ inputs.go-version || (inputs.go-version-file == '' && 'stable' || '') }} + go-version-file: ${{ inputs.go-version-file || '' }} + + - name: Resolve Fabrica ref + id: fabrica-ref + env: + FABRICA_REF: ${{ inputs.fabrica-ref }} + run: | + ref="$FABRICA_REF" + if [[ -z "$ref" ]]; then + ref=$(awk '/github.com\/openchami\/fabrica[[:space:]]+v/ {print $2; exit}' go.mod) + fi + if [[ -z "$ref" ]]; then + echo "::error::fabrica-ref not set and no github.com/openchami/fabrica version found in go.mod" + exit 1 + fi + echo "Using Fabrica ref: ${ref}" + echo "ref=${ref}" >> "$GITHUB_OUTPUT" + - name: Checkout Fabrica + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: openchami/fabrica + ref: ${{ steps.fabrica-ref.outputs.ref }} + path: fabrica + fetch-depth: 0 # full history + tags so `git describe` stamps the same version as a local build + persist-credentials: false + - name: Build Fabrica + working-directory: fabrica + run: make build + + - name: Verify generated code is committed + run: make generate-check LOCAL_FABRICA="$GITHUB_WORKSPACE/fabrica" diff --git a/README.md b/README.md index c848c81..f4a53f4 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ Reusable GitHub Actions for CI/CD. - `.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 - `.github/workflows/stale.yml`: Reusable workflow that marks and closes inactive issues and PRs (org-default policy) +- `.github/workflows/lint-codegen-fabrica.yml`: Reusable workflow that fails when a Fabrica project's committed generated code is out of date ### Workflow naming @@ -402,6 +403,27 @@ jobs: # close-pr-message: ... ``` +### lint-codegen-fabrica (Reusable Workflow) +Fails when a Fabrica project's committed generated code is out of date by running the caller's `make generate-check` with Fabrica built from source (passed as `LOCAL_FABRICA`), at the version pinned in `go.mod` unless `fabrica-ref` (tag, branch, or commit SHA) is set. A source build is required: Fabrica stamps its version into generated code, and a `go run` build reports `dev` instead. + +**Usage:** +```yaml +name: Lint Codegen +on: + pull_request: + branches: [main] + workflow_dispatch: + +jobs: + lint-codegen: + uses: OpenCHAMI/github-actions/.github/workflows/lint-codegen-fabrica.yml@v4.0 + # Optional overrides: + # with: + # go-version: stable # mutually exclusive with go-version-file + # go-version-file: go.mod + # fabrica-ref: v0.4.5 # tag, branch, or SHA; default: version in go.mod +``` + ## Actions ### gpg-configure-release-keys From 095221ed83f8ebd3528074fbef652ef3b574abda Mon Sep 17 00:00:00 2001 From: Sean Tronsen Date: Thu, 8 Oct 2026 10:56:24 -0600 Subject: [PATCH 3/8] fix: force fabrica regeneration Signed-off-by: Sean Tronsen --- .github/workflows/lint-codegen-fabrica.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint-codegen-fabrica.yml b/.github/workflows/lint-codegen-fabrica.yml index 50de828..233c167 100644 --- a/.github/workflows/lint-codegen-fabrica.yml +++ b/.github/workflows/lint-codegen-fabrica.yml @@ -67,6 +67,8 @@ jobs: - name: Build Fabrica working-directory: fabrica run: make build - - name: Verify generated code is committed - run: make generate-check LOCAL_FABRICA="$GITHUB_WORKSPACE/fabrica" + run: | + make generate-check \ + LOCAL_FABRICA="$GITHUB_WORKSPACE/fabrica" \ + FABRICA_SOURCE_ARG="--fabrica-source $GITHUB_WORKSPACE/fabrica --force" From c98a8172bb26c084b6e2199392a2f707f2613ba5 Mon Sep 17 00:00:00 2001 From: Sean Tronsen Date: Thu, 8 Oct 2026 11:09:27 -0600 Subject: [PATCH 4/8] fix: switch to robust go.mod parsing Signed-off-by: Sean Tronsen --- .github/workflows/lint-codegen-fabrica.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/lint-codegen-fabrica.yml b/.github/workflows/lint-codegen-fabrica.yml index 233c167..a67f5ce 100644 --- a/.github/workflows/lint-codegen-fabrica.yml +++ b/.github/workflows/lint-codegen-fabrica.yml @@ -48,7 +48,11 @@ jobs: run: | ref="$FABRICA_REF" if [[ -z "$ref" ]]; then - ref=$(awk '/github.com\/openchami\/fabrica[[:space:]]+v/ {print $2; exit}' go.mod) + ref=$(go mod edit -json | jq -r ' + .Require[]? + | select(.Path == "github.com/openchami/fabrica") + | .Version + ') fi if [[ -z "$ref" ]]; then echo "::error::fabrica-ref not set and no github.com/openchami/fabrica version found in go.mod" From e9cb5c9a801ff6570632f78bf2c81b4930ba37a5 Mon Sep 17 00:00:00 2001 From: Sean Tronsen Date: Thu, 8 Oct 2026 12:02:59 -0600 Subject: [PATCH 5/8] fix: update fabrica ref selector to handle go pseudo-versioning Signed-off-by: Sean Tronsen --- .github/workflows/lint-codegen-fabrica.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/lint-codegen-fabrica.yml b/.github/workflows/lint-codegen-fabrica.yml index a67f5ce..b3d5b47 100644 --- a/.github/workflows/lint-codegen-fabrica.yml +++ b/.github/workflows/lint-codegen-fabrica.yml @@ -48,15 +48,17 @@ jobs: run: | ref="$FABRICA_REF" if [[ -z "$ref" ]]; then - ref=$(go mod edit -json | jq -r ' + version=$(go mod edit -json | jq -r ' .Require[]? | select(.Path == "github.com/openchami/fabrica") | .Version ') - fi - if [[ -z "$ref" ]]; then - echo "::error::fabrica-ref not set and no github.com/openchami/fabrica version found in go.mod" - exit 1 + if [[ -z "$version" ]]; then + echo "::error::fabrica-ref not set and no github.com/openchami/fabrica version found in go.mod" + exit 1 + fi + # Resolve the tag or pseudo-version to the commit it names + ref=$(go list -mod=mod -m -json "github.com/openchami/fabrica@${version}" | jq -r '.Origin.Hash') fi echo "Using Fabrica ref: ${ref}" echo "ref=${ref}" >> "$GITHUB_OUTPUT" From c496d0898e135dafd5b49ef9271d541cc8c844ec Mon Sep 17 00:00:00 2001 From: Sean Tronsen Date: Mon, 12 Oct 2026 10:55:54 -0600 Subject: [PATCH 6/8] fix: address omitempty type for .Origin.Hash Signed-off-by: Sean Tronsen --- .github/workflows/lint-codegen-fabrica.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/lint-codegen-fabrica.yml b/.github/workflows/lint-codegen-fabrica.yml index b3d5b47..b0416a4 100644 --- a/.github/workflows/lint-codegen-fabrica.yml +++ b/.github/workflows/lint-codegen-fabrica.yml @@ -46,8 +46,8 @@ jobs: env: FABRICA_REF: ${{ inputs.fabrica-ref }} run: | - ref="$FABRICA_REF" - if [[ -z "$ref" ]]; then + version="$FABRICA_REF" + if [[ -z "$version" ]]; then version=$(go mod edit -json | jq -r ' .Require[]? | select(.Path == "github.com/openchami/fabrica") @@ -57,8 +57,12 @@ jobs: echo "::error::fabrica-ref not set and no github.com/openchami/fabrica version found in go.mod" exit 1 fi - # Resolve the tag or pseudo-version to the commit it names - ref=$(go list -mod=mod -m -json "github.com/openchami/fabrica@${version}" | jq -r '.Origin.Hash') + fi + # Resolve the tag, branch, commit, or pseudo-version to the commit it names + if ! ref=$(go list -mod=mod -m -json "github.com/openchami/fabrica@${version}" | + jq -er '.Origin.Hash | select(type == "string" and test("^[0-9a-f]{40}$"))'); then + echo "::error::Could not resolve Fabrica ref: ${version}" + exit 1 fi echo "Using Fabrica ref: ${ref}" echo "ref=${ref}" >> "$GITHUB_OUTPUT" From bba485a4f39d424c3858a3a31a1dd795c9b7635b Mon Sep 17 00:00:00 2001 From: Sean Tronsen Date: Mon, 12 Oct 2026 13:55:29 -0600 Subject: [PATCH 7/8] feat: add lint-renovate based on https://github.com/OpenCHAMI/ochami/pull/198 Signed-off-by: Sean Tronsen --- .github/workflows/0-local-ci.yml | 3 ++ .github/workflows/lint-renovate.yml | 46 +++++++++++++++++++++++++++++ README.md | 21 +++++++++++++ renovate.json | 16 ++++++++++ 4 files changed, 86 insertions(+) create mode 100644 .github/workflows/lint-renovate.yml diff --git a/.github/workflows/0-local-ci.yml b/.github/workflows/0-local-ci.yml index f57b746..087d430 100644 --- a/.github/workflows/0-local-ci.yml +++ b/.github/workflows/0-local-ci.yml @@ -20,3 +20,6 @@ jobs: permissions: contents: read # baseline for checkout security-events: write # zizmor SARIF upload to GHAS + + lint-renovate: + uses: ./.github/workflows/lint-renovate.yml diff --git a/.github/workflows/lint-renovate.yml b/.github/workflows/lint-renovate.yml new file mode 100644 index 0000000..00bbcd8 --- /dev/null +++ b/.github/workflows/lint-renovate.yml @@ -0,0 +1,46 @@ +# SPDX-FileCopyrightText: © 2026 OpenCHAMI a Series of LF Projects, LLC +# SPDX-License-Identifier: MIT + +name: lint-renovate + +# Reusable workflow that validates the caller's Renovate config with +# renovate-config-validator. With no config-file, the validator checks every +# config file it finds in the repository (renovate.json, .github/renovate.json, +# renovate.json5, .renovaterc, ...). + +on: + workflow_call: + inputs: + renovate-version: + description: 'Renovate npm version that provides renovate-config-validator' + required: false + type: string + default: '44.115.13' + config-file: + description: 'Config file to validate (default: every Renovate config file found in the repo)' + required: false + type: string + default: '' + +permissions: + contents: read # baseline for checkout + +jobs: + lint-renovate: + runs-on: ubuntu-slim + timeout-minutes: 10 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: '24' + - name: Validate Renovate config + env: + RENOVATE_VERSION: ${{ inputs.renovate-version }} + CONFIG_FILE: ${{ inputs.config-file }} + run: | + # --no-global: validate as repository config, not self-hosted config. + npx --yes --package "renovate@${RENOVATE_VERSION}" -- \ + renovate-config-validator --no-global ${CONFIG_FILE:+"$CONFIG_FILE"} diff --git a/README.md b/README.md index f4a53f4..ad2edf7 100644 --- a/README.md +++ b/README.md @@ -424,6 +424,27 @@ jobs: # fabrica-ref: v0.4.5 # tag, branch, or SHA; default: version in go.mod ``` +### lint-renovate (Reusable Workflow) +Validates the caller's Renovate config with `renovate-config-validator` (as repository config). With no `config-file`, it checks every Renovate config file it finds (`renovate.json`, `.github/renovate.json`, `renovate.json5`, `.renovaterc`, ...). + +**Usage:** +```yaml +name: Lint Renovate +on: + pull_request: + paths: + - renovate.json + - .github/workflows/lint-renovate.yml + +jobs: + lint-renovate: + uses: OpenCHAMI/github-actions/.github/workflows/lint-renovate.yml@v4.0 + # Optional overrides: + # with: + # renovate-version: 44.115.13 + # config-file: renovate.json +``` + ## Actions ### gpg-configure-release-keys diff --git a/renovate.json b/renovate.json index 8ec9325..bcbbf00 100644 --- a/renovate.json +++ b/renovate.json @@ -3,5 +3,21 @@ "extends": [ "github>OpenCHAMI/.github//renovate-config/default", "config:recommended" + ], + "customManagers": [ + { + "customType": "regex", + "description": "Default renovate-config-validator version in lint-renovate and its README example", + "managerFilePatterns": [ + "/^\\.github/workflows/lint-renovate\\.yml$/", + "/^README\\.md$/" + ], + "matchStrings": [ + "renovate-version:\\s*\\n(?:\\s+[a-z]+:.*\\n)*?\\s+default: '(?\\d+\\.\\d+\\.\\d+)'", + "#\\s+renovate-version: (?\\d+\\.\\d+\\.\\d+)" + ], + "depNameTemplate": "renovate", + "datasourceTemplate": "npm" + } ] } From 26eaa1c6353c3627b377c23648b2b6cc50c4222a Mon Sep 17 00:00:00 2001 From: Sean Tronsen Date: Mon, 12 Oct 2026 14:01:53 -0600 Subject: [PATCH 8/8] chore: make zizmore happy Signed-off-by: Sean Tronsen --- .github/workflows/0-local-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/0-local-ci.yml b/.github/workflows/0-local-ci.yml index 087d430..0dd0ef1 100644 --- a/.github/workflows/0-local-ci.yml +++ b/.github/workflows/0-local-ci.yml @@ -22,4 +22,4 @@ jobs: security-events: write # zizmor SARIF upload to GHAS lint-renovate: - uses: ./.github/workflows/lint-renovate.yml + uses: $/.github/workflows/lint-renovate.yml