diff --git a/.ci-operator.yaml b/.ci-operator.yaml index a3628cf24..188626d79 100644 --- a/.ci-operator.yaml +++ b/.ci-operator.yaml @@ -1,4 +1,4 @@ build_root_image: - name: release + name: boilerplate namespace: openshift - tag: rhel-9-release-golang-1.25-openshift-4.22 + tag: image-v8.3.6 diff --git a/.codecov.yml b/.codecov.yml index ba05647ad..20cbf543c 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -8,8 +8,14 @@ coverage: range: "20...100" status: - project: no - patch: no + project: + default: + target: 35% + threshold: 1% + patch: + default: + target: 50% + threshold: 1% changes: no parsers: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..94e5b26c8 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,143 @@ +# ============================================================================= +# Tier 1 — Common Pre-Commit Hooks for OSD Operators +# SREP-4485 | Golden rules: SREP-4450 +# ============================================================================= +# +# INSTALL +# For detailed setup instructions including uv (recommended) and pip, +# see: boilerplate/openshift/golang-osd-operator/docs/pre-commit.md +# +# Quick start (uv): +# uv sync && source .venv/bin/activate && pre-commit install +# +# Quick start (pip): +# pip install 'pre-commit==4.6.0' && pre-commit install +# +# USAGE +# pre-commit run # staged files only (developer / agent workflow) +# pre-commit run --all-files # full repo (CI / first-time setup) +# +# BYPASS (golden rule 16) +# Skip one hook: SKIP=hook-id git commit +# Never use: git commit --no-verify +# Agents: never bypass any hook +# Security hooks: never bypassable under any circumstances +# +# CI RELATIONSHIP (golden rule 17) +# These hooks mirror ci/prow/lint. CI remains the authoritative gate. +# Every check here also runs in CI. Pre-commit is developer convenience. +# +# AGENT USAGE (golden rule 1, 7, 19) +# Agents run: pre-commit run +# Output: PRE_COMMIT=1 is set automatically — hooks emit structured output +# Retry: max 2 fix-and-retry iterations before escalating to human +# +# TIMING TARGETS (golden rule 2, 3) +# Total run: <= 10s target / <= 60s hard limit on a 10-file changeset +# Hooks run fastest-first (golden rule 13). Each hook has a timeout guard. +# +# FIRST RUN NOTE +# Auto-fix hooks (trailing-whitespace, end-of-file-fixer) will correct +# pre-existing violations on the first run. Stage and commit those fixes +# separately before day-to-day use. +# +# Fix commits can be excluded from git blame +# https://git-scm.com/docs/git-blame#Documentation/git-blame.txt---ignore-revs-filefile +# +# ============================================================================= + +repos: + + # --------------------------------------------------------------------------- + # 1. FILE HYGIENE + YAML SYNTAX | target < 2s | auto-fix + error + # - check-merge-conflict: detects unresolved merge markers + # - trailing-whitespace: removes trailing spaces (auto-fix) + # - end-of-file-fixer: ensures single EOF newline (auto-fix) + # - check-yaml: validates YAML syntax in deploy/ manifests; + # mirrors ci/prow/lint: olm-deploy-yaml-validate + # --------------------------------------------------------------------------- + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 # pinned immutable tag + hooks: + - id: check-merge-conflict + - id: trailing-whitespace + args: [--markdown-linebreak-ext=md] + - id: end-of-file-fixer + - id: check-yaml + name: YAML syntax (deploy/) + files: ^deploy/.*\.ya?ml$ + args: [--allow-multiple-documents] + + # --------------------------------------------------------------------------- + # 2. SECRETS DETECTION | target < 5s | always blocking + # Scans all file types (YAML, shell, config) — gosec covers Go only. + # High-confidence findings block; configure .gitleaks.toml for allowlist. + # --------------------------------------------------------------------------- + - repo: https://github.com/gitleaks/gitleaks + rev: v8.18.0 # pinned immutable tag (golden rule 15) + hooks: + - id: gitleaks + + # --------------------------------------------------------------------------- + # 3. STATIC ANALYSIS | target < 15s cached | error + # Mirrors ci/prow/lint: go-check exactly (same version + config as CI). + # Linter config: boilerplate/openshift/golang-osd-operator/golangci.yml + # --------------------------------------------------------------------------- + - repo: https://github.com/golangci/golangci-lint + rev: v2.0.2 # pinned immutable tag — must match CI (golden rule 15) + hooks: + - id: golangci-lint + args: + - --config=boilerplate/openshift/golang-osd-operator/golangci.yml + - --timeout=120s # graceful timeout (golden rule 3) + + # --------------------------------------------------------------------------- + # Local hooks — compile, dependency, security + # + # TIMEOUT NOTE (golden rule 3) + # Uses portable timeout detection: 'timeout' on Linux, 'gtimeout' on macOS. + # macOS: brew install coreutils + # Linux: timeout is available by default (GNU coreutils) + # --------------------------------------------------------------------------- + - repo: local + hooks: + + # ----------------------------------------------------------------------- + # 4. COMPILE CHECK | target < 10s cached | error + # Catches import cycles and type errors before golangci-lint runs. + # Note: go build ./... writes no binary to the repo (compile check only). + # Fix: resolve compilation errors reported by go build. + # ----------------------------------------------------------------------- + - id: go-build + name: go build + language: system + entry: bash -c 'T=$(command -v timeout || command -v gtimeout || echo); ${T:+$T 30s} go build ./...' + types: [go] + pass_filenames: false + + # ----------------------------------------------------------------------- + # 5. DEPENDENCY DRIFT | target < 10s | error + # Detects uncommitted go.mod/go.sum changes after go mod tidy. + # Fix: run 'go mod tidy' and stage go.mod and go.sum. + # ----------------------------------------------------------------------- + - id: go-mod-tidy + name: go mod tidy + language: system + entry: bash -c 'T=$(command -v timeout || command -v gtimeout || echo); ${T:+$T 60s} go mod tidy && git diff --exit-code go.mod go.sum' + files: '(\.go$|go\.(mod|sum)$)' + exclude: '^vendor/' + pass_filenames: false + + # ----------------------------------------------------------------------- + # 6. RBAC WILDCARD CHECK | target < 5s | warn-only (blocking after cleanup) + # Rejects wildcard RBAC in deploy/ manifests (verbs/resources: ["*"] + # or multi-line - '*' format). Logic lives in standard.mk target + # 'rbac-wildcard-check' for readability and reuse. + # Fix: replace wildcards with explicit verbs and resource names. + # ----------------------------------------------------------------------- + - id: rbac-wildcard-check + name: RBAC wildcard permissions + language: system + entry: bash -c 'make rbac-wildcard-check' + files: ^deploy/.*\.ya?ml$ + pass_filenames: false diff --git a/OWNERS_ALIASES b/OWNERS_ALIASES index 3c43bbc8b..12676f650 100644 --- a/OWNERS_ALIASES +++ b/OWNERS_ALIASES @@ -4,12 +4,12 @@ # ============================================================================= aliases: srep-functional-team-aurora: - - abyrne55 - AlexSmithGH + - BATMAN-JD - dakotalongRH - eth1030 + - geowa4 - joshbranham - - luis-falcon - reedcort srep-functional-team-fedramp: - theautoroboto @@ -61,7 +61,6 @@ aliases: - casey-williams-rh - boranx srep-functional-team-thor: - - a7vicky - diakovnec - MitaliBhalla - feichashao @@ -74,7 +73,6 @@ aliases: - yiqinzhang - varunraokadaparthi srep-functional-leads: - - abyrne55 - clcollins - bergmannf - theautoroboto @@ -83,7 +81,6 @@ aliases: - ravitri srep-team-leads: - rafael-azevedo - - iamkirkbater - dustman9000 - bmeng - typeid @@ -92,5 +89,4 @@ aliases: - maorfr - rogbas srep-architects: - - jharrington22 - cblecker diff --git a/boilerplate/_data/backing-image-tag b/boilerplate/_data/backing-image-tag index 77a6bbe44..ca21d244a 100644 --- a/boilerplate/_data/backing-image-tag +++ b/boilerplate/_data/backing-image-tag @@ -1 +1 @@ -image-v8.3.4 +image-v8.3.6 diff --git a/boilerplate/_data/last-boilerplate-commit b/boilerplate/_data/last-boilerplate-commit index 73b13b819..3e14fd497 100644 --- a/boilerplate/_data/last-boilerplate-commit +++ b/boilerplate/_data/last-boilerplate-commit @@ -1 +1 @@ -2f25b66747494403fd6c00524e580f97982de853 +8fb7c801f68dc7e06e8d2ae138c2a98f0b234b56 diff --git a/boilerplate/_lib/container-make b/boilerplate/_lib/container-make index 77834586d..8da20031a 100755 --- a/boilerplate/_lib/container-make +++ b/boilerplate/_lib/container-make @@ -29,12 +29,14 @@ if [[ "${CONTAINER_ENGINE##*/}" == "podman" ]] && [[ $OSTYPE == *"linux"* ]]; th else CE_OPTS="${CE_OPTS} -v $REPO_ROOT:$CONTAINER_MOUNT" fi -container_id=$($CONTAINER_ENGINE run -d ${CE_OPTS} $IMAGE_PULL_PATH sleep infinity) +container_id=$($CONTAINER_ENGINE run --rm -d ${CE_OPTS} $IMAGE_PULL_PATH sleep infinity) if [[ $? -ne 0 ]] || [[ -z "$container_id" ]]; then err "Couldn't start detached container" fi +trap "$CONTAINER_ENGINE stop $container_id >/dev/null 2>&1" EXIT + # Now run our `make` command in it with the right UID and working directory args="exec -it -u $(id -u):0 -w $CONTAINER_MOUNT $container_id" banner "Running: make $@" @@ -52,6 +54,9 @@ if [[ $rc -ne 0 ]]; then fi fi +# Disarm the interrupt trap -- normal cleanup handles it from here +trap - EXIT + # Finally, remove the container banner "Cleaning up the container" $CONTAINER_ENGINE rm -f $container_id >/dev/null diff --git a/boilerplate/openshift/golang-osd-operator/.codecov.yml b/boilerplate/openshift/golang-osd-operator/.codecov.yml index ba05647ad..20cbf543c 100644 --- a/boilerplate/openshift/golang-osd-operator/.codecov.yml +++ b/boilerplate/openshift/golang-osd-operator/.codecov.yml @@ -8,8 +8,14 @@ coverage: range: "20...100" status: - project: no - patch: no + project: + default: + target: 35% + threshold: 1% + patch: + default: + target: 50% + threshold: 1% changes: no parsers: diff --git a/boilerplate/openshift/golang-osd-operator/OWNERS_ALIASES b/boilerplate/openshift/golang-osd-operator/OWNERS_ALIASES index 3c43bbc8b..12676f650 100644 --- a/boilerplate/openshift/golang-osd-operator/OWNERS_ALIASES +++ b/boilerplate/openshift/golang-osd-operator/OWNERS_ALIASES @@ -4,12 +4,12 @@ # ============================================================================= aliases: srep-functional-team-aurora: - - abyrne55 - AlexSmithGH + - BATMAN-JD - dakotalongRH - eth1030 + - geowa4 - joshbranham - - luis-falcon - reedcort srep-functional-team-fedramp: - theautoroboto @@ -61,7 +61,6 @@ aliases: - casey-williams-rh - boranx srep-functional-team-thor: - - a7vicky - diakovnec - MitaliBhalla - feichashao @@ -74,7 +73,6 @@ aliases: - yiqinzhang - varunraokadaparthi srep-functional-leads: - - abyrne55 - clcollins - bergmannf - theautoroboto @@ -83,7 +81,6 @@ aliases: - ravitri srep-team-leads: - rafael-azevedo - - iamkirkbater - dustman9000 - bmeng - typeid @@ -92,5 +89,4 @@ aliases: - maorfr - rogbas srep-architects: - - jharrington22 - cblecker diff --git a/boilerplate/openshift/golang-osd-operator/README.md b/boilerplate/openshift/golang-osd-operator/README.md index 0f415ee09..fea37f6af 100644 --- a/boilerplate/openshift/golang-osd-operator/README.md +++ b/boilerplate/openshift/golang-osd-operator/README.md @@ -119,6 +119,18 @@ Checks consist of: - `go generate`. This is a no-op if you have no `//go:generate` directives in your code. +## PKO (Package Operator) fixture validation + +Operators deployed via [Package Operator](https://package-operator.run/) can define snapshot test fixtures that validate `.gotmpl` template rendering. If `deploy_pko/manifest.yaml` exists and contains a `test:` section, the following targets are available: + +- `make validate-pko-fixtures` validates that committed fixtures in `deploy_pko/.test-fixtures/` match the current template output. This runs automatically as part of `make validate` (and therefore `make container-validate`). Repos without PKO test fixtures are silently skipped. +- `make generate-pko-fixtures` regenerates fixtures after intentional changes to `.gotmpl` files or `manifest.yaml` config. Review the diff and commit the updated fixtures alongside the template changes. +- `make container-generate-pko-fixtures` runs fixture generation inside the boilerplate backing container, which has `kubectl-package` pre-installed. Useful if you don't have `kubectl-package` installed locally. The repository is bind-mounted into the container, so the generated fixtures appear directly in your local `deploy_pko/.test-fixtures/` directory — no manual copy step needed. + +Both targets require `kubectl-package`. If it is not found, the target fails with installation instructions. The backing container image includes `kubectl-package`, so `make container-validate` and `make container-generate-pko-fixtures` always work. + +**Important:** Buildah's `COPY *` includes dotfiles and dotdirs (contrary to standard glob behavior), so `deploy_pko/.test-fixtures/` will be included in the PKO OCI image unless excluded. `make generate-pko-fixtures` automatically creates a `deploy_pko/.dockerignore` with `.test-fixtures` to prevent this. `make validate-pko-fixtures` verifies the exclusion exists. Without it, PKO will see duplicate objects and fail to deploy the ClusterPackage. + ## FIPS (Federal Information Processing Standards) To enable FIPS in your build there is a `make ensure-fips` target. diff --git a/boilerplate/openshift/golang-osd-operator/dependabot.yml b/boilerplate/openshift/golang-osd-operator/dependabot.yml index 45e5fe356..eae3de413 100644 --- a/boilerplate/openshift/golang-osd-operator/dependabot.yml +++ b/boilerplate/openshift/golang-osd-operator/dependabot.yml @@ -5,8 +5,13 @@ updates: labels: - "area/dependency" - "ok-to-test" + - "lgtm" + - "approved" schedule: interval: "weekly" + day: "monday" + time: "03:00" + timezone: "UTC" ignore: - dependency-name: "redhat-services-prod/openshift/boilerplate" # don't upgrade boilerplate via these means diff --git a/boilerplate/openshift/golang-osd-operator/docs/pre-commit.md b/boilerplate/openshift/golang-osd-operator/docs/pre-commit.md new file mode 100644 index 000000000..88ff5bca3 --- /dev/null +++ b/boilerplate/openshift/golang-osd-operator/docs/pre-commit.md @@ -0,0 +1,123 @@ +# Pre-Commit Hooks Setup Guide + +## Installation + +### Recommended: Using uv + +[uv](https://github.com/astral-sh/uv) is recommended for Python dependency management. It provides dependency locking with package hashes (supply-chain protection), virtual environment management, and is 10-100x faster than pip. + +**Install uv:** +```bash +# macOS/Linux +curl -LsSf https://astral.sh/uv/install.sh | sh + +# Windows +powershell -c "irm https://astral.sh/uv/install.ps1 | iex" + +# Via pip +pip install uv +``` + +**First-time setup:** +```bash +uv init --bare # creates pyproject.toml +uv add --dev pre-commit==4.6.0 # adds dependency, generates uv.lock +source .venv/bin/activate # macOS/Linux (.venv\Scripts\activate on Windows) +pre-commit install +``` + +**Subsequent setup** (when `pyproject.toml` and `uv.lock` exist): +```bash +uv sync +source .venv/bin/activate +pre-commit install +``` + +### Alternative: Using pip + +```bash +pip install 'pre-commit==4.6.0' # pinned version (Golden Rule 15) +pre-commit install +``` + +Add to `requirements-dev.txt`: `pre-commit==4.6.0` + +## First-Time Setup + +Run on all files to catch existing issues: +```bash +pre-commit run --all-files +``` + +Auto-fix hooks will modify files on first run. Stage and commit these separately: +```bash +git diff +git add . +git commit -m "Fix: Apply pre-commit auto-fixes" +``` + +**Exclude fix commits from git blame:** +```bash +# Create .git-blame-ignore-revs with commit hashes +git config blame.ignoreRevsFile .git-blame-ignore-revs +``` + +See [git-blame docs](https://git-scm.com/docs/git-blame#Documentation/git-blame.txt---ignore-revs-filefile). + +## Usage + +**Automatic** (runs on `git commit`): +```bash +git add +git commit -m "Message" +``` + +**Manual:** +```bash +pre-commit run # staged files only +pre-commit run --all-files # entire repo +pre-commit run --files path/to/file # specific files +``` + +**Bypass (use sparingly):** +```bash +SKIP=hook-id git commit -m "Message" # skip one hook +git commit --no-verify # NEVER use (Golden Rule 16) +``` + +Rules: Agents never bypass hooks. Security hooks (gitleaks) never bypassable. + +## Troubleshooting + +**macOS timeout issues:** +```bash +brew install coreutils # provides gtimeout +``` + +**Virtual environment not found:** +```bash +source .venv/bin/activate +uv sync +``` + +**Hooks not running:** +```bash +ls -la .git/hooks/pre-commit # verify installation +pre-commit install # reinstall +``` + +**Hook failures:** Read error messages and fix issues: +- `go-build`: Fix compilation errors +- `go-mod-tidy`: Run `go mod tidy` and stage go.mod/go.sum +- `check-yaml`: Fix YAML syntax + +## CI Integration + +Pre-commit mirrors `ci/prow/lint`. CI is authoritative; pre-commit is developer convenience. All hooks run in CI with same config. + +If pre-commit passes but CI fails: `pre-commit autoupdate` + +## Resources + +- [Pre-Commit Documentation](https://pre-commit.com/) +- [uv Documentation](https://github.com/astral-sh/uv) diff --git a/boilerplate/openshift/golang-osd-operator/ensure.sh b/boilerplate/openshift/golang-osd-operator/ensure.sh index fd5982037..4de1116e8 100755 --- a/boilerplate/openshift/golang-osd-operator/ensure.sh +++ b/boilerplate/openshift/golang-osd-operator/ensure.sh @@ -31,6 +31,23 @@ golangci-lint) fi ;; +kubectl-package) + KUBECTL_PACKAGE_VERSION="v1.18.6" + GOPATH=$(go env GOPATH) + if which kubectl-package ; then + exit + else + mkdir -p "${GOPATH}/bin" + if ! echo "${PATH}" | grep -q "${GOPATH}/bin"; then + echo "${GOPATH}/bin not in $PATH" + exit 1 + fi + DOWNLOAD_URL="https://github.com/package-operator/package-operator/releases/download/${KUBECTL_PACKAGE_VERSION}/kubectl-package_${GOOS}_amd64" + curl -sfL "${DOWNLOAD_URL}" -o "${GOPATH}/bin/kubectl-package" + chmod +x "${GOPATH}/bin/kubectl-package" + fi + ;; + opm) mkdir -p .opm/bin cd .opm/bin diff --git a/boilerplate/openshift/golang-osd-operator/golangci.yml b/boilerplate/openshift/golang-osd-operator/golangci.yml index 46fec0352..df1596ffc 100644 --- a/boilerplate/openshift/golang-osd-operator/golangci.yml +++ b/boilerplate/openshift/golang-osd-operator/golangci.yml @@ -1,39 +1,76 @@ version: "2" -run: - concurrency: 10 + linters: - default: none enable: + # Error Handling & Security - errcheck - - gosec - govet - - ineffassign - - misspell - staticcheck + - gosec + - bodyclose + - sqlclosecheck + - contextcheck + - noctx + + # Error Prevention + - errorlint + - nilerr + - nilnil + - revive + + # Code Quality + - ineffassign + - unconvert + - unparam - unused + - misspell + + # Maintainability + - prealloc + - nolintlint + - gocyclo + - exhaustive + - makezero + - containedctx + settings: + revive: + rules: + - name: package-comments + disabled: true + + errcheck: + check-type-assertions: true + check-blank: false + + exclusions: + presets: + - std-error-handling + + gocyclo: + min-complexity: 15 + + errorlint: + errorf: true + asserts: true + comparison: true + misspell: extra-words: - typo: openshit correction: OpenShift - exclusions: - generated: lax - presets: - - comments - - common-false-positives - - legacy - - std-error-handling - paths: - - third_party/ - - builtin/ - - examples/ + +run: + timeout: 5m + # Incremental linting (new-from-rev) is passed via the Makefile's + # go-check target. In CI it uses PULL_BASE_SHA (guaranteed to exist + # even in shallow clones); locally it falls back to origin/HEAD. + +formatters: + enable: + - gofmt + - goimports + issues: max-issues-per-linter: 0 max-same-issues: 0 -formatters: - exclusions: - generated: lax - paths: - - third_party/ - - builtin/ - - examples/ diff --git a/boilerplate/openshift/golang-osd-operator/olm_pko_migration.py b/boilerplate/openshift/golang-osd-operator/olm_pko_migration.py index 6b7b67bf3..e9866b3e5 100644 --- a/boilerplate/openshift/golang-osd-operator/olm_pko_migration.py +++ b/boilerplate/openshift/golang-osd-operator/olm_pko_migration.py @@ -11,7 +11,7 @@ import subprocess import sys from pathlib import Path -from typing import Any +from typing import Any, Optional import yaml @@ -446,8 +446,8 @@ def get_pko_manifest(operator_name: str) -> dict[str, Any]: "type": "string", "default": "None", }, + }, "type": "object", - } } }, }, @@ -563,6 +563,8 @@ def annotate_manifests(manifests: list[str]) -> list[dict[str, Any]]: annotated.append(manifest) elif kind == "ServiceMonitor": annotated.append(annotate(manifest, PHASE_DEPLOY)) + elif kind == "ConfigMap": + annotated.append(annotate(manifest, PHASE_DEPLOY)) else: print(f"Unhandled type: {kind}") annotated.append(manifest) @@ -627,7 +629,7 @@ def write_pko_dockerfile(): ) ) -def extract_deployment_selector() -> str | None: +def extract_deployment_selector() -> Optional[str]: """ Extract the clusterDeploymentSelector from hack/olm-registry/olm-artifacts-template.yaml. diff --git a/boilerplate/openshift/golang-osd-operator/pre-commit-config.yaml b/boilerplate/openshift/golang-osd-operator/pre-commit-config.yaml new file mode 100644 index 000000000..94e5b26c8 --- /dev/null +++ b/boilerplate/openshift/golang-osd-operator/pre-commit-config.yaml @@ -0,0 +1,143 @@ +# ============================================================================= +# Tier 1 — Common Pre-Commit Hooks for OSD Operators +# SREP-4485 | Golden rules: SREP-4450 +# ============================================================================= +# +# INSTALL +# For detailed setup instructions including uv (recommended) and pip, +# see: boilerplate/openshift/golang-osd-operator/docs/pre-commit.md +# +# Quick start (uv): +# uv sync && source .venv/bin/activate && pre-commit install +# +# Quick start (pip): +# pip install 'pre-commit==4.6.0' && pre-commit install +# +# USAGE +# pre-commit run # staged files only (developer / agent workflow) +# pre-commit run --all-files # full repo (CI / first-time setup) +# +# BYPASS (golden rule 16) +# Skip one hook: SKIP=hook-id git commit +# Never use: git commit --no-verify +# Agents: never bypass any hook +# Security hooks: never bypassable under any circumstances +# +# CI RELATIONSHIP (golden rule 17) +# These hooks mirror ci/prow/lint. CI remains the authoritative gate. +# Every check here also runs in CI. Pre-commit is developer convenience. +# +# AGENT USAGE (golden rule 1, 7, 19) +# Agents run: pre-commit run +# Output: PRE_COMMIT=1 is set automatically — hooks emit structured output +# Retry: max 2 fix-and-retry iterations before escalating to human +# +# TIMING TARGETS (golden rule 2, 3) +# Total run: <= 10s target / <= 60s hard limit on a 10-file changeset +# Hooks run fastest-first (golden rule 13). Each hook has a timeout guard. +# +# FIRST RUN NOTE +# Auto-fix hooks (trailing-whitespace, end-of-file-fixer) will correct +# pre-existing violations on the first run. Stage and commit those fixes +# separately before day-to-day use. +# +# Fix commits can be excluded from git blame +# https://git-scm.com/docs/git-blame#Documentation/git-blame.txt---ignore-revs-filefile +# +# ============================================================================= + +repos: + + # --------------------------------------------------------------------------- + # 1. FILE HYGIENE + YAML SYNTAX | target < 2s | auto-fix + error + # - check-merge-conflict: detects unresolved merge markers + # - trailing-whitespace: removes trailing spaces (auto-fix) + # - end-of-file-fixer: ensures single EOF newline (auto-fix) + # - check-yaml: validates YAML syntax in deploy/ manifests; + # mirrors ci/prow/lint: olm-deploy-yaml-validate + # --------------------------------------------------------------------------- + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 # pinned immutable tag + hooks: + - id: check-merge-conflict + - id: trailing-whitespace + args: [--markdown-linebreak-ext=md] + - id: end-of-file-fixer + - id: check-yaml + name: YAML syntax (deploy/) + files: ^deploy/.*\.ya?ml$ + args: [--allow-multiple-documents] + + # --------------------------------------------------------------------------- + # 2. SECRETS DETECTION | target < 5s | always blocking + # Scans all file types (YAML, shell, config) — gosec covers Go only. + # High-confidence findings block; configure .gitleaks.toml for allowlist. + # --------------------------------------------------------------------------- + - repo: https://github.com/gitleaks/gitleaks + rev: v8.18.0 # pinned immutable tag (golden rule 15) + hooks: + - id: gitleaks + + # --------------------------------------------------------------------------- + # 3. STATIC ANALYSIS | target < 15s cached | error + # Mirrors ci/prow/lint: go-check exactly (same version + config as CI). + # Linter config: boilerplate/openshift/golang-osd-operator/golangci.yml + # --------------------------------------------------------------------------- + - repo: https://github.com/golangci/golangci-lint + rev: v2.0.2 # pinned immutable tag — must match CI (golden rule 15) + hooks: + - id: golangci-lint + args: + - --config=boilerplate/openshift/golang-osd-operator/golangci.yml + - --timeout=120s # graceful timeout (golden rule 3) + + # --------------------------------------------------------------------------- + # Local hooks — compile, dependency, security + # + # TIMEOUT NOTE (golden rule 3) + # Uses portable timeout detection: 'timeout' on Linux, 'gtimeout' on macOS. + # macOS: brew install coreutils + # Linux: timeout is available by default (GNU coreutils) + # --------------------------------------------------------------------------- + - repo: local + hooks: + + # ----------------------------------------------------------------------- + # 4. COMPILE CHECK | target < 10s cached | error + # Catches import cycles and type errors before golangci-lint runs. + # Note: go build ./... writes no binary to the repo (compile check only). + # Fix: resolve compilation errors reported by go build. + # ----------------------------------------------------------------------- + - id: go-build + name: go build + language: system + entry: bash -c 'T=$(command -v timeout || command -v gtimeout || echo); ${T:+$T 30s} go build ./...' + types: [go] + pass_filenames: false + + # ----------------------------------------------------------------------- + # 5. DEPENDENCY DRIFT | target < 10s | error + # Detects uncommitted go.mod/go.sum changes after go mod tidy. + # Fix: run 'go mod tidy' and stage go.mod and go.sum. + # ----------------------------------------------------------------------- + - id: go-mod-tidy + name: go mod tidy + language: system + entry: bash -c 'T=$(command -v timeout || command -v gtimeout || echo); ${T:+$T 60s} go mod tidy && git diff --exit-code go.mod go.sum' + files: '(\.go$|go\.(mod|sum)$)' + exclude: '^vendor/' + pass_filenames: false + + # ----------------------------------------------------------------------- + # 6. RBAC WILDCARD CHECK | target < 5s | warn-only (blocking after cleanup) + # Rejects wildcard RBAC in deploy/ manifests (verbs/resources: ["*"] + # or multi-line - '*' format). Logic lives in standard.mk target + # 'rbac-wildcard-check' for readability and reuse. + # Fix: replace wildcards with explicit verbs and resource names. + # ----------------------------------------------------------------------- + - id: rbac-wildcard-check + name: RBAC wildcard permissions + language: system + entry: bash -c 'make rbac-wildcard-check' + files: ^deploy/.*\.ya?ml$ + pass_filenames: false diff --git a/boilerplate/openshift/golang-osd-operator/standard.mk b/boilerplate/openshift/golang-osd-operator/standard.mk index 9e3249dfd..4f617e93b 100644 --- a/boilerplate/openshift/golang-osd-operator/standard.mk +++ b/boilerplate/openshift/golang-osd-operator/standard.mk @@ -172,10 +172,19 @@ docker-login: mkdir -p ${CONTAINER_ENGINE_CONFIG_DIR} @${CONTAINER_ENGINE} login -u="${REGISTRY_USER}" -p="${REGISTRY_TOKEN}" quay.io +# Only lint new/changed code. In Prow CI, PULL_BASE_SHA points to the +# base commit and is guaranteed to exist in the checkout (even shallow +# clones). Locally, fall back to the default branch ref. +ifdef PULL_BASE_SHA +LINT_NEW_FROM_REV := $(PULL_BASE_SHA) +else +LINT_NEW_FROM_REV := $(shell git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/||') +endif + .PHONY: go-check go-check: ## Golang linting and other static analysis ${CONVENTION_DIR}/ensure.sh golangci-lint - ${GOENV} GOLANGCI_LINT_CACHE=${GOLANGCI_LINT_CACHE} golangci-lint run -c ${CONVENTION_DIR}/golangci.yml ./... + ${GOENV} GOLANGCI_LINT_CACHE=${GOLANGCI_LINT_CACHE} golangci-lint run -c ${CONVENTION_DIR}/golangci.yml $(if $(LINT_NEW_FROM_REV),--new-from-rev=$(LINT_NEW_FROM_REV)) ./... .PHONY: go-generate go-generate: @@ -234,8 +243,28 @@ else $(info Did not find 'config/default' - skipping kustomize manifest generation) endif +.PHONY: sync-pko-crds +sync-pko-crds: +ifneq (,$(wildcard deploy_pko)) + @if [ -d deploy/crds ]; then \ + yq_yaml_flag=""; \ + if $(YQ) --version 2>&1 | grep -qE "^yq [0-9]"; then \ + yq_yaml_flag="-y"; \ + fi; \ + for crd in deploy/crds/*.yaml; do \ + [ -f "$$crd" ] || continue; \ + name=$$($(YQ) -r '.metadata.name' "$$crd"); \ + $(YQ) $$yq_yaml_flag '.metadata.annotations["package-operator.run/phase"] = "crds" | .metadata.annotations["package-operator.run/collision-protection"] = "IfNoController"' \ + "$$crd" > "deploy_pko/CustomResourceDefinition-$$name.yaml"; \ + echo "Synced CRD $$name to deploy_pko/"; \ + done; \ + fi +else + $(info deploy_pko/ not found - skipping PKO CRD sync) +endif + .PHONY: generate -generate: op-generate go-generate openapi-generate manifests +generate: op-generate go-generate openapi-generate manifests sync-pko-crds ifeq (${FIPS_ENABLED}, true) go-build: ensure-fips @@ -309,15 +338,94 @@ prow-config: # Targets used by prow ###################### +# validate-pko-fixtures: Validate PKO package templates against committed snapshot fixtures. +# Silently skips if deploy_pko/ has no manifest.yaml with a test section. +# Requires kubectl-package; see https://github.com/package-operator/package-operator/releases +.PHONY: validate-pko-fixtures +validate-pko-fixtures: + @if [ -d deploy_pko ] && grep -q '^test:' deploy_pko/manifest.yaml 2>/dev/null; then \ + ${CONVENTION_DIR}/ensure.sh kubectl-package; \ + echo "Validating PKO package fixtures..."; \ + kubectl-package validate deploy_pko/ || \ + (echo "ERROR: PKO fixture validation failed. Rendered templates do not match committed fixtures." >&2; \ + echo "If you intentionally changed a deploy_pko/ .gotmpl or manifest.yaml config, regenerate fixtures:" >&2; \ + echo " make generate-pko-fixtures" >&2; \ + echo " git diff deploy_pko/.test-fixtures/" >&2; \ + echo "Review the diff to confirm only your intended changes are reflected, then commit the updated fixtures." >&2; \ + echo "If you did NOT intend to change template output, your modifications may have introduced an unintended" >&2; \ + echo "regression in the rendered deployment manifests. Review your changes to deploy_pko/ carefully." >&2; \ + exit 1); \ + if [ -d deploy_pko/.test-fixtures ]; then \ + ignore_file=""; \ + if [ -f deploy_pko/.containerignore ]; then \ + ignore_file="deploy_pko/.containerignore"; \ + elif [ -f deploy_pko/.dockerignore ]; then \ + ignore_file="deploy_pko/.dockerignore"; \ + fi; \ + if [ -z "$$ignore_file" ]; then \ + echo "ERROR: deploy_pko/.test-fixtures/ exists but no .dockerignore or .containerignore found in deploy_pko/." >&2; \ + echo "Without it, test fixtures will be included in the PKO OCI image, causing Duplicate Object errors." >&2; \ + echo "Fix: run 'make generate-pko-fixtures' to auto-create deploy_pko/.dockerignore" >&2; \ + exit 1; \ + elif ! grep -q '\.test-fixtures' "$$ignore_file"; then \ + echo "ERROR: $$ignore_file exists but does not exclude .test-fixtures." >&2; \ + echo "Without this exclusion, test fixtures will be included in the PKO OCI image." >&2; \ + echo "Fix: add '.test-fixtures' to $$ignore_file" >&2; \ + exit 1; \ + fi; \ + fi; \ + fi + +# generate-pko-fixtures: Regenerate PKO snapshot fixtures after template changes. +# Requires kubectl-package; see https://github.com/package-operator/package-operator/releases +.PHONY: generate-pko-fixtures +generate-pko-fixtures: + @if [ -d deploy_pko ] && grep -q '^test:' deploy_pko/manifest.yaml 2>/dev/null; then \ + ${CONVENTION_DIR}/ensure.sh kubectl-package; \ + echo "Regenerating PKO test fixtures..."; \ + rm -rf deploy_pko/.test-fixtures; \ + kubectl-package validate deploy_pko/ && \ + if [ ! -f deploy_pko/.dockerignore ] && [ ! -f deploy_pko/.containerignore ]; then \ + echo ".test-fixtures" > deploy_pko/.dockerignore; \ + echo "Created deploy_pko/.dockerignore to exclude .test-fixtures from PKO image."; \ + elif [ -f deploy_pko/.dockerignore ] && ! grep -q '\.test-fixtures' deploy_pko/.dockerignore; then \ + echo ".test-fixtures" >> deploy_pko/.dockerignore; \ + echo "Added .test-fixtures to deploy_pko/.dockerignore."; \ + elif [ -f deploy_pko/.containerignore ] && ! grep -q '\.test-fixtures' deploy_pko/.containerignore; then \ + echo ".test-fixtures" >> deploy_pko/.containerignore; \ + echo "Added .test-fixtures to deploy_pko/.containerignore."; \ + fi; \ + echo "Fixtures regenerated. Review with 'git diff deploy_pko/.test-fixtures/' and commit."; \ + else \ + echo "No PKO test configuration found in deploy_pko/manifest.yaml, nothing to generate."; \ + fi + # validate: Ensure code generation has not been forgotten; and ensure # generated and boilerplate code has not been modified. .PHONY: validate -validate: boilerplate-freeze-check generate-check +validate: boilerplate-freeze-check generate-check validate-pko-fixtures # lint: Perform static analysis. .PHONY: lint lint: olm-deploy-yaml-validate go-check +# rbac-wildcard-check: Detect wildcard RBAC permissions in deploy/ manifests. +# Checks both inline (verbs: ["*"]) and multi-line (- '*' under verbs/resources:) +# formats. Called by the pre-commit rbac-wildcard-check hook. +# Currently warn-only (exits 0) to avoid breaking repos with pre-existing wildcards. +# Will become blocking once existing violations are resolved across the fleet. +.PHONY: rbac-wildcard-check +rbac-wildcard-check: + @python3 -c "\ +import sys,glob;\ +violations=[(f,n,l.rstrip()) for f in glob.glob('deploy/*.yaml')+glob.glob('deploy/*.yml') \ +for lines in [list(enumerate(open(f),1))] \ +for i,(n,l) in enumerate(lines) \ +if l.strip().lstrip('- ').strip(chr(39)+chr(34))=='*' \ +and any(lines[j][1].strip() in ('verbs:','resources:') for j in range(max(0,i-5),i))];\ +[print('WARNING: wildcard RBAC found: '+v[0]+'|'+str(v[1])+'|'+v[2]) for v in violations];\ +sys.exit(0)" + # test: "Local" unit and functional testing. .PHONY: test test: go-test @@ -396,6 +504,10 @@ container-validate: container-coverage: ${BOILERPLATE_CONTAINER_MAKE} coverage +.PHONY: container-generate-pko-fixtures +container-generate-pko-fixtures: + ${BOILERPLATE_CONTAINER_MAKE} generate-pko-fixtures + # Run all container-* validation targets in sequence. # Set NONINTERACTIVE=true to skip debug shells and fail fast for CI/automation. .PHONY: container-all diff --git a/boilerplate/openshift/golang-osd-operator/update b/boilerplate/openshift/golang-osd-operator/update index 7f2c702f2..5fe4a3859 100755 --- a/boilerplate/openshift/golang-osd-operator/update +++ b/boilerplate/openshift/golang-osd-operator/update @@ -110,6 +110,10 @@ echo " name: $IMAGE_NAME" echo " tag: $LATEST_IMAGE_TAG" ${SED?} "s/__NAMESPACE__/$IMAGE_NAMESPACE/; s/__NAME__/$IMAGE_NAME/; s/__TAG__/$LATEST_IMAGE_TAG/" ${HERE}/.ci-operator.yaml >$REPO_ROOT/.ci-operator.yaml +# Add pre-commit hooks configuration (SREP-4485) +echo "Copying pre-commit-config.yaml to .pre-commit-config.yaml" +cp ${HERE}/pre-commit-config.yaml $REPO_ROOT/.pre-commit-config.yaml + # Check for pipeline files in .tekton directory and centralize them TEKTON_DIR="${REPO_ROOT}/.tekton" if [ -d "$TEKTON_DIR" ]; then diff --git a/build/Dockerfile b/build/Dockerfile index 4c20a445c..8e7b556ed 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -1,4 +1,4 @@ -FROM quay.io/redhat-services-prod/openshift/boilerplate:image-v8.3.4 AS builder +FROM quay.io/redhat-services-prod/openshift/boilerplate:image-v8.3.6 AS builder # Copy and download the dependecies so that they are cached locally in the stages. WORKDIR /go/src/github.com/openshift/must-gather-operator @@ -7,7 +7,7 @@ COPY . /go/src/github.com/openshift/must-gather-operator RUN make go-build -FROM registry.access.redhat.com/ubi9/ubi-minimal:9.7-1771346502 +FROM registry.access.redhat.com/ubi9/ubi-minimal:9.8-1780378819 RUN microdnf install -y tar gzip openssh-clients wget shadow-utils procps sshpass nc && \ microdnf clean all diff --git a/build/Dockerfile.olm-registry b/build/Dockerfile.olm-registry index 0b73aec81..408bc99d5 100644 --- a/build/Dockerfile.olm-registry +++ b/build/Dockerfile.olm-registry @@ -4,7 +4,7 @@ COPY ${SAAS_OPERATOR_DIR} manifests RUN initializer --permissive # ubi-micro does not work for clusters with fips enabled unless we make OpenSSL available -FROM registry.access.redhat.com/ubi9/ubi-minimal:9.7-1771346502 +FROM registry.access.redhat.com/ubi9/ubi-minimal:9.8-1780378819 COPY --from=builder /bin/registry-server /bin/registry-server COPY --from=builder /bin/grpc_health_probe /bin/grpc_health_probe