Kollect enforces package import boundaries (go-arch-lint), external dependency policy
(golangci-lint depguard + gomodguard), and optional SonarCloud maintainability
dashboards. This page covers what works locally without any SaaS account and what maintainers must
configure once.
!!! tip "Works without accounts"
task lint (golangci-lint + go-arch-lint), task arch-lint, task format:check, and
task vulncheck run fully offline after go mod download. No SonarCloud or GitHub secrets
required for day-to-day development.
| Task | Purpose |
|---|---|
task lint |
golangci-lint v2 and go-arch-lint check |
task arch-lint |
Import-graph fitness only (.go-arch-lint.yml) |
task arch-lint:graph |
Render DI dependency graph (--type di --include-vendors) to docs/architecture-graph.svg |
task arch-lint:graph:flow |
Same graph in flow (reverse-DI) view — optional |
task format:check |
gofmt + goimports drift gate (golangci-lint fmt --diff) |
task vulncheck |
govulncheck (module CVE scan) |
task sonar / task sonar:local |
Local SonarCloud upload (maintainer; needs SONARCLOUD_TOKEN) |
Architecture rules live in .go-arch-lint.yml.
See concepts/architecture.md for the intended dependency direction.
- Edit
.go-arch-lint.ymlto describe the target graph (components +mayDependOn). - Run
task arch-lint. - For each existing violation, legalize it in config (add
mayDependOnor# todo(arch-NN)comment) rather than fixing all imports in one PR. - Remove
todoentries incrementally as refactors land.
Generate a dependency graph (optional):
task arch-lint:graph # DI view + vendors (default, linked from concepts/architecture.md)
task arch-lint:graph:flow # reverse-DI / execution-flow viewOutput: docs/architecture-graph.svg (both tasks write the same path; run only one before commit).
Flags are fixed in Taskfile.yml: --type di|flow, --include-vendors. Vendor nodes require the
vendors and per-component canUse entries in .go-arch-lint.yml. Pinned version:
GO_ARCH_LINT_VERSION in Taskfile.yml (invoked via go run …@version, not linked
into the operator module — go-arch-lint's module graph conflicts with go mod tidy).
See concepts/architecture.md § Package boundaries for the rendered graph.
depguard (deny deprecated / non-standard logging and errors) and gomodguard (block logrus,
pkg/errors in go.mod) are configured in .golangci.yaml. The
logcheck plugin is built via hack/tooling/.custom-gcl.yml when make lint runs.
Configuration changes should keep task lint green — adjust depguard / gomodguard.blocked
rules if a legitimate new dependency is blocked.
SonarCloud is optional until SONAR_TOKEN is configured. CI runs the scan with
continue-on-error: true so missing tokens do not block merges.
- Sign in at SonarCloud with a GitHub account that administers the
platformrelayorganization. - Create or import organization
platformrelay(must matchsonar.organizationinsonar-project.properties). - Add project
PlatformRelay_Kollect(must matchsonar.projectKey). - Set visibility to Public (free for OSS).
SonarCloud exposes one project analysis token. Use the same value in two places — different env var names by convention:
| Where | Variable | Notes |
|---|---|---|
| GitHub Actions | SONAR_TOKEN |
Repository secret (Settings → Secrets and variables → Actions) |
| Local (direnv) | SONARCLOUD_TOKEN |
Export in .envrc (gitignored); run direnv allow after edit |
Steps:
- SonarCloud → My Account → Security → Generate Tokens (project analysis token).
- GitHub →
platformrelay/kollect→ add repository secretSONAR_TOKENwith that token. - Locally, add
export SONARCLOUD_TOKEN="<same token>"to.envrc(never commit).
After SONARCLOUD_TOKEN is in .envrc and direnv is loaded:
task sonar # alias for sonar:local
task sonar:local # runs task coverage, then sonar-scanner via DockerRequires Docker. Uploads to org platformrelay, project PlatformRelay_Kollect per
sonar-project.properties. First run may take a few minutes;
confirm the project appears at SonarCloud.
After the first successful scan:
- SonarCloud → project kollect → Quality Gates.
- Prefer Sonar way or a custom gate that fails on new issues only (not legacy debt).
- Enable Pull Request decoration (GitHub App) when ready for PR comments.
| Workflow | When | Coverage |
|---|---|---|
.github/workflows/ci.yaml job sonarcloud |
Every push / PR (after test) |
Downloads coverage artifact (coverage.out) |
.github/workflows/sonarcloud.yaml |
workflow_dispatch manual |
Re-runs task coverage then scans |
Properties file: sonar-project.properties.
Codecov complements the merge gate (task coverage / COVERAGE_MIN in CI) with PR patch
coverage comments, project trends, and the README badge. Uploads are non-blocking
(fail_ci_if_error: false); the test job enforces the coverage floor.
Codecov uploads can succeed without the app, but PR comments and status checks are rate-limited when Codecov calls the GitHub API with a shared token instead of app credentials. If you see “install Codecov GitHub App for reliable uploads/comments” on a PR, complete this step once:
- Open github.com/apps/codecov and click Configure.
- Select the
platformrelayorganization (ownsplatformrelay/kollect). - Grant access to
kollect(or all repositories if you prefer org-wide setup). - Confirm the repo appears at codecov.io/gh/platformrelay/kollect.
No repository secret is required for uploads when CI uses OIDC (see below).
| Item | Location |
|---|---|
| Upload step | .github/workflows/ci.yaml job test — codecov/codecov-action v6 with use_oidc: true |
| Project / patch targets | codecov.yml at repo root |
| Merge gate (blocking) | COVERAGE_MIN env on the same job — independent of Codecov |
The test job requests id-token: write so GitHub Actions can mint an OIDC token for
Codecov upload authentication (Codecov OIDC docs).
Legacy CODECOV_TOKEN repository secrets are optional and ignored when use_oidc: true; you
may remove the secret after verifying uploads on main.
Contributors do not need Codecov accounts. Run task coverage before opening a PR; CI uploads
coverage.out automatically when the test job passes.
Renovate is self-hosted: the Renovate workflow (.github/workflows/renovate.yaml) runs it
hourly (cron 17 * * * *), one run at a time, and a maintainer can start a run by hand with
Run workflow (manual runs log at debug level). It groups updates for Go modules, Kubernetes
libraries, GitHub Actions, container images, the docs npm and pip-compile locks, and the pinned
build tools. Dependabot vulnerability alerts and security updates stay enabled in the repository
settings; there are no Dependabot version-update jobs.
Token. Renovate authenticates with the repository secret RENOVATE_TOKEN, a token owned by
the maintainer (Settings → Secrets and variables → Actions). There is no fallback: when the
secret is empty the first step fails the run and Renovate does not start, because pull requests
opened with the workflow's own token never start CI and could never merge. The job's
GITHUB_TOKEN is read-only and only checks out the repository.
Pinned Renovate. The workflow runs a Renovate image pinned by tag and digest (the
renovate-image input). Renovate updates that pin itself, in a PR of its own that is opened or
updated only on Mondays (UTC), manual runs included, and is never auto-merged: a new Renovate
changes what the bot does, so a maintainer reads it first. Reviewing it: run
bash hack/ci/renovate-config-validate.sh on the PR branch (it uses the new image), and check
in the Renovate release notes that no preset this config extends, config:recommended and the
presets it pulls in, now enables auto-merge. Built-in presets resolve inside the image, so no
guard in this repository reads them.
Dependency dashboard. Renovate keeps a Dependency Dashboard issue open with the pending, rate-limited and failed updates. Its checkboxes take effect on the next run.
What auto-merges. Only Docker image digest updates of the Dockerfiles and the devcontainer
(same tag, new digest). Renovate merges such a PR itself, by rebase, once every check on its head
is green, and rebases it first when it is behind main; it merges at most one branch per run.
Everything else waits for a maintainer: Go modules, the go directive and the golang image tag
(the go toolchain group), GitHub Actions, the pinned tools, the docs packages, the Renovate
image, and any image tag change. A group PR that mixes a digest with any other update is not
auto-merged. The scope is narrow on purpose: RENOVATE_TOKEN is a bypass actor of the main
ruleset, so GitHub would not stop its merge even if a required check never started. A new
third-party image added to a Dockerfile or the devcontainer joins the auto-merge scope.
Rebasing a bot PR. Bot PRs outside the auto-merge scope are rebased only when they conflict.
The exception is a group PR whose first update in Renovate's order is a Dockerfile or
devcontainer image digest: it takes that update's setting, so Renovate rebases it whenever it
falls behind main, but never merges it. Rebase a bot PR before merging with
gh pr update-branch --rebase <number>; the rebase checkbox in the PR body works too, but only
on the next hourly run. Adding a label other than dependencies to a bot PR restarts its E2E
extended run and cancels the one in flight; the cancelled checks stay on that head, and Renovate
holds an auto-merge PR until the next rebase gives it a new head.
Pausing. gh workflow disable renovate.yaml stops the schedule;
gh workflow enable renovate.yaml turns it back on.
Validating the config. Repository rules live in renovate.json, the self-hosted global
config in .github/renovate-config.json. Validate both with
bash hack/ci/renovate-config-validate.sh. It runs renovate-config-validator from the same
pinned image as the bot, so it needs Docker, and the required lint job runs it on every PR.
hack/test/renovate_policy_test.sh guards the workflow, both configs and this section.
The hosted Renovate App is unused. It is still installed on the organisation and has never
opened a PR here; removing it from the repository is the maintainer's job. If it ever starts
working on this repository, two bots race on the same renovate/* branches.
Before a release. Auto-merged digest bumps reach main without a human. Before approving a
release, the maintainer reviews the bot merges since the last tag: the PRs labelled
dependencies merged after it.
| Item | Contributor | Maintainer |
|---|---|---|
task lint / arch-lint |
Run before PR | Keep .go-arch-lint.yml todos current |
SONAR_TOKEN / SONARCLOUD_TOKEN |
— | GitHub secret + .envrc (same token, different names) |
| Codecov GitHub App | — | Install app on platformrelay/kollect for reliable PR comments |
CODECOV_TOKEN |
— | Legacy optional; CI uses OIDC — safe to delete after upload verified |
RENOVATE_TOKEN |
— | Repository secret: the maintainer-owned token Renovate runs as (contents and pull-request write), so bot PRs start CI; no fallback, the Renovate run fails without it |
| SonarCloud quality gate blocking | — | Enable after baseline scan (Phase 1) |
- Testing strategy — coverage floors, CI matrix, Sonar as trend dashboard
- CONTRIBUTING.md — PR lint checklist
- development/setup.md — local dev commands