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
143 changes: 143 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# Contributing

Thanks for your interest in the project. This guide covers the setup, the conventions, and
what a change has to satisfy before it can merge.

## Development environment

All dev tooling comes from the Nix flake; `task` wraps every command in `nix develop -c`, so
you never need to enter a shell manually.

**Prerequisites:**

- **Nix** with flakes enabled — [Determinate Nix Installer](https://install.determinate.systems/nix)
- **go-task** — `brew install go-task` (global, deliberately not in the flake)
- **Docker or Podman** — for integration tests. Either works; the Taskfile detects which.
- **gh** (optional) — for PRs and CI status

```bash
git clone https://github.com/jasoet/pkg.git
cd pkg

task nix:check # verify the toolchain
task docker:check # verify a container runtime
task test
```

Run `task --list` to see everything available. **Use `task <name>` rather than raw `go`
commands** — the tasks carry the flags and environment that CI uses, so "works locally"
means something.

## Making a change

1. **Branch off `next`.** v3 development happens there; `main` carries the released state.
Name branches `feat/...`, `fix/...`, `docs/...`, `ci/...`.

2. **Write the test first.** Every behaviour change needs a test that failed before the fix.
Unit tests carry no build tag, integration tests use `//go:build integration`.

3. **Verify before you push:**

```bash
task check # tests + lint
go vet -tags='example integration argo' ./... # tagged code CI also compiles
```

The tagged vet matters: `task check` only compiles untagged code, and example- or
integration-only breakage has slipped past that gap before.

4. **Open a PR against `next`** and let CI finish. Merges are squash-only, so the PR title
becomes the commit message — write it as a proper Conventional Commit.

## Conventions

### Commit messages

[Conventional Commits](https://www.conventionalcommits.org/) — they drive releases, so the
type is not cosmetic:

```
<type>(<scope>): <description>

[optional body]

[optional footer]
```

Types: `feat`, `fix`, `docs`, `refactor`, `test`, `chore`, `perf`, `ci`, `style`.
Scope is the package (`fix(db): ...`).

**Breaking changes need a `BREAKING CHANGE:` footer** *and* an entry in
[MIGRATION.md](./MIGRATION.md). Several v3 breaks shipped in `fix:`-typed commits without a
footer and would never have reached the release notes — the migration guide is the backstop,
so update it in the same PR as the break.

### Package conventions

Every configurable package must:

1. Expose a functional-options constructor — `New(opts ...Option) (T, error)` where
construction can fail.
2. Carry `OTelConfig *otel.Config` tagged exactly `yaml:"-" mapstructure:"-"`.
3. Expose `WithOTelConfig(cfg *otel.Config) Option` as the single OTel injection point.
4. Instrument through `otel.Layers.Start*()` at layer boundaries.
5. Back README snippets with `Example*` tests, so documentation cannot drift from a
compiling API.
6. Use testify (`assert`/`require`).

These are enforced mechanically by `internal/archtest` — **adding a package means extending
its registry**, or the convention test fails. See [CONTEXT.md](./CONTEXT.md) for the
vocabulary and [docs/adr/](./docs/adr/) for why the conventions are shaped this way.

### Documentation

Docs are treated as part of the product, not an afterthought. When a change affects the
public API, update in the same PR:

- the package's `README.md`
- `MIGRATION.md`, if consumers must do something
- `INSTRUCTION.md` / `README.md`, for structural changes
- `AI_PATTERN.md`, which indexes consumer patterns

Do not write a README snippet you have not compiled.

### Adding a package

1. Create `newpkg/`, `newpkg/README.md`, `newpkg/newpkg.go`, `newpkg/newpkg_test.go`.
2. Follow the six package conventions above.
3. Register it in `internal/archtest`.
4. Add an example under `examples/newpkg/` behind `//go:build example`.
5. Update the `README.md` package table and `AI_PATTERN.md`.

## Testing

| Tier | Build tag | Command |
|---|---|---|
| Unit | none | `task test` |
| Integration | `integration` | `task test:integration` (needs Docker/Podman) |
| Argo | `argo` | `task test:argo` (needs a k8s cluster with Argo) |
| Examples | `example` | `go build -tags=example ./...` |
| Everything | — | `task test:complete` |

Integration tests use testcontainers with a 15-minute timeout; always
`defer container.Terminate(ctx)`.

## Git authorship

**Commits must be authored solely by a human contributor.** Do not add AI tools as
co-authors, committers, or contributors — no `Co-authored-by`, `Generated by`, or
`Created with` trailers, and no AI attribution in commit messages, PR descriptions, or code
comments. This applies to every commit, including those produced with tool assistance.

## Versioning

- **v3** (`next` branch) — the only line receiving features.
- **v2** (`release/v2`) — frozen at v2.13.1, emergency patches only.
- **v1** (`release/v1`) — closed at v1.6.0.

See [ADR 0001](./docs/adr/0001-freeze-v2-and-ship-v3-as-one-big-bang.md) for the reasoning.

## Reporting issues

Include the package, the version, a minimal reproduction, and what you expected instead.
For security issues, please report privately rather than opening a public issue.
34 changes: 18 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,9 @@ Examples for all packages live in the top-level `examples/` directory (e.g. `./e

## Test Coverage

Coverage combines the unit and integration suites (Argo tests require a k8s cluster and are not included). Per-package figures are shown next to each package in the [Packages](#packages) section above; regenerate the full report with `task test:complete` (writes `output/coverage-all.html`).
Per-package figures are shown next to each package in the [Packages](#packages) section above. They come from the unit **and** integration suites — regenerate with `task test:integration` (needs Docker or Podman; writes `output/coverage-integration.html`).

Argo tests are excluded from those figures because they need a live k8s cluster with Argo Workflows. To include them, run `task test:complete` (writes `output/coverage-complete.html`).

### Run Tests

Expand All @@ -144,7 +146,7 @@ task test:integration

# Complete test suite with coverage report
task test:complete
open output/coverage-all.html
open output/coverage-complete.html
```

## Key Features
Expand Down Expand Up @@ -237,7 +239,7 @@ logger := config.GetLogger("my-service")
```

**Features:** Automatic instrumentation, context propagation, graceful shutdown
**Coverage:** 84.8% | **[Examples](./examples/otel/)** | **[Documentation](./otel/README.md)**
**Coverage:** 89.5% | **[Examples](./examples/otel/)** | **[Documentation](./otel/README.md)**

#### [config](./config/) - Configuration Management
Type-safe YAML configuration with environment variable overrides.
Expand All @@ -254,7 +256,7 @@ cfg, _ := config.LoadString[AppConfig](yamlContent, "APP")
```

**Features:** Environment variable overrides, nested env vars, generics-based loading
**Coverage:** 97.6% | **[Examples](./examples/config/)** | **[Documentation](./config/README.md)**
**Coverage:** 96.4% | **[Examples](./examples/config/)** | **[Documentation](./config/README.md)**

### Data Access

Expand All @@ -279,7 +281,7 @@ pool.Find(&users)
```

**Features:** Connection pooling, migrations, OTel tracing, health monitoring
**Coverage:** 76.7% | **[Examples](./examples/db/)** | **[Documentation](./db/README.md)**
**Coverage:** 83.5% | **[Examples](./examples/db/)** | **[Documentation](./db/README.md)**

#### [docker](./docker/) - Docker Container Executor
Production-ready Docker container management with dual API styles.
Expand Down Expand Up @@ -313,7 +315,7 @@ exec, _ := docker.NewFromRequest(req)
```

**Features:** Lifecycle management, wait strategies, log streaming, dual API (functional + struct)
**Coverage:** 83.1% | **[Examples](./examples/docker/)** | **[Documentation](./docker/README.md)**
**Coverage:** 84.1% | **[Examples](./examples/docker/)** | **[Documentation](./docker/README.md)**

#### [argo](./argo/) - Argo Workflows Client
Production-ready Argo Workflows client with flexible configuration.
Expand Down Expand Up @@ -343,7 +345,7 @@ ctx, client, err := argo.NewClientWithOptions(ctx,
```

**Features:** Multiple connection modes, functional options, OTel support, proper error handling
**[Examples](./examples/argo/)** | **[Documentation](./argo/README.md)**
**Coverage:** 94.8% | **[Examples](./examples/argo/)** | **[Documentation](./argo/README.md)**

#### [retry](./retry/) - Retry with Exponential Backoff
Production-ready retry mechanism using `cenkalti/backoff/v4` with OTel instrumentation.
Expand All @@ -364,7 +366,7 @@ return retry.Permanent(fmt.Errorf("invalid config"))
```

**Features:** Exponential backoff, context-aware, OTel tracing, permanent error marking
**[Documentation](./retry/README.md)**
**Coverage:** 100.0% | **[Examples](./examples/retry/)** | **[Documentation](./retry/README.md)**

#### [base32](./base32/) - Crockford Base32 Encoding
Crockford Base32 encoding with CRC-10 checksums for human-readable, error-correcting identifiers.
Expand All @@ -386,7 +388,7 @@ normalized := base32.NormalizeBase32("ab-CD iL o9") // "ABCD1109"
```

**Features:** URL-safe alphabet, automatic error correction, CRC-10 checksums, compact encoding
**[Examples](./examples/base32/)** | **[Documentation](./base32/README.md)**
**Coverage:** 100.0% | **[Examples](./examples/base32/)** | **[Documentation](./base32/README.md)**

### HTTP & gRPC

Expand Down Expand Up @@ -416,7 +418,7 @@ if err := srv.Start(); err != nil { // blocks until srv.Shutdown(ctx)
```

**Features:** Health checks, graceful shutdown, middleware
**Coverage:** 77.1% | **[Examples](./examples/server/)** | **[Documentation](./server/README.md)**
**Coverage:** 97.0% | **[Examples](./examples/server/)** | **[Documentation](./server/README.md)**

#### [grpc](./grpc/) - gRPC Server
Production-ready gRPC with Echo gateway integration.
Expand All @@ -435,7 +437,7 @@ server.Start()
```

**Features:** H2C mode, dual HTTP/gRPC, gateway, observability
**Coverage:** 71.2% | **[Examples](./examples/grpc/)** | **[Documentation](./grpc/README.md)**
**Coverage:** 82.0% | **[Examples](./examples/grpc/)** | **[Documentation](./grpc/README.md)**

#### [rest](./rest/) - HTTP Client
Resilient REST client with OTel tracing.
Expand All @@ -457,7 +459,7 @@ response, _ := client.MakeRequestWithTrace(ctx, "GET", url, "", headers)
```

**Features:** Retries, tracing, middleware support
**Coverage:** 92.9% | **[Examples](./examples/rest/)** | **[Documentation](./rest/README.md)**
**Coverage:** 93.0% | **[Examples](./examples/rest/)** | **[Documentation](./rest/README.md)**

### Utilities

Expand All @@ -478,7 +480,7 @@ results, _ := concurrent.ExecuteConcurrently(ctx, funcs)
```

**Features:** Go 1.26+ generics, error aggregation, context support
**Coverage:** 95.1% | **[Examples](./examples/concurrent/)** | **[Documentation](./concurrent/README.md)**
**Coverage:** 100.0% | **[Examples](./examples/concurrent/)** | **[Documentation](./concurrent/README.md)**

#### [temporal](./temporal/) - Workflow Orchestration
Temporal workflow integration with observability.
Expand All @@ -501,7 +503,7 @@ handle, _ := manager.CreateWorkflowSchedule(ctx, "daily-job", temporal.WorkflowS
```

**Features:** Schedule management, workers, job definitions, monitoring
**Coverage:** 81.2% | **[Examples](./examples/temporal/)** | **[Documentation](./temporal/README.md)**
**Coverage:** 84.5% | **[Examples](./examples/temporal/)** | **[Documentation](./temporal/README.md)**

#### [ssh](./ssh/) - SSH Tunneling
Secure SSH tunneling and port forwarding.
Expand All @@ -523,7 +525,7 @@ defer tunnel.Close()
```

**Features:** Port forwarding, connection pooling, error handling
**Coverage:** 78.2% | **[Examples](./examples/ssh/)** | **[Documentation](./ssh/README.md)**
**Coverage:** 85.6% | **[Examples](./examples/ssh/)** | **[Documentation](./ssh/README.md)**

#### [compress](./compress/) - File Compression
Secure file compression with validation.
Expand All @@ -540,7 +542,7 @@ compress.TarGz("/path/to/directory", outputFile)
```

**Features:** gzip, tar.gz, security validation, path traversal protection
**Coverage:** 82.4% | **[Examples](./examples/compress/)** | **[Documentation](./compress/README.md)**
**Coverage:** 85.3% | **[Examples](./examples/compress/)** | **[Documentation](./compress/README.md)**

## Contributing

Expand Down
3 changes: 1 addition & 2 deletions docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,5 @@ if err := exec.Start(ctx); err != nil {

## Related Packages

- **[otel](../otel/)** - OpenTelemetry v2 configuration and utilities
- **[otel](../otel/)** - OpenTelemetry configuration, utilities, and structured logging
- **[config](../config/)** - Configuration management with validation
- **[logging](../logging/)** - Structured logging with context
Loading
Loading