Skip to content
This repository was archived by the owner on Mar 11, 2026. It is now read-only.
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
17 changes: 17 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "Spindle Dev",
"build": {
"dockerfile": "../Dockerfile"
},
"customizations": {
"vscode": {
"extensions": [
"golang.go"
],
"settings": {
"go.lintTool": "golangci-lint"
}
}
},
"postCreateCommand": "go mod download"
}
40 changes: 40 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: CI

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

permissions:
contents: read

jobs:
ci:
name: Lint, Test & Coverage
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: "1.25"

- name: Lint
uses: golangci/golangci-lint-action@v7
with:
version: latest

- name: Vet
run: go vet ./...

- name: Test
run: go test -race -coverprofile=coverage.out -covermode=atomic ./...

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: coverage.out
33 changes: 33 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Release

on:
push:
tags:
- "v*"

permissions:
contents: write

jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: "1.25"

- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser
version: "~> v2"
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
docs/
bin/
coverage.out
dist/
26 changes: 26 additions & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
version: 2

builds:
- skip: true

changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
- "^chore:"
groups:
- title: Features
regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$'
order: 0
- title: Bug fixes
regexp: '^.*?fix(\([[:word:]]+\))??!?:.+$'
order: 1
- title: Others
order: 999

release:
github:
owner: mutantkeyboard
name: spindle
13 changes: 13 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM golang:1.25-alpine

RUN apk add --no-cache gcc musl-dev git curl

# Install golangci-lint
RUN curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b /usr/local/bin

WORKDIR /workspace

COPY go.mod go.sum ./
RUN go mod download

COPY . .
12 changes: 12 additions & 0 deletions Dockerfile.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM golang:1.25-alpine

RUN apk add --no-cache gcc musl-dev

WORKDIR /src

COPY go.mod go.sum ./
RUN go mod download

COPY . .

CMD ["go", "test", "-race", "-v", "./..."]
53 changes: 53 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
.PHONY: test lint vet fmt check docker-test docker-build clean help

BIN := $(CURDIR)/bin
GOLANGCI_LINT := $(BIN)/golangci-lint

## Testing

test: ## Run tests with race detector
go test -race -v ./...

test-cover: ## Run tests with coverage
go test -race -coverprofile=coverage.out -covermode=atomic ./...
go tool cover -func=coverage.out

bench: ## Run benchmarks
go test -bench=. -benchmem ./...

## Code quality

$(GOLANGCI_LINT):
@mkdir -p $(BIN)
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(BIN)

lint: $(GOLANGCI_LINT) ## Run golangci-lint (downloads to ./bin if missing)
$(GOLANGCI_LINT) run

fmt: ## Format code
gofmt -w .

vet: ## Run go vet
go vet ./...

check: fmt vet lint test ## Run all checks (fmt, vet, lint, test)

## Docker

docker-test: ## Run tests in Docker
docker build -f Dockerfile.test -t spindle-test .
docker run --rm spindle-test

docker-build: ## Build dev Docker image
docker build -t spindle-dev .

## Cleanup

clean: ## Remove build artifacts
rm -f coverage.out
rm -rf dist/

## Help

help: ## Show this help
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-15s\033[0m %s\n", $$1, $$2}'
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Spindle

[![Go Reference](https://pkg.go.dev/badge/github.com/mutantkeyboard/spindle.svg)](https://pkg.go.dev/github.com/mutantkeyboard/spindle)
[![CI](https://github.com/mutantkeyboard/spindle/actions/workflows/ci.yml/badge.svg)](https://github.com/mutantkeyboard/spindle/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/mutantkeyboard/spindle/branch/main/graph/badge.svg)](https://codecov.io/gh/mutantkeyboard/spindle)

Pagination middleware for [Fiber](https://github.com/gofiber/fiber) v3.

Expand Down Expand Up @@ -122,6 +124,25 @@ type PageInfo struct {
- Negative offsets are reset to 0
- Sort fields are validated against `AllowedSorts`

## Development

### Run tests locally

```bash
go test -race -v ./...
```

### Run tests in Docker

```bash
docker build -f Dockerfile.test -t spindle-test .
docker run --rm spindle-test
```

### Dev container

Open this project in VS Code with the Dev Containers extension to get a pre-configured Go development environment.

## Acknowledgements

Heavily inspired by [fiberpaginate](https://github.com/garrettladley/fiberpaginate) by Garrett Ladley.
Expand Down
11 changes: 2 additions & 9 deletions paginate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package spindle

import (
"encoding/json"
"io"
"net/http/httptest"
"reflect"
"testing"
Expand Down Expand Up @@ -53,16 +52,10 @@ func Test_PaginateWithQueries(t *testing.T) {
t.Fatalf("status = %d, want %d", resp.StatusCode, fiber.StatusOK)
}

body := resp.Body
defer body.Close()

bodyBytes, err := io.ReadAll(body)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close() //nolint:errcheck

var respBody Response
if err := json.Unmarshal(bodyBytes, &respBody); err != nil {
if err := json.NewDecoder(resp.Body).Decode(&respBody); err != nil {
t.Fatal(err)
}

Expand Down