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
102 changes: 102 additions & 0 deletions docs/go-licensing-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,32 @@ Public projects in the Benjamin Borbe ecosystem use **BSD-2-Clause** (BSD-style)

## 1. LICENSE File

### RULE go-licensing/license-file-required (MUST)

**Owner**: license-assistant
**Applies when**: a Go project published to `github.com/*` (public) does not have a `LICENSE` file in its repo root.
**Enforcement**: judgment (file-existence check; ast-grep cannot detect file absence)
**Why**: GitHub's repo metadata, package managers (`go.mod` consumers), and downstream Linux distros all key off the root `LICENSE` file. Without it, consumers can't legally redistribute, GitHub's "License" badge stays empty, and `go list -m all` license aggregators report the project as unlicensed. Private/internal repos (`bitbucket.seibert.tools` etc.) are exempt.

#### Bad

```
repo/
├── README.md
├── go.mod
└── main.go # no LICENSE file at root — public consumers can't redistribute
```

#### Good

```
repo/
├── LICENSE # BSD-2-Clause, copyright year matches project creation
├── README.md
├── go.mod
└── main.go
```

Place a `LICENSE` file in the root directory with the BSD-2-Clause license text:

```
Expand Down Expand Up @@ -67,6 +93,47 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

## 2. README License Section

### RULE go-licensing/readme-license-section-required (MUST)

**Owner**: license-assistant
**Applies when**: a public Go project's `README.md` has no `## License` (H2) section pointing at the root `LICENSE` file.
**Enforcement**: judgment (markdown section presence; ast-grep doesn't parse markdown structure)
**Why**: README is the first thing a consumer reads. Without a License section pointing at the LICENSE file, downstream users have to scroll the repo file tree to discover the license — friction that turns into "I'll just use a different library." The section is one line of work and removes a real adoption barrier.

#### Bad

```markdown
# my-go-tool

CLI for processing widgets.

## Usage

...

## Contributing

...

# (no License section — consumers must guess)
```

#### Good

```markdown
# my-go-tool

CLI for processing widgets.

## Usage

...

## License

BSD-style license. See [LICENSE](LICENSE) file for details.
```

Add this at the end of README.md:

```markdown
Expand All @@ -83,6 +150,34 @@ Keep it simple - the full text is in the LICENSE file.

## 3. Source File License Headers

### RULE go-licensing/source-file-header-required (MUST)

**Owner**: license-assistant
**Applies when**: a public Go project has `*.go` files outside `vendor/` without the 3-line BSD-2-Clause header block at the top.
**Enforcement**: `addlicense -check` invocation via `make precommit` (the canonical tool; ast-grep can detect missing headers via first-line regex, but `addlicense` is already wired through the toolchain).
**Why**: License headers per source file are a redistribution requirement under the BSD-2-Clause terms. The 3-line header makes every file self-describing for legal review and prevents the "this file was copied into my project without provenance" failure mode that bites at audit time. `addlicense` automates the maintenance — `make precommit` should never let a public project commit a Go file without the header.

#### Bad

```go
// errors.go (public repo, no header)
package errors

func Wrap(...) error { ... }
```

#### Good

```go
// Copyright (c) 2026 Benjamin Borbe All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package errors

func Wrap(...) error { ... }
```

All `.go` files (excluding `vendor/`) must have license headers:

```go
Expand Down Expand Up @@ -139,6 +234,13 @@ go run -mod=mod github.com/google/addlicense \
- Optionally update to a range when making substantial changes (e.g., `2020-2025`)
- Not updating years is fine - it's conservative and acceptable

### RULE go-licensing/copyright-year-discipline (MUST)

**Owner**: license-assistant
**Applies when**: a PR diff modifies copyright years in `*.go` source-file headers — either bulk-updating across many files or setting future / non-numeric years (`2099`, `present`, etc.).
**Enforcement**: judgment (diff inspection — ast-grep can detect `Copyright (c) 2099` shapes but the "bulk-update for trivial changes" trigger needs PR-scope reasoning)
**Why**: Copyright years record when copyright was *established* on a file, not when the file was last touched. Bulk year-updates obscure the original publication date (legally meaningful for derivative-work claims) and produce noisy diffs that bury the real change. Future / non-numeric years break OSS license aggregators and look unprofessional to downstream auditors.

### What NOT to Do
- Don't bulk update all years just because it's a new year
- Don't update years for trivial formatting changes
Expand Down
36 changes: 36 additions & 0 deletions rules/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,42 @@
"level": "MUST",
"owner": "go-http-handler-assistant"
},
{
"anchor": "go-licensing/copyright-year-discipline",
"applies_when": "a PR diff modifies copyright years in `*.go` source-file headers — either bulk-updating across many files or setting future / non-numeric years (`2099`, `present`, etc.).",
"doc_path": "docs/go-licensing-guide.md",
"enforcement": "judgment (diff inspection — ast-grep can detect `Copyright (c) 2099` shapes but the \"bulk-update for trivial changes\" trigger needs PR-scope reasoning)",
"id": "go-licensing/copyright-year-discipline",
"level": "MUST",
"owner": "license-assistant"
},
{
"anchor": "go-licensing/license-file-required",
"applies_when": "a Go project published to `github.com/*` (public) does not have a `LICENSE` file in its repo root.",
"doc_path": "docs/go-licensing-guide.md",
"enforcement": "judgment (file-existence check; ast-grep cannot detect file absence)",
"id": "go-licensing/license-file-required",
"level": "MUST",
"owner": "license-assistant"
},
{
"anchor": "go-licensing/readme-license-section-required",
"applies_when": "a public Go project's `README.md` has no `## License` (H2) section pointing at the root `LICENSE` file.",
"doc_path": "docs/go-licensing-guide.md",
"enforcement": "judgment (markdown section presence; ast-grep doesn't parse markdown structure)",
"id": "go-licensing/readme-license-section-required",
"level": "MUST",
"owner": "license-assistant"
},
{
"anchor": "go-licensing/source-file-header-required",
"applies_when": "a public Go project has `*.go` files outside `vendor/` without the 3-line BSD-2-Clause header block at the top.",
"doc_path": "docs/go-licensing-guide.md",
"enforcement": "`addlicense -check` invocation via `make precommit` (the canonical tool; ast-grep can detect missing headers via first-line regex, but `addlicense` is already wired through the toolchain).",
"id": "go-licensing/source-file-header-required",
"level": "MUST",
"owner": "license-assistant"
},
{
"anchor": "go-prometheus/composed-metrics-interface",
"applies_when": "a single `Metrics` interface aggregates methods spanning two or more distinct functional domains (handlers + senders + schedulers + …), forcing consumers to depend on methods they don't use.",
Expand Down
Loading