From 05c468ccd446cc2f50db54d32278ef1b0b7384ac Mon Sep 17 00:00:00 2001 From: Brian Bechtel Date: Wed, 2 Sep 2026 07:03:10 -0700 Subject: [PATCH 1/2] feat: implement modern repository best practices and CI Gatekeeper --- .agents/AGENTS.md | 7 +- .gitattributes | 22 ++++ .github/ISSUE_TEMPLATE/bug_report.yml | 78 +++++++++++ .github/ISSUE_TEMPLATE/config.yml | 5 + .github/ISSUE_TEMPLATE/feature_request.yml | 51 ++++++++ .github/PULL_REQUEST_TEMPLATE.md | 32 +++++ .github/dependabot.yml | 32 +++++ .github/workflows/benchmark.yml | 7 + .github/workflows/ci.yml | 145 +++++++++++++++++++++ .github/workflows/go.yml | 52 -------- .github/workflows/security.yml | 54 ++++++++ .gitignore | 54 ++++++-- .golangci.yml | 11 +- CONTRIBUTING.md | 122 +++++++++++++++++ README.md | 18 ++- SECURITY.md | 32 +++++ codecov.yml | 23 ++++ llms.txt | 84 ++++++++++++ 18 files changed, 757 insertions(+), 72 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/go.yml create mode 100644 .github/workflows/security.yml create mode 100644 CONTRIBUTING.md create mode 100644 SECURITY.md create mode 100644 codecov.yml create mode 100644 llms.txt diff --git a/.agents/AGENTS.md b/.agents/AGENTS.md index c236f5c..7682fb1 100644 --- a/.agents/AGENTS.md +++ b/.agents/AGENTS.md @@ -103,6 +103,9 @@ For all tests in this repository, strictly adhere to the following conventions: Before concluding any code modification task, agents MUST run and verify the following commands succeed without errors or warnings: 1. `gofmt -s -w .` -2. `go test -race ./...` -3. `golangci-lint run ./...` (or `revive ./... && go vet ./...`) +2. `go mod tidy && git diff --exit-code go.mod go.sum` +3. `go test -race ./...` +4. `golangci-lint run ./...` (or `revive ./... && go vet ./...`) +5. `govulncheck ./...` + diff --git a/.gitattributes b/.gitattributes index 6313b56..b781bd5 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,23 @@ +# Auto-detect text files and normalize line endings to LF * text=auto eol=lf + +# Explicit text files +*.go text eol=lf +*.md text eol=lf +*.yml text eol=lf +*.yaml text eol=lf +*.json text eol=lf +*.mod text eol=lf +*.sum text eol=lf +*.txt text eol=lf + +# Export ignore for archive tarballs +.github export-ignore +.gitignore export-ignore +.gitattributes export-ignore +.golangci.yml export-ignore +codecov.yml export-ignore +CONTRIBUTING.md export-ignore +SECURITY.md export-ignore +AGENTS.md export-ignore +CLAUDE.md export-ignore diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..202597f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,78 @@ +name: 🐛 Bug Report +description: Report a bug, panic, incorrect behavior, or unexpected allocation +title: "[Bug]: " +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + Thank you for reporting an issue! Please provide as much detail as possible to help us diagnose and fix the problem. + + - type: textarea + id: description + attributes: + label: Description + description: A clear and concise description of what the bug is. + placeholder: Describe what happened... + validations: + required: true + + - type: textarea + id: reproduction + attributes: + label: Minimal Reproducible Example + description: | + Please provide a short, self-contained Go code snippet that reproduces the issue. + placeholder: | + ```go + package main + + import ( + "fmt" + "github.com/lock14/collections/treeset" + ) + + func main() { + // Your code here + } + ``` + render: go + validations: + required: true + + - type: textarea + id: expected + attributes: + label: Expected Behavior + description: What did you expect to happen? + placeholder: Explain what behavior was expected... + validations: + required: true + + - type: textarea + id: actual + attributes: + label: Actual Behavior & Output + description: What actually happened? Include error messages or stack traces if applicable. + placeholder: | + panic: runtime error... + validations: + required: true + + - type: input + id: go-version + attributes: + label: Go Version + description: Output of `go version` + placeholder: e.g. go version go1.27.0 windows/amd64 + validations: + required: true + + - type: input + id: environment + attributes: + label: Operating System & Architecture + description: OS and Architecture (e.g. macOS arm64, Linux amd64, Windows amd64) + placeholder: e.g. Linux amd64 + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..541bf49 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Security Vulnerability Reporting + url: https://github.com/lock14/collections/security/policy + about: Please report security vulnerabilities privately according to our security policy. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..3686721 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,51 @@ +name: 💡 Feature Request +description: Propose a new data structure, collection method, or architectural enhancement +title: "[Feature]: " +labels: ["enhancement"] +body: + - type: markdown + attributes: + value: | + Thank you for suggesting a feature! Please describe the use case and API proposal in detail. + + - type: textarea + id: problem + attributes: + label: Problem or Use Case + description: Is your feature request related to a specific problem or missing collection type? + placeholder: I need a data structure that... + validations: + required: true + + - type: textarea + id: proposal + attributes: + label: Proposed Solution / API Design + description: Describe the proposed API, types, constructors, and method signatures. + placeholder: | + ```go + type MyNewDataStructure[T any] struct { ... } + func New[T any]() *MyNewDataStructure[T] + ``` + render: go + validations: + required: true + + - type: textarea + id: performance + attributes: + label: Performance & Complexity Characteristics + description: Describe expected time and space complexity, allocation profile, and growth strategy. + placeholder: | + - Read operations (Get, Contains, Peek): O(1) time, 0 allocations + - Insertions / Deletions: Amortized O(1) + validations: + required: false + + - type: textarea + id: alternatives + attributes: + label: Alternatives Considered + description: Describe any alternative solutions or workarounds you have considered. + validations: + required: false diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..a17239c --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,32 @@ +## Description + +Please provide a summary of the changes introduced in this pull request, including motivation and context. + +## Type of Change + +- [ ] 🐛 Bug fix (non-breaking change which fixes an issue) +- [ ] ✨ New feature (non-breaking change which adds functionality) +- [ ] 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] ⚡ Performance optimization (latency, CPU, memory, or allocation improvements) +- [ ] 📝 Documentation update +- [ ] 🔧 Tooling / CI / Build configuration + +## Repository Design & Architecture Checklist + +- [ ] **Generics & Type Safety:** Uses Go generics cleanly; avoids unnecessary `any` boxing or runtime assertions. +- [ ] **Interface Compliance:** Concrete structures declare compile-time interface assertions (`var _ collections.Interface = (*Type)(nil)`). +- [ ] **Standard Iteration:** Exposes standard Go 1.23+ `iter.Seq` / `iter.Seq2` iterators respecting early yield termination. +- [ ] **Zero-Allocation Reads:** Read operations (`Get`, `Contains`, `Peek`, `Size`, `Empty`) produce 0 B/op and 0 allocs/op in benchmarks. +- [ ] **Stringer:** Implements `fmt.Stringer` matching Go slice `[e1 e2]` or map `map[k:v]` format. +- [ ] **Panic Semantics:** Slice-like indexing panics on out-of-bounds; positional extractions panic on empty; targeted map/set removals are silent no-ops. +- [ ] **Standard Package Layout:** Follows the 4-file package structure (`.go`, `_test.go`, `_bench_test.go`, `example__test.go`). + +## Verification & Testing + +- [ ] Ran `gofmt -s -w .` (no unformatted files). +- [ ] Ran `go test -race ./...` (all unit tests pass without data races). +- [ ] Ran `golangci-lint run ./...` (or `revive ./... && go vet ./...`) with zero warnings. +- [ ] Added table-driven unit tests in `_test.go`. +- [ ] Added/updated benchmarks in `_bench_test.go` and verified no regressions with `benchdiff` / `benchstat`. +- [ ] Added/updated runnable examples with `// Output:` in `example__test.go`. +- [ ] Documentation (Go doc comments, `README.md`, `AGENTS.md`) is updated and synchronized. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..0e45a68 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,32 @@ +version: 2 +updates: + # Go modules + - package-ecosystem: gomod + directory: "/" + schedule: + interval: weekly + day: monday + labels: + - dependencies + - go + open-pull-requests-limit: 10 + groups: + go-minor-patch: + update-types: + - minor + - patch + + # GitHub Actions + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly + day: monday + labels: + - dependencies + - ci + open-pull-requests-limit: 10 + groups: + actions: + patterns: + - "*" diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 046fdf8..76bdf87 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -6,6 +6,13 @@ on: pull_request: branches: [ "main" ] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + jobs: detect-packages: name: Detect Changed Packages diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..31d3c73 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,145 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + format-and-tidy: + name: Format & Module Hygiene + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + + - name: Verify dependencies + run: | + go mod verify + go mod download + + - name: Check Formatting + run: | + if [ -n "$(gofmt -s -l .)" ]; then + echo "The following files are not formatted properly:" + gofmt -s -l . + echo "Please run 'gofmt -s -w .' locally and commit the changes." + exit 1 + fi + + - name: Check go mod tidy + run: | + go mod tidy + git diff --exit-code go.mod go.sum + + test: + name: Test (Go ${{ matrix.go }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + go: + - "1.27" + - "stable" + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: ${{ matrix.go }} + + - name: Build + run: go build -v ./... + + - name: Run tests with race detector + run: go test -v -race -shuffle=on -coverprofile=coverage.out -covermode=atomic ./... + + - name: Upload coverage to Codecov + if: matrix.go == 'stable' + uses: codecov/codecov-action@v5 + with: + files: ./coverage.out + fail_ci_if_error: false + + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + + - name: Run go vet + run: go vet ./... + + - name: Run golangci-lint + uses: golangci/golangci-lint-action@v6 + with: + version: latest + args: --timeout=5m + + govulncheck: + name: Vulnerability Scan + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + + - name: Run govulncheck + uses: golang/govulncheck-action@v1 + with: + go-version-file: 'go.mod' + + # CI Gatekeeper Aggregator Job + # This single job acts as the required status check for branch protection rules. + ci-gatekeeper: + name: CI Gatekeeper + runs-on: ubuntu-latest + needs: + - format-and-tidy + - test + - lint + - govulncheck + if: always() + steps: + - name: Evaluate Upstream Job Statuses + run: | + echo "Evaluating CI pipeline job results:" + echo "Format & Tidy: ${{ needs.format-and-tidy.result }}" + echo "Test Matrix: ${{ needs.test.result }}" + echo "Lint: ${{ needs.lint.result }}" + echo "Govulncheck: ${{ needs.govulncheck.result }}" + + if [ "${{ needs.format-and-tidy.result }}" != "success" ] || \ + [ "${{ needs.test.result }}" != "success" ] || \ + [ "${{ needs.lint.result }}" != "success" ] || \ + [ "${{ needs.govulncheck.result }}" != "success" ]; then + echo "❌ CI Gatekeeper failed: One or more upstream checks did not succeed." + exit 1 + fi + + echo "✅ All required CI checks passed successfully!" diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml deleted file mode 100644 index 5896cf4..0000000 --- a/.github/workflows/go.yml +++ /dev/null @@ -1,52 +0,0 @@ -# This workflow will build a golang project -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go - -name: Go CI - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -jobs: - build-and-test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version-file: 'go.mod' - - - name: Check Formatting - run: | - if [ -n "$(gofmt -s -l .)" ]; then - echo "The following files are not formatted properly:" - gofmt -s -l . - echo "Please run 'gofmt -s -w .' locally and commit the changes." - exit 1 - fi - - - name: Vet - run: go vet ./... - - - name: golangci-lint - uses: golangci/golangci-lint-action@v6 - continue-on-error: true - with: - version: latest - args: --timeout=5m - - - name: Build - run: go build -v ./... - - - name: Test - run: go test -v -coverprofile=coverage.out ./... - - - name: Upload coverage - uses: codecov/codecov-action@v5 - with: - files: ./coverage.out - fail_ci_if_error: false diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 0000000..71c732b --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,54 @@ +name: Security + +on: + push: + branches: [main] + pull_request: + branches: [main] + schedule: + # Run weekly security scan on Mondays at 00:00 UTC + - cron: '0 0 * * 1' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + security-events: write + +jobs: + govulncheck: + name: Govulncheck + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + + - name: Run govulncheck + uses: golang/govulncheck-action@v1 + with: + go-version-file: 'go.mod' + + codeql: + name: CodeQL Analysis + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: go + + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 diff --git a/.gitignore b/.gitignore index e528ae7..51f4177 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,48 @@ -.idea +# Binaries and build artifacts +bin/ +dist/ +*.exe +*.exe~ +*.dll +*.so +*.dylib -# Coverage files -coverage.out -coverage -cov -cov.out -c.out -*.out +# Test binaries and coverage artifacts *.test +*.out +coverage.txt +coverage.html +coverage/ +cov/ +c.out +cov.out + +# Performance profiling artifacts +*.pprof +*.prof +cpu.out +mem.out +bench.txt +bench-old.txt +bench-new.txt + +# IDEs and editors +.idea/ +.vscode/ +*.swp +*.swo +*~ + +# Operating System files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Local environment & tools +.env +.env.local +.mise.local.toml diff --git a/.golangci.yml b/.golangci.yml index ef98e1e..209c1ff 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -16,15 +16,17 @@ issues: linters: disable-all: true enable: + - errcheck + - gocyclo - govet - ineffassign - misspell - - gocyclo + - nolintlint - revive - staticcheck + - unconvert - unused - - errcheck - - nolintlint + - whitespace linters-settings: gocyclo: @@ -33,6 +35,9 @@ linters-settings: rules: - name: exported - name: package-comments + - name: blank-imports + - name: dot-imports + - name: var-naming nolintlint: require-explanation: true require-specific: true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..7f3c684 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,122 @@ +# Contributing to collections + +Thank you for your interest in contributing to `github.com/lock14/collections`! This document outlines our repository standards, architectural guidelines, development workflow, and testing requirements. + +--- + +## 1. Repository Design Principles + +* **Generics & Type Safety:** Leverage Go generics (`any`, `comparable`, `cmp.Ordered`, custom constraints). Avoid `interface{}` / `any` boxing or runtime type assertions. Sorted collections must provide both `New` accepting a custom `comparator.Comparator[T]` and `NewOrdered` for `cmp.Ordered` types. +* **Interface Compliance & Compile-Time Assertions:** Concrete data structures should implement the generic collection interfaces defined in `collections.go` (`Collection[T]`, `MutableCollection[T]`, `List[T]`, `Queue[T]`, `Stack[T]`, `Deque[T]`, `Set[T]`, `Map[K, V]`, etc.) and `fmt.Stringer`. Every struct must declare compile-time interface assertions: + ```go + var ( + _ collections.MutableSet[T] = (*HashSet[T])(nil) + _ fmt.Stringer = (*HashSet[T])(nil) + ) + ``` +* **Standard Iteration (Go 1.23+ `iter`):** + * Expose standard range-over-func iterators using `iter.Seq[T]` (e.g. `All()`) or `iter.Seq2[K, V]` (e.g. `All()`, `Keys()`, `Values()`). + * Iterators must terminate immediately when `!yield(...)` returns `false` without leaking resources. + * Iterator implementations should minimize or eliminate allocation overhead in loops. +* **Non-Concurrent by Design:** Implementations are single-threaded by contract (matching Go's standard slice/map philosophy). Do not add internal mutexes or sync primitives to collection structs. Concurrency is strictly the caller's responsibility. +* **Zero-Allocation Reads:** Read operations (`Get`, `Contains`, `Peek`, `PeekFront`, `PeekBack`, `Size`, `Empty`) must avoid heap allocations (0 B/op and 0 allocs/op in benchmarks). +* **Predictable Memory & Growth:** Growth policies should be amortized O(1) (e.g. geometric resizing) and support explicit capacity APIs (`NewWithCapacity`). +* **Panic & Error Handling Alignment with Built-in Types:** + * **Slice-Like Indexing (`Get`, `Set`):** Bounds check violations (`idx < 0 || idx >= size`) MUST panic with a clear index error (matching built-in Go `slice[i]` panic). + * **Consumable Extraction & Positional Access (`Remove()`, `First()`, `Last()`, `Peek()`, `Pop()`, `RemoveFront()`, `RemoveBack()`, `PollFirst()`, `PollLast()`):** Extraction or access on an empty collection MUST panic with a clear error (matching built-in `s[0]` / `s[len-1]` semantics). + * **Targeted Deletions & Map Lookups (`RemoveElement(T)`, `Map.Remove(K)`, `Map.Get(K)`):** Targeted removal of a specific element or key is a silent no-op if absent (matching `delete(m, k)`). Key lookup uses the comma-ok idiom `(V, bool)` without panicking on missing keys. +* **String Representations (`fmt.Stringer`):** + * `Collection[T]`: Formatted as `[e1 e2 e3]` (space-separated, `[]` when empty). + * `Map[K, V]`: Formatted as `map[k1:v1 k2:v2]` (`map[]` when empty). + * `Graph[V]` / `LabeledGraph[V, L]`: Formatted as `graph[...]` or `digraph[...]`. + +--- + +## 2. Package File Structure + +Single-collection packages (e.g. `hashset`, `hashmap`, `arraydeque`, `treeset`, etc.) MUST consist of exactly four files: + +1. **`.go`**: Primary implementation file containing type declarations, compile-time assertions, configuration options, constructors, and public/private methods. +2. **`_test.go`**: Table-driven unit tests verifying functional correctness, edge cases, and bounds checks (`package `). +3. **`_bench_test.go`**: Performance benchmarks measuring throughput and zero-allocation invariants (`package `). +4. **`example__test.go`**: Consumer-facing runnable examples with `// Output:` comments (`package _test`). + +--- + +## 3. Development Setup & Workflow + +### Prerequisites + +* Go 1.27 or higher +* Git + +### Clone & Test + +```bash +# Clone the repository +git clone https://github.com/lock14/collections.git +cd collections + +# Verify dependencies +go mod verify + +# Run all unit tests with race detection +go test -race ./... + +# Run all benchmarks +go test -bench=. -benchmem ./... +``` + +### Table-Driven Testing + +All unit tests must be table-driven: +* Define a `cases` slice of structs containing `name string`. +* Iterate with `for _, tc := range cases`. +* Rebind `tc := tc` and invoke `t.Run(tc.name, func(t *testing.T) { ... })`. +* Use `t.Parallel()` where appropriate with independent collection instances. + +### Benchmarking & Performance Regression Verification + +* Benchmark functions belong in `_bench_test.go` named `func Benchmark_(b *testing.B)`. +* Always call `b.ReportAllocs()`. +* Isolate setup with `b.ResetTimer()`. +* Read paths must show 0 B/op and 0 allocs/op. +* To verify no performance regressions against the `main` branch locally: + ```bash + go install filippo.io/mostly-harmless/benchdiff@latest + benchdiff -base-ref origin/main -- -run '^$' -bench . -benchmem -count=6 ./... + ``` + +--- + +## 4. Pre-Completion Verification Checklist + +Before opening a pull request or submitting code, ensure all of the following commands execute without errors or warnings: + +```bash +# 1. Format code +gofmt -s -w . + +# 2. Verify module hygiene +go mod tidy +git diff --exit-code go.mod go.sum + +# 3. Static analysis & Vet +go vet ./... +golangci-lint run ./... + +# 4. Security vulnerability check +govulncheck ./... + +# 5. Run tests with race detection +go test -race ./... +``` + +--- + +## 5. Pull Request Guidelines + +1. **Branch Naming:** Use clear branch names such as `feat/new-collection`, `fix/bounds-check`, `perf/hash-distribution`. +2. **Commit Messages:** Write concise, descriptive commit messages following the Conventional Commits specification (e.g. `feat: add LinkedHashSet`, `fix: correct deque wrap-around index`). +3. **PR Description:** Fill out the [Pull Request Template](.github/PULL_REQUEST_TEMPLATE.md) completely, detailing changes, architectural adherence, and benchmark comparisons. +4. **Documentation Sync:** When adding or modifying APIs, update the corresponding Go doc comments, `README.md`, and `AGENTS.md` in the same pull request. diff --git a/README.md b/README.md index 05d33bb..fbeafcf 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,12 @@ # collections [![Go Version](https://img.shields.io/github/go-mod/go-version/lock14/collections)](https://go.dev/) -[![Build Status](https://img.shields.io/github/actions/workflow/status/lock14/collections/go.yml?branch=main)](https://github.com/lock14/collections/actions) -[![golangci-lint](https://img.shields.io/badge/golangci--lint-enabled-brightgreen)](https://golangci-lint.run/) +[![CI](https://img.shields.io/github/actions/workflow/status/lock14/collections/ci.yml?branch=main&label=CI)](https://github.com/lock14/collections/actions/workflows/ci.yml) [![Benchmarks](https://img.shields.io/github/actions/workflow/status/lock14/collections/benchmark.yml?branch=main&label=Benchmarks)](https://github.com/lock14/collections/actions/workflows/benchmark.yml) +[![Security](https://img.shields.io/github/actions/workflow/status/lock14/collections/security.yml?branch=main&label=Security)](https://github.com/lock14/collections/actions/workflows/security.yml) [![Coverage](https://img.shields.io/codecov/c/github/lock14/collections)](https://codecov.io/gh/lock14/collections) [![Go Reference](https://pkg.go.dev/badge/github.com/lock14/collections.svg)](https://pkg.go.dev/github.com/lock14/collections) +[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE) Generic data structures for Go 1.27+. Focuses on type safety, minimal allocations, and predictable performance. @@ -20,17 +21,17 @@ package main import ( "fmt" - + "github.com/lock14/collections/treeset" ) func main() { set := treeset.NewOrdered[int]() - + set.Add(5) set.Add(1) set.Add(10) - + for val := range set.All() { fmt.Println(val) } @@ -77,9 +78,12 @@ Implementations in this library are **not thread-safe** by design, matching Go s ## Contributing -Submit PRs with passing tests and benchmarks. Table-driven testing is required. Performance regressions will not be merged. +We welcome contributions! Please review our [Contributing Guidelines](CONTRIBUTING.md) for architectural invariants, testing standards, and pull request verification instructions. + +## Security + +Please report security issues responsibly according to our [Security Policy](SECURITY.md). ## License Apache 2.0. See [LICENSE](LICENSE). - diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..3189a0b --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,32 @@ +# Security Policy + +## Supported Versions + +We support security updates for the following versions of `collections`: + +| Version | Supported | +| ------- | ------------------ | +| Latest | :white_check_mark: | +| < 1.0 | :x: | + +## Reporting a Vulnerability + +We take the security of `collections` seriously. If you discover a security vulnerability, please report it responsibly by following these steps: + +1. **Do not** report security vulnerabilities through public GitHub issues or discussions. +2. Please use the [GitHub Private Vulnerability Reporting](https://github.com/lock14/collections/security/advisories/new) feature on this repository. +3. If private vulnerability reporting is unavailable, email the maintainer directly. + +### What to Include in Your Report + +To help us triage and resolve the issue quickly, please include: +* A detailed description of the vulnerability. +* Steps to reproduce the issue or a minimal Go code reproducer. +* Impact assessment (e.g. denial of service, memory corruption, unexpected data exposure). +* Any suggested fixes or mitigations. + +### Response Timeline + +* We will acknowledge receipt of your vulnerability report within 48 hours. +* We will provide a status update and estimated timeline for a fix within 7 days. +* Once a fix is verified, a patch release and security advisory will be published. diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..6655aba --- /dev/null +++ b/codecov.yml @@ -0,0 +1,23 @@ +codecov: + require_ci_to_pass: yes + +coverage: + precision: 2 + round: down + range: "70...100" + + status: + project: + default: + target: 80% + threshold: 1% + patch: + default: + target: 80% + threshold: 1% + +ignore: + - "**/*_bench_test.go" + - "**/example_*_test.go" + - ".github/**" + - "*.md" diff --git a/llms.txt b/llms.txt new file mode 100644 index 0000000..161bfd6 --- /dev/null +++ b/llms.txt @@ -0,0 +1,84 @@ +# collections + +> Generic, high-performance data structures and interfaces for Go 1.27+. + +## Overview + +`github.com/lock14/collections` provides idiomatic, generic data structures designed for type safety, zero-allocation reads, predictable memory growth, and seamless standard Go iteration (`iter.Seq` and `iter.Seq2`). The library avoids `interface{}` boxing, reflection, and runtime assertions. + +## Quick Start + +```bash +go get github.com/lock14/collections +``` + +```go +package main + +import ( + "fmt" + + "github.com/lock14/collections/treeset" +) + +func main() { + set := treeset.NewOrdered[int]() + set.Add(5) + set.Add(1) + set.Add(10) + + for val := range set.All() { + fmt.Println(val) + } +} +``` + +## Core Interfaces (`collections.go`) + +- `Iterable[T]`: Types providing `All() iter.Seq[T]` iteration. +- `Collection[T]`: Base interface with `Size() int`, `Empty() bool`, and `All() iter.Seq[T]`. +- `MutableCollection[T]`: Supports `Add(T)`, `Remove() T`, `AddAll(iter.Seq[T])`, and `Clear()`. +- `List[T]` / `MutableList[T]`: Indexable sequences with `Get(idx int)` and `Set(idx int, t T)`. +- `Queue[T]` / `MutableQueue[T]`: FIFO queues with `Peek() T`. +- `Stack[T]` / `MutableStack[T]`: LIFO stacks with `Push(T)`, `Pop() T`, and `Peek() T`. +- `Deque[T]` / `MutableDeque[T]`: Double-ended queues with `AddFront`, `AddBack`, `RemoveFront`, `RemoveBack`, `PeekFront`, `PeekBack`. +- `Set[T]` / `MutableSet[T]`: Unique element collections with `Contains(T)`, `RemoveElement(T)`. +- `SortedSet[T]`: Ordered sets with `First()`, `Last()`, `Floor(T)`, `Ceiling(T)`, `Lower(T)`, `Higher(T)`, and range queries (`SubSet`, `HeadSet`, `TailSet`). +- `Map[K, V]` / `MutableMap[K, V]`: Associative key-value mappings with `Get(K) (V, bool)`, `Put(K, V)`, `Remove(K)`, `Keys()`, `Values()`. +- `SortedMap[K, V]`: Ordered key-value maps with navigational queries (`FirstKey`, `LastKey`, `FloorKey`, etc.) and range views. +- `Graph[V]` / `LabeledGraph[V, L]`: Directed and undirected graph structures. +- `Trie[K, V]` / `MutableTrie[K, V]`: Prefix trees supporting string and generic slice keys with prefix queries (`KeysWithPrefix`, `LongestPrefixOf`). + +## Available Packages + +- `arraydeque`: Double-ended queue backed by a dynamic circular buffer (`New`, `NewWithCapacity`). +- `arraylist`: Dynamically resizing generic array list (`New`, `NewWithCapacity`). +- `bitset`: High-performance 64-bit word-aligned dense integer bitset (`New`, `NewWithCapacity`). +- `comparator`: Type-safe comparison primitives (`NaturalOrder`, `Reverse`, `Compare`). +- `graph`: Directed and undirected graph implementations. +- `hashmap`: Open-addressing / bucket-based generic hash map (`New`, `NewWithCapacity`). +- `hashset`: Hash-table backed generic set (`New`, `NewWithCapacity`). +- `heap`: Binary min/max priority heap (`New`, `NewOrdered`, `NewWithCapacity`). +- `labeledgraph`: Directed and undirected graphs with edge labels. +- `linkedhashmap`: Hash map maintaining insertion or access order (`New`, `NewWithCapacity`). +- `linkedhashset`: Hash set maintaining insertion or access order (`New`, `NewWithCapacity`). +- `linkedlist`: Generic doubly-linked list (`New`). +- `treemap`: Sorted key-value map backed by a B-Tree (`New`, `NewOrdered`). +- `treeset`: Sorted set backed by a B-Tree (`New`, `NewOrdered`). +- `trie`: String- and generic slice-keyed prefix trees (`NewStringTrie`, `NewSliceTrie`). + +## Key Design Invariants + +- **Non-Concurrent:** Collections are not thread-safe by default, matching Go's built-in `slice` and `map` semantics. +- **Zero-Allocation Reads:** `Get`, `Contains`, `Peek`, `Size`, and `Empty` do not allocate on the heap. +- **Panic Semantics:** + - Slice indexing out-of-bounds panics (matching Go built-in slice index panic). + - Empty collection positional access/extraction panics (matching Go `s[0]` / `slices.Min` panic). + - Key lookup uses comma-ok `(V, bool)`; targeted deletion `Map.Remove(K)` / `Set.RemoveElement(T)` is a silent no-op if key is absent. +- **Standard Stringer:** `Collection[T]` outputs `[e1 e2]`; `Map[K, V]` outputs `map[k1:v1 k2:v2]`. + +## References + +- Repository: https://github.com/lock14/collections +- Go Reference: https://pkg.go.dev/github.com/lock14/collections +- Issues: https://github.com/lock14/collections/issues From 2e4a9d4e333969cb35d3017fa51851bdde2756c5 Mon Sep 17 00:00:00 2001 From: Brian Bechtel Date: Wed, 2 Sep 2026 07:06:17 -0700 Subject: [PATCH 2/2] fix(ci): use revive and native go vet for Go 1.27 lint quality gate --- .gitattributes | 1 + .github/workflows/ci.yml | 9 ++++----- revive.toml | 24 ++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 revive.toml diff --git a/.gitattributes b/.gitattributes index b781bd5..6c4783b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -16,6 +16,7 @@ .gitignore export-ignore .gitattributes export-ignore .golangci.yml export-ignore +revive.toml export-ignore codecov.yml export-ignore CONTRIBUTING.md export-ignore SECURITY.md export-ignore diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 31d3c73..69c5000 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -91,11 +91,10 @@ jobs: - name: Run go vet run: go vet ./... - - name: Run golangci-lint - uses: golangci/golangci-lint-action@v6 - with: - version: latest - args: --timeout=5m + - name: Install and run revive + run: | + go install github.com/mgechev/revive@latest + revive -config revive.toml -formatter friendly ./... govulncheck: name: Vulnerability Scan diff --git a/revive.toml b/revive.toml new file mode 100644 index 0000000..915bece --- /dev/null +++ b/revive.toml @@ -0,0 +1,24 @@ +# Revive configuration file +# https://github.com/mgechev/revive + +ignoreGeneratedHeader = false +severity = "error" +confidence = 0.8 + +[rule.blank-imports] +[rule.context-as-argument] +[rule.dot-imports] +[rule.error-return] +[rule.error-strings] +[rule.error-naming] +[rule.exported] +[rule.if-return] +[rule.increment-decrement] +[rule.var-naming] +[rule.package-comments] +[rule.range] +[rule.receiver-naming] +[rule.time-naming] +[rule.unexported-return] +[rule.indent-error-flow] +[rule.errorf]