Skip to content
This repository was archived by the owner on Jul 4, 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
6 changes: 6 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
BasedOnStyle: LLVM
IndentWidth: 4
ColumnLimit: 100
SortIncludes: false
AllowShortIfStatementsOnASingleLine: Never
BreakBeforeBraces: Attach
5 changes: 5 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.git
.DS_Store
ld_preload
ld_preload.so
smoke-artifacts
17 changes: 17 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

[*.go]
indent_style = tab

[*.{c,h}]
indent_style = space
indent_size = 4

[*.md]
trim_trailing_whitespace = false
81 changes: 81 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: CI

on:
push:
branches:
- main
pull_request:
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
lint-and-test:
name: Lint and Test
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v7

- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: "1.26.0"
cache: true

- name: Install clang-format
run: sudo apt-get update && sudo apt-get install -y clang-format

- name: Install Go tools
run: make tools

- name: Verify formatting
run: make fmt-check

- name: Lint and unit test
run: make lint

macos-sanity:
name: macOS Sanity
runs-on: macos-latest

steps:
- name: Checkout
uses: actions/checkout@v7

- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: go.mod
cache: true

- name: Build and test
run: |
go test ./...
make build

docker-smoke:
name: Docker Smoke
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v7

- name: Run Docker smoke harness
run: make docker-smoke

- name: Upload smoke artifacts
if: failure()
uses: actions/upload-artifact@v7
with:
name: smoke-artifacts-${{ github.run_id }}
path: smoke-artifacts/
if-no-files-found: ignore
retention-days: 7
26 changes: 2 additions & 24 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,26 +1,4 @@
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
ld_preload
ld_preload.h
ld_preload.so

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

# Go workspace file
go.work
go.work.sum

# env file
.env

# IDE files
.vscode/
smoke/fixture
smoke-artifacts/
72 changes: 72 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
version: "2"

run:
issues-exit-code: 1
tests: true

output:
formats:
text:
path: stdout
print-linter-name: true
print-issued-lines: true

linters:
enable:
- bidichk
- bodyclose
- errcheck
- errorlint
- gocheckcompilerdirectives
- gocritic
- gosec
- govet
- ineffassign
- misspell
- nakedret
- nilerr
- revive
- staticcheck
- unconvert
- unused
- wastedassign

settings:
errcheck:
check-type-assertions: false
check-blank: false
gocritic:
enabled-checks:
- deferInLoop
disabled-checks:
- ifElseChain
- elseif
nakedret:
max-func-lines: 30

exclusions:
generated: lax
rules:
- linters:
- revive
text: package-comments
- linters:
- revive
text: var-naming
- linters:
- revive
text: unused-parameter

issues:
max-issues-per-linter: 0
max-same-issues: 0
new: false

formatters:
enable:
- gofmt
settings:
gofmt:
simplify: true
exclusions:
generated: lax
16 changes: 16 additions & 0 deletions Dockerfile.smoke
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
ARG GO_BASE_IMAGE=golang:1.26-bookworm

FROM ${GO_BASE_IMAGE}

RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential \
coreutils \
strace \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /workspace

COPY . .

CMD ["./smoke/run.sh"]
11 changes: 11 additions & 0 deletions lefthook.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
pre-commit:
commands:
fmt-check:
run: make fmt-check
lint:
run: make lint

pre-push:
commands:
docker-smoke:
run: make docker-smoke
113 changes: 104 additions & 9 deletions makefile
Original file line number Diff line number Diff line change
@@ -1,26 +1,121 @@
# Define variables
APP_NAME := ld_preload
SRC := .
SO_NAME := ld_preload.so
GOFMT_FILES := $(shell find . -name '*.go' -not -path './vendor/*')
C_FILES := $(shell find . \( -name '*.c' -o -name '*.h' \) -not -path './vendor/*')
GOLANGCI_LINT_VERSION := v2.4.0
STATICCHECK_VERSION := 2025.1.1
LEFTHOOK_VERSION := v1.12.4
GOBIN ?= $(or $(shell go env GOBIN),$(firstword $(subst :, ,$(shell go env GOPATH)))/bin)
GOLANGCI_LINT := $(GOBIN)/golangci-lint
STATICCHECK := $(GOBIN)/staticcheck
LEFTHOOK := $(GOBIN)/lefthook
SMOKE_IMAGE_BOOKWORM := golang:1.26-bookworm
SMOKE_IMAGE_BULLSEYE := golang:1.24-bullseye
SMOKE_TAG_BOOKWORM := ld_preload-smoke:bookworm
SMOKE_TAG_BULLSEYE := ld_preload-smoke:bullseye

.DEFAULT_GOAL := help

.PHONY: help
help:
@printf "Usage:\n"
@printf " make %-12s %s\n" "build" "Build the executable and shared object"
@printf " make %-12s %s\n" "fmt" "Format Go and C sources"
@printf " make %-12s %s\n" "fmt-check" "Verify formatting without changing files"
@printf " make %-12s %s\n" "lint" "Run Go linters and tests"
@printf " make %-12s %s\n" "lint-fix" "Apply supported auto-fixes"
@printf " make %-12s %s\n" "docker-smoke" "Run Linux LD_PRELOAD smoke tests in both Docker images"
@printf " make %-12s %s\n" "docker-smoke-bookworm" "Run smoke tests in Debian bookworm"
@printf " make %-12s %s\n" "docker-smoke-bullseye" "Run smoke tests in Debian bullseye"
@printf " make %-12s %s\n" "hooks" "Install local git hooks with lefthook"
@printf " make %-12s %s\n" "tools" "Install Go-based developer tools"
@printf " make %-12s %s\n" "clean" "Remove build artifacts"

# Default target
.PHONY: all
all: build

# Build target: creates both an executable and a shared object
.PHONY: build
build:
# Build executable
go build -o $(APP_NAME) $(SRC)
# Build shared object
go build -o $(SO_NAME) -buildmode=c-shared $(SRC)
@if [ "$$(uname -s)" = "Linux" ]; then \
go build -o $(SO_NAME) -buildmode=c-shared $(SRC); \
else \
printf '%s\n' 'Skipping shared object build on non-Linux host.'; \
fi

# Run target: implicitly calls build, then runs the application
.PHONY: run
run: build
./$(APP_NAME)

# Clean target: removes the executable and shared object
.PHONY: tools
tools:
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION)
go install honnef.co/go/tools/cmd/staticcheck@$(STATICCHECK_VERSION)
go install github.com/evilmartians/lefthook@$(LEFTHOOK_VERSION)

.PHONY: hooks
hooks:
$(LEFTHOOK) install

.PHONY: fmt
fmt:
gofmt -w $(GOFMT_FILES)
@if command -v clang-format >/dev/null 2>&1; then \
clang-format -i $(C_FILES); \
else \
printf '%s\n' 'clang-format not found; Go files formatted, C files skipped.'; \
fi

.PHONY: fmt-check
fmt-check:
@test -z "$$(gofmt -l $(GOFMT_FILES))" || \
(printf '%s\n' 'Go files need formatting:'; gofmt -l $(GOFMT_FILES); exit 1)
@if command -v clang-format >/dev/null 2>&1; then \
tmpdir=$$(mktemp -d); \
failed=0; \
for file in $(C_FILES); do \
out="$$tmpdir/$$(basename "$$file")"; \
clang-format "$$file" > "$$out"; \
if ! cmp -s "$$file" "$$out"; then \
printf '%s\n' "$$file"; \
failed=1; \
fi; \
done; \
rm -rf "$$tmpdir"; \
test $$failed -eq 0 || (printf '%s\n' 'C files need formatting.'; exit 1); \
else \
printf '%s\n' 'clang-format not found; skipping C format check.'; \
fi

.PHONY: lint
lint:
$(GOLANGCI_LINT) run
$(STATICCHECK) ./...
go test ./...

.PHONY: lint-fix
lint-fix:
$(GOLANGCI_LINT) run --fix
$(MAKE) fmt

.PHONY: docker-smoke
docker-smoke: docker-smoke-bookworm docker-smoke-bullseye

.PHONY: docker-smoke-bookworm
docker-smoke-bookworm:
chmod +x smoke/run.sh
docker build --build-arg GO_BASE_IMAGE=$(SMOKE_IMAGE_BOOKWORM) -f Dockerfile.smoke -t $(SMOKE_TAG_BOOKWORM) .
mkdir -p smoke-artifacts/bookworm
docker run --rm -e SMOKE_SUITE=bookworm -v "$(CURDIR)/smoke-artifacts:/workspace/smoke-artifacts" $(SMOKE_TAG_BOOKWORM)

.PHONY: docker-smoke-bullseye
docker-smoke-bullseye:
chmod +x smoke/run.sh
docker build --build-arg GO_BASE_IMAGE=$(SMOKE_IMAGE_BULLSEYE) -f Dockerfile.smoke -t $(SMOKE_TAG_BULLSEYE) .
mkdir -p smoke-artifacts/bullseye
docker run --rm -e SMOKE_SUITE=bullseye -v "$(CURDIR)/smoke-artifacts:/workspace/smoke-artifacts" $(SMOKE_TAG_BULLSEYE)

.PHONY: clean
clean:
rm -f $(APP_NAME) $(SO_NAME)
rm -f $(APP_NAME) $(SO_NAME) ld_preload.h
Loading
Loading