Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Shared editor defaults for ODC repositories.
# Bootstrapped from dev-kit by common.mk. Commit a local .editorconfig file to override it.
#
# Kept in sync with treefmt.toml on purpose: shfmt reads this file for shell
# style, so the editor and `make fmt` cannot disagree.

root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 2

[*.go]
indent_style = tab
indent_size = 4

[{Makefile,*.mk}]
indent_style = tab

[*.md]
trim_trailing_whitespace = false

[*.{sh,bash}]
switch_case_indent = true
space_redirects = true

[*.{json,yml,yaml,toml}]
indent_size = 2
6 changes: 6 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
## What

<!-- One sentence summary -->

Closes #

## Why

<!-- Motivation / problem being solved. Skip if obvious from the linked issue. -->

## Testing

<!-- How was this tested? e.g. unit, envtest, manual against vX.Y.Z -->

## Notes for reviewers

<!-- CRD/API changes, RBAC changes, new watches, breaking changes, upgrade path.
Delete if not applicable. -->

## Checklist

- [ ] Tests added/updated
- [ ] No breaking changes (or upgrade path documented above)
- [ ] Readable commit history (squashed and cleaned up as desired)
Expand Down
32 changes: 32 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Lint

permissions:
contents: read

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
lint:
runs-on: ubuntu-24.04
defaults:
run:
shell: nix develop --command bash -e {0}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
- name: Set up nix with the shared Cachix cache
uses: ./.github/actions/setup-nix
with:
cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }}
cachix-signing-key: ${{ secrets.CACHIX_SIGNING_KEY }}
- name: fmt
run: make fmt
- name: verify formatting
uses: ./.github/actions/diff-check
- name: lint
run: make lint
8 changes: 4 additions & 4 deletions .github/workflows/release-drafter.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ on:
branches:
- main

# pull_request event is required only for autolabeler
# pull_request event is required only for autolabeler.
# pull_request_target is required for the autolabeler to support PRs from forks
# pull_request_target:
# types: [opened, reopened, synchronize]
pull_request:
# Only following types are handled by the action, but one can default to all as well
types: [opened, reopened, synchronize]
# pull_request_target event is required for autolabeler to support PRs from forks
# pull_request_target:
# types: [opened, reopened, synchronize]

permissions:
contents: read
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
example/common.mk
example/bin/
.pre-commit-config.yaml
.common.mk-configs
100 changes: 100 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Baseline golangci-lint configuration for ODC repositories.
# Bootstrapped from dev-kit by common.mk; commit a local .golangci.yml file to override it.
version: "2"
linters:
default: none
settings:
dogsled:
max-blank-identifiers: 4
exhaustive:
default-signifies-exhaustive: true
godot:
scope: declarations
capital: false
gosec:
excludes:
- G101 # Look for hardcoded credentials
- G204 # Audit use of command execution
- G306 # Poor file permissions used when writing to a file
modernize:
disable:
- omitzero
nlreturn:
block-size: 2
staticcheck:
checks: ["all", "-ST1000", "-ST1001", "-ST1003", "-ST1005", "-ST1012", "-ST1016", "-ST1020", "-ST1021", "-ST1022", "-QF1001", "-QF1003", "-QF1008"]
exclusions:
generated: lax
presets: [comments, common-false-positives, legacy, std-error-handling]
paths: [third_party, builtin$, examples$]
warn-unused: true
enable:
- asasalint
- asciicheck
- bidichk
- bodyclose
- canonicalheader
- contextcheck
- copyloopvar
- decorder
- dogsled
- dupword
- durationcheck
- errcheck
- errchkjson
- errname
- exhaustive
- exptostd
- forbidigo
- ginkgolinter
- gocheckcompilerdirectives
- gochecksumtype
- gocritic
- godoclint
- godot
- goprintffuncname
- gosec
- gosmopolitan
- govet
- grouper
- ineffassign
- interfacebloat
- intrange
- loggercheck
- makezero
- mirror
- misspell
- modernize
- musttag
- nakedret
- nilerr
- nlreturn
- noctx
- nosprintfhostport
- predeclared
- promlinter
- protogetter
- reassign
- sloglint
- staticcheck
- testableexamples
- unconvert
- unparam
- unused
- usestdlibvars
- usetesting
- wastedassign
formatters:
enable: [gci, gofmt]
settings:
gci:
sections:
- standard # Standard section: captures all standard packages.
- default # Default section: contains all imports that could not be matched to another section type.
- localmodule # Local module section: contains all local packages. This section is not present unless explicitly enabled.
- blank # Blank section: contains all blank imports. This section is not present unless explicitly enabled.
- dot # Dot section: contains all dot imports. This section is not present unless explicitly enabled.
custom-order: true
exclusions:
generated: lax
paths: [third_party, builtin$, examples$]
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# dev-kit is the source of common.mk, so it includes its own copy directly. The
# self-update check skips repositories where common.mk is tracked by git, and
# DEV_KIT_CONFIGS resolves to nothing here because the shared configs live in
# this repository already.
DEV_KIT_VERSION := main
DEV_KIT_FORMATTING := on
include common.mk
126 changes: 96 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,93 @@ Copy the files from `example/` into your project and adjust them for your needs.

The included `common.mk` provides:

| Target | Description |
| --- | --- |
| `help` | Display all available targets |
| `clean` | Remove the `bin/` directory |
| `mod` | Run `go mod tidy`, `download`, and `verify` |
| `golangci-lint` | Run golangci-lint |
| `shellcheck` | Run shellcheck on shell scripts |
| `scan` | Scan for vulnerabilities using osv-scanner |
| `setup-local-cluster` | Create a Kind cluster for local development |
| `repo-settings` | Reconcile GitHub repository settings |
| `envtest-binaries-sideload` | Populate the envtest cache from upstream K8s/etcd releases |
| Target | Description |
| --------------------------- | -------------------------------------------------------------------------------------------- |
| `help` | Display all available targets |
| `clean` | Remove the `bin/` directory |
| `fmt` | Format every file in the repository |
| `lint` | Check formatting, then run shellcheck and, when the repository has a `go.mod`, golangci-lint |
| `fmt-all` | Formatting only (what `fmt` calls) |
| `lint-all` | Fail if any file is not formatted (what `lint` calls) |
| `mod` | Run `go mod tidy`, `download`, and `verify` |
| `golangci-lint` | Run golangci-lint |
| `shellcheck` | Run shellcheck on shell scripts |
| `scan` | Scan for vulnerabilities using osv-scanner |
| `setup-local-cluster` | Create a Kind cluster for local development |
| `repo-settings` | Reconcile GitHub repository settings |
| `envtest-binaries-sideload` | Populate the envtest cache from upstream K8s/etcd releases |

### Formatting and linting

Formatting is **opt-in**. Set it in the project `Makefile` before
`-include common.mk`, next to `DEV_KIT_VERSION`:

```make
DEV_KIT_FORMATTING := on
```

While it is off, no formatting runs: `fmt` does nothing beyond whatever the
project's own `fmt:` rule does, and `lint` runs `shellcheck` and
`golangci-lint` exactly as before, fetching only `.golangci.yml`.

This is opt-in because a repository that still defines its own `fmt:` or `lint:`
recipe would otherwise have `fmt-all`/`lint-all` merged in as prerequisites
silently on its next `DEV_KIT_VERSION` bump — make appends prerequisites without
warning — and start failing on every YAML, Markdown and shell file it has never
formatted. Run the one-time `make fmt-all` commit first, then switch it on
deliberately.

One formatter front-end for the whole repository. `treefmt` drives gofmt (via
`golangci-lint fmt`, so import order stays configured in one place), `yamlfmt`,
`shfmt` and `mdformat`.

```sh
make fmt # writes
make lint # fails on drift, then shellcheck and golangci-lint (Go repos)
```

The Go steps are conditional on a `go.mod` in the repository root: without one,
`lint` skips golangci-lint, `fmt` does not install it, `.golangci.yml` is not
fetched, and treefmt is passed `--allow-missing-formatter` so it does not insist
on a formatter it will never call. None of that needs configuring by hand.

`treefmt.toml`, `.golangci.yml` and `.editorconfig` are fetched from dev-kit at
the pinned `DEV_KIT_VERSION` the first time they are needed, and refetched when
that version changes. A repository that commits its own copy of one of these
files keeps it. The fetch rule only fires for a file that is absent, and an
alternate spelling counts as an override (`.golangci.yaml`, `.treefmt.toml`).
Add the bootstrapped ones to `.gitignore`, together with `.common.mk-configs`,
next to the existing `common.mk` entry — `.common.mk-configs` records what was
fetched so that a `DEV_KIT_VERSION` bump only ever removes files this mechanism
created, never a hand-written override that is not committed yet.

Refresh happens when the `DEV_KIT_VERSION` string changes. With a moving ref
(`main`) the configs are therefore fetched once and not refreshed again, while
`common.mk` itself keeps updating via its hourly content check.

`shfmt` reads its style from `.editorconfig`, so an editor and `make fmt` cannot
disagree about shell scripts.

Repositories extend the entry points by adding prerequisites rather than
redefining them, which keeps the recipe in `common.mk`:

```make
fmt: license-headers
lint: license-headers-check
```

### Variables

| Variable | Default | Description |
| --- | --- | --- |
| `BUILD_PATH` | `$(shell pwd)` | Base directory for local binaries |
| `LOCALBIN` | `$(BUILD_PATH)/bin` | Directory for installed binaries |
| `OSV_SCANNER_CONFIG` | `./.osv-scanner.toml` | Path to osv-scanner configuration |
| `OS` | `$(shell $(GO) env GOOS)` | Current Operating System |
| `ARCH` | `$(shell $(GO) env GOARCH)` | Current CPU architecture |
| Variable | Default | Description |
| -------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ |
| `BUILD_PATH` | `$(shell pwd)` | Base directory for local binaries |
| `LOCALBIN` | `$(BUILD_PATH)/bin` | Directory for installed binaries |
| `OSV_SCANNER_CONFIG` | `./.osv-scanner.toml` | Path to osv-scanner configuration |
| `DEV_KIT_FORMATTING` | `off` | `on` enables the shared `fmt`/`lint` formatting. Set it before `-include common.mk` |
| `DEV_KIT_CONFIGS` | `.editorconfig .golangci.yml treefmt.toml` | Shared configs to bootstrap; set to empty to opt out. Each file is skipped automatically when the repo overrides or does not need it |
| `TREEFMT_ARGS` | derived | Extra treefmt flags. Defaults to `--allow-missing-formatter` in repos without a `go.mod`, empty otherwise |
| `OS` | `$(shell $(GO) env GOOS)` | Current Operating System |
| `ARCH` | `$(shell $(GO) env GOARCH)` | Current CPU architecture |

Any binary defined in your `tools.lock` is also available as a Make target
(e.g. `make $(CONTROLLER_GEN)`). Take a look at the variables defined in
Expand Down Expand Up @@ -122,7 +188,7 @@ It configures:
The repository settings are configurable via make variables (set them in your `Makefile` or pass them on the command line, e.g. `make repo-settings REPO_STATUS_CHECKS='["CI","lint"]' REPO_RULESET_BRANCHES='["release/*"]'`):

| Variable | Default | Description |
| --- | --- | --- |
| -------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `REPO_ALLOW_MERGE_COMMIT` | `true` | Allow merge commits in the merge strategy |
| `REPO_ALLOW_SQUASH_MERGE` | `false` | Allow squash merging |
| `REPO_ALLOW_REBASE_MERGE` | `false` | Allow rebase merging |
Expand All @@ -140,11 +206,11 @@ The repo ships composite actions that consuming repositories reference directly.
Pin them to a SHA with a version comment, as the action-pin check requires. The
`<40-char-sha>` placeholders below are not copy-pasteable — substitute real digests:

| Action | Description |
| --- | --- |
| `.github/actions/setup-nix` | Install upstream Nix and enable the shared Cachix cache |
| `.github/actions/get-go-version` | Extract the Go version from `flake.nix` |
| `.github/actions/diff-check` | Fail if a command left uncommitted changes behind |
| Action | Description |
| -------------------------------- | ------------------------------------------------------- |
| `.github/actions/setup-nix` | Install upstream Nix and enable the shared Cachix cache |
| `.github/actions/get-go-version` | Extract the Go version from `flake.nix` |
| `.github/actions/diff-check` | Fail if a command left uncommitted changes behind |

`setup-nix` replaces the two-step Nix installer + Cachix preamble that every
job needs before it can use `nix develop` as its shell. Secrets are not visible
Expand All @@ -167,18 +233,18 @@ jobs:
- run: make test
```

| Input | Default | Description |
| --- | --- | --- |
| `cachix-name` | `opendefensecloud` | Cachix cache to use |
| `cachix-auth-token` | `''` | Auth token; empty falls back to a read-only cache, as on fork PRs |
| `cachix-signing-key` | `''` | Signing key; empty disables pushing to the cache |
| Input | Default | Description |
| -------------------- | ------------------ | ----------------------------------------------------------------- |
| `cachix-name` | `opendefensecloud` | Cachix cache to use |
| `cachix-auth-token` | `''` | Auth token; empty falls back to a read-only cache, as on fork PRs |
| `cachix-signing-key` | `''` | Signing key; empty disables pushing to the cache |

### Default git hooks

The dev shell installs the following git hooks automatically:

| Hook | Stage | Description |
| --- | --- | --- |
| ------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------- |
| `fmt` | `pre-commit` | Runs `make fmt` |
| `lint` | `pre-commit` | Runs `make lint` |
| `osv-scanner` | `pre-commit` | Runs `make scan` on dependency file changes (disabled by default — see [Vulnerability scanning](#vulnerability-scanning)) |
Expand Down
Loading
Loading