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
11 changes: 5 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@

name: CI

on:
push:
branches: [ master ]
branches: [master]
pull_request:
branches: [ master ]
branches: [master]

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
go: [ '1.24' ]
go: ["1.26"]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v7

- name: Setup Go
uses: actions/setup-go@v3
uses: actions/setup-go@v7
with:
go-version: ${{ matrix.go }}

Expand Down
196 changes: 193 additions & 3 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
version: "2"

run:
go: "1.24"
go: "1.26"
timeout: 5m
tests: false
tests: true
issues-exit-code: 1
modules-download-mode: readonly
allow-parallel-runners: true
Expand All @@ -12,7 +12,7 @@ issues:
max-issues-per-linter: 0
max-same-issues: 0
new: false
fix: false
fix: true

output:
formats:
Expand All @@ -27,6 +27,7 @@ formatters:
enable:
- gofmt
- goimports
- gofumpt

linters:
settings:
Expand All @@ -46,7 +47,160 @@ linters:
- G306
- G501
- G505
tagliatelle:
case:
rules:
json: snake # JSON: snake_case (user_id)
yaml: snake # YAML: snake_case
xml: camel # XML: camelCase
mapstructure: snake # mapstructure: snake_case
env: upperSnake # ENV: UPPER_SNAKE_CASE
varnamelen:
min-name-length: 2 # Минимальная длина имени
max-distance: 5 # i, j, k допустимы в scope <= 5 строк
ignore-names:
- err # err — идиоматично
- ok # ok — идиоматично
- id # id — часто используется
- db # db — часто используется
- tx # tx — транзакция
- wg # wg — WaitGroup
- mu # mu — mutex
- rw # rw — RWMutex
- ch # ch — channel
- fn # fn — function
- sb # sb — strings.Builder
- ctx # ctx — context
- q # q — querier
- r # r — repo / reader
- s # s — strategy / service
- f # f — filter
- a # a — left operand (сравнение)
- b # b — right operand (сравнение)
ignore-type-assert-ok: true # Игнорировать v, ok := x.(T)
ignore-map-index-ok: true # Игнорировать v, ok := m[k]
ignore-chan-recv-ok: true # Игнорировать v, ok := <-ch
ignore-decls:
- i int # for i := ...
- j int # вложенные циклы
- n int # количество
- t testing.T # тесты
- b testing.B # бенчмарки
- r *http.Request # HTTP handler
- w http.ResponseWriter # HTTP handler
- c *gin.Context # Gin context
- c echo.Context # Echo context
- s *Server # конструктор Server
- m *metrics # конструктор metrics
revive:
severity: warning
rules:
# -------------------------------------------------------------------------
# Предотвращение багов
# -------------------------------------------------------------------------
- name: atomic # Проверяет правильное использование sync/atomic
- name: range-val-in-closure # Захват переменной цикла в замыкании
- name: range-val-address # Взятие адреса переменной цикла
- name: unreachable-code # Недостижимый код после return/panic
- name: unchecked-type-assertion # Type assertion без проверки ok
- name: datarace # Потенциальные data races
- name: identical-branches # Одинаковые ветки if/else
- name: defer # Проблемы с defer (в циклах, результат)
- name: call-to-gc # Явные вызовы runtime.GC()
- name: waitgroup-by-value # WaitGroup передан по значению

# -------------------------------------------------------------------------
# Обработка ошибок — Go proverb: "Don't just check errors, handle them gracefully"
# -------------------------------------------------------------------------
- name: error-strings # Ошибки не должны начинаться с большой буквы
- name: error-return # error должен быть последним возвращаемым значением
- name: errorf # Использовать fmt.Errorf вместо errors.New + fmt.Sprintf
- name: unhandled-error # Необработанные ошибки
arguments:
- "fmt.Print"
- "fmt.Printf"
- "fmt.Println"

# -------------------------------------------------------------------------
# Сложность — Go proverb: "Clear is better than clever"
# -------------------------------------------------------------------------
- name: cognitive-complexity
arguments: [15] # Cognitive complexity <= 15
- name: cyclomatic
arguments: [10] # Cyclomatic complexity <= 10
- name: function-result-limit
arguments: [3] # Максимум 3 возвращаемых значения
- name: argument-limit
arguments: [5] # Максимум 5 аргументов функции

# -------------------------------------------------------------------------
# Чистота кода — Go proverb: "A little copying is better than a little dependency"
# -------------------------------------------------------------------------
- name: indent-error-flow # if err != nil { return } вместо else
- name: early-return # Ранний возврат вместо вложенности
- name: superfluous-else # Лишний else после return
- name: if-return # Упрощение if/return
- name: empty-block # Пустые блоки кода
- name: unnecessary-stmt # Ненужные операторы
- name: redundant-import-alias # import pkg "pkg" — лишний алиас
- name: confusing-results # Запутанные возвращаемые значения
- name: bool-literal-in-expr # if x == true → if x
- name: constant-logical-expr # Константные логические выражения
- name: modifies-parameter # Модификация параметров функции
- name: modifies-value-receiver # Модификация value receiver (бесполезно)
- name: redefines-builtin-id # Переопределение встроенных идентификаторов
- name: string-of-int # string(int) — частая ошибка
- name: time-equal # time.Time сравнение через ==
- name: unconditional-recursion # Безусловная рекурсия (бесконечный цикл)
- name: useless-break # break в конце case (Go делает это автоматически)

# -------------------------------------------------------------------------
# Хорошие практики — Go proverbs
# -------------------------------------------------------------------------
- name: context-as-argument # context.Context первым аргументом
- name: context-keys-type # Ключи контекста должны быть типизированы
- name: var-declaration # var x = 1 → x := 1
- name: blank-imports # Запрет blank imports кроме main/test
- name: dot-imports # Запрет dot imports
- name: unexported-return # Публичная функция возвращает приватный тип
- name: exported # Экспортируемые идентификаторы должны быть задокументированы
arguments:
- "checkPrivateReceivers"
- "disableStutteringCheck"

# -------------------------------------------------------------------------
# Именование — Go proverb: "Good naming is like good coding: concise"
# -------------------------------------------------------------------------
- name: var-naming
arguments:
- [
"ID",
"URL",
"API",
"HTTP",
"JSON",
"XML",
"DB",
"SQL",
"UUID",
"UID",
"GUID",
"TTL",
"TCP",
"UDP",
"IP",
"RPC",
"QPS",
"EOF",
]
- name: package-comments # Пакеты должны иметь комментарии
- name: receiver-naming # Имена receiver (r, s, c, не this/self)
exclusions:
rules:
- path: pki/internal/xocsp/ocsp.go
linters:
- revive
- nestif
paths:
- vendors/
default: none
Expand All @@ -66,3 +220,39 @@ linters:
- errorlint
- bodyclose
- gosec
- nilerr
- nilnesserr
- nilnil
- bidichk
- contextcheck
- fatcontext
- makezero
- forcetypeassert
- unconvert
- copyloopvar
- prealloc
- perfsprint
- gocritic
- goconst
- mnd
- revive
- predeclared
- reassign
- recvcheck
- asciicheck
- importas
- durationcheck
- tparallel
- thelper
- usetesting
- musttag
- errchkjson
- tagalign
- usestdlibvars
- nestif
- mirror
- whitespace
- decorder
- nonamedreturns
- inamedparam
- testpackage
2 changes: 1 addition & 1 deletion .lic.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
author: Mikhail Knyazhev <markus621@yandex.ru>
author: Mikhail Knyazhev <markus621@yandex.com>
lic_short: "BSD 3-Clause"
lic_file: LICENSE
25 changes: 25 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Agent instructions

## Project

- This repository is the Go module `go.osspkg.com/errors`, a single package at the repository root. It targets Go 1.26.
- Keep the public error API compatible. `Trace` is exported; `Wrap` with multiple inputs must preserve each input for standard `errors.Is` and `errors.As` while retaining the colon-separated error text and legacy `Cause` behavior.
- Tests for the public API use the external package `errors_test`.

## Commands

Run commands from the repository root.

- `make lint` runs `goppy lint`, which performs Go module tidy/download and formatting before `golangci-lint` and `govulncheck`. It can change files; inspect `git status` and the diff afterward.
- `make tests` runs `goppy test`.
- `make build` runs `goppy build --arch=amd64`.
- CI runs `make ci`. This target also installs `goppy@latest`, runs `goppy setup-lib` and the license target, then lint, tests, and build. Use it when the full CI sequence and local setup are intended.

## Project memory

For non-trivial work, use the Chroma collection `chat_go-errors_memory`:

1. Ensure the collection exists before reading or writing. If missing, list collections and create this exact collection with the default embedding configuration.
2. Query it with a concise semantic description of the current task before making design decisions.
3. After the work, add a concise document only for a durable project decision or lesson. Query related memories first and update an existing document instead of duplicating it.
4. Never store secrets, transcripts, or temporary command output. Memory supplements the current source and tests; it does not override them.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
BSD 3-Clause License

Copyright (c) 2024-2025, Mikhail Knyazhev <markus621@yandex.ru>
Copyright (c) 2024-2026, Mikhail Knyazhev <markus621@yandex.com>

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ SHELL=/bin/bash

.PHONY: install
install:
go install go.osspkg.com/goppy/v2/cmd/goppy@latest
go install go.osspkg.com/goppy/v3/cmd/goppy@latest
goppy setup-lib

.PHONY: lint
Expand Down
68 changes: 67 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,67 @@
# go-errors
# go-errors

[![CI](https://github.com/osspkg/go-errors/actions/workflows/ci.yml/badge.svg)](https://github.com/osspkg/go-errors/actions/workflows/ci.yml)

A small Go library for creating errors, adding context, retaining causes, and inspecting error chains. It has no third-party runtime dependencies and requires Go 1.26 or newer.

## Installation

```sh
go get go.osspkg.com/errors
```

## Quick start

```go
package main

import (
"errors"
"fmt"

errpkg "go.osspkg.com/errors"
)

var errConnectionRefused = errors.New("connection refused")

func main() {
err := errpkg.Wrapf(errConnectionRefused, "dial %s", "db.example:5432")
if errors.Is(err, errConnectionRefused) {
fmt.Println(err)
}
}
```

## API

| API | Purpose |
| --- | --- |
| `New(message string) error` | Creates an error with a message. |
| `Wrapf(cause error, message string, args ...any) error` | Adds formatted context to one cause; returns `nil` when `cause` is `nil`. |
| `Wrap(errors ...error) error` | Combines non-nil errors, joining their messages with `: `. Every input remains discoverable through `errors.Is` and `errors.As`; returns `nil` if there are no non-nil inputs. |
| `Trace(cause error, message string, args ...any) error` | Adds formatted context and a runtime stack trace; returns `nil` when `cause` is `nil`. |
| `Queue(calls ...func() error) error` | Calls functions in order and returns the first error, or `nil` if all succeed. Callbacks must be non-nil. |
| `Unwrap(err error) error` | Returns one underlying error from an `Unwrapper`; returns `nil` for nil errors and multi-cause errors. |
| `Cause(err error) error` | Follows the legacy `Cause() error` chain and returns its terminal error. |
| `Is(err, target error) bool` | Reports whether `err` or an error in its chain matches `target`. |
| `As(err error, target any) bool` | Finds the first error in the chain assignable to `target`, following the standard `errors.As` contract. |
| `Causer` | Interface for errors exposing `Cause() error`. |
| `Unwrapper` | Interface for errors exposing `Unwrap() error`. |

`Cause` follows legacy `Cause()` methods; it does not walk standard-library `%w` wrappers. Use `Is` or `As` to inspect those chains. For a `Wrap` call with several errors, `Cause` retains its legacy behavior and returns the final input, while `Is` and `As` inspect all inputs.

## Development

The repository uses Go 1.26 and `goppy` for its Makefile targets.

```sh
make lint
make tests
make build
```

CI runs `make ci`, which installs `goppy@latest`, runs `goppy setup-lib` and license generation, then runs lint, tests, and build.

## License

BSD 3-Clause. See [LICENSE](LICENSE).
Loading
Loading