Skip to content
Open
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
34 changes: 34 additions & 0 deletions .github/workflows/ci-test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Test & Build

on:
pull_request:
push:
Comment thread
aperezg marked this conversation as resolved.
branches:
- main

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
go: [ '1.27' ]
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Go
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: ${{ matrix.go }}
- name: Run unit tests
run: go test -race ./...
- name: Run static checks
run: go vet ./...
- name: Run acceptance tests
run: make acceptance
- name: Check for vulnerabilities
run: |
go run golang.org/x/vuln/cmd/govulncheck@v1.9.0 ./...
go run golang.org/x/vuln/cmd/govulncheck@v1.9.0 -tags=acceptance ./...
27 changes: 0 additions & 27 deletions .github/workflows/main.yaml

This file was deleted.

10 changes: 5 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: '1.21'
go-version: '1.27'
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v4

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice to see the actions being pinned to SHAs here. It could be worth pinning the ones this PR does not touch as well, for consistency: goreleaser-action@v4 and the docker/* actions further down (setup-qemu, setup-buildx, login, metadata, build-push). These are the ones that run with the release and Docker Hub secrets, so they benefit the most.

Not a blocker for this PR, it can be a follow-up.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My idea is improve all related with the release on other PR that will depends from this one, that will be ok for you?

if: startsWith(github.ref, 'refs/tags/')
Expand All @@ -32,7 +32,7 @@ jobs:
name: Deploy to DockerHub
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Set up QEMU
uses: docker/setup-qemu-action@v3
Expand Down Expand Up @@ -61,4 +61,4 @@ jobs:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
tags: ${{ steps.meta.outputs.tags }}
32 changes: 32 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Repository Guidelines

## Project Structure & Module Organization

Killgrave is a Go HTTP mock server. `cmd/killgrave/main.go` is the executable entry point; `internal/app/` wires the application together, and `internal/app/cmd/` defines the Cobra CLI. Configuration and file watching live in `internal/`; HTTP routing, imposters, proxying, and response handling live in `internal/server/http/`.

Unit tests sit beside source files as `*_test.go`. Fixtures live in `internal/test/testdata/` and `internal/server/http/test/testdata/`. Process-level acceptance tests and `.txtar` fixtures live in `acceptance/`. Consult `README.md` for configuration and imposter examples.

## Build, Test, and Development Commands

Use Go 1.27, matching `go.mod` and CI.

- `make build`: produce `bin/killgrave` with branch and commit version metadata.
- `./bin/killgrave --imposters ./internal/server/http/test/testdata/imposters`: run locally with existing fixtures on localhost:3000.
- `go test ./...`: run the default unit test suite.
- `go test -v -vet=off -race ./...`: reproduce the CI unit test command with race detection.
- `make acceptance`: build the executable, then run acceptance tests with the `acceptance` build tag.
- `gofmt -w path/to/changed.go`: format changed Go files; use `go vet ./...` for static checks.

## Coding Style & Naming Conventions

Follow existing Go conventions: use `gofmt` formatting and tab indentation, short lowercase package names, exported `CamelCase` identifiers, and unexported `camelCase` identifiers. Keep related behavior in the existing packages. Group standard-library, external, and repository imports consistently with nearby code. Name imposter fixtures with `.imp.json`, `.imp.yml`, or `.imp.yaml` extensions.

## Testing Guidelines

Use Go's `testing` package with Testify assertions. Name tests `Test<Behavior>` and use descriptive table-driven subtests for input variations. Add regression coverage for behavior changes, including error cases. Each acceptance scenario is one `acceptance/tests/<scenario>.txtar` archive containing setup files, optional `scenario.json`, and named `http/<step>/req.http` and `res.http` entries. See `acceptance/README.md` for the format. Run unit and acceptance suites before submitting. No numeric coverage threshold is configured.

## Commit & Pull Request Guidelines

Recent commits use concise action-oriented subjects, such as `Fix imposters search` and `Replace spf13/afero with io/fs`; merged subjects often include a PR number. Follow that style.

Follow `CONTRIBUTING.md`: ask to take an issue, confirm its milestone, and base work on the milestone branch. PR descriptions should explain the change, link the issue, and list validation performed. For bugs, include reproduction steps, expected versus actual behavior, and relevant configuration or imposters.
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.21-alpine AS build
FROM golang:1.27-alpine AS build

LABEL MAINTAINER = 'Friends of Go (it@friendsofgo.tech)'

Expand All @@ -17,4 +17,4 @@ RUN go mod tidy && TAG=$(git describe --tags --abbrev=0) \
FROM scratch
COPY --from=build /go/bin/killgrave /go/bin/killgrave
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
ENTRYPOINT ["/go/bin/killgrave"]
ENTRYPOINT ["/go/bin/killgrave"]
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2019 friendsofgo
Copyright (c) 2026 friendsofgo

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
86 changes: 86 additions & 0 deletions acceptance/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Acceptance tests

Run `make acceptance` from the repository root. It builds Killgrave and runs each
`tests/<scenario>.txtar` against a separate process with an isolated working
directory and port. No external services are needed.

Keep each scenario in one archive containing its setup and named HTTP steps:

```text
-- imposters/routes.imp.json --
[{"request":{"method":"GET","endpoint":"/example"},"response":{"status":200,"headers":{"Content-Type":"text/plain"},"body":"example\n"}}]
-- http/matched/req.http --
GET /example HTTP/1.1

-- http/matched/res.http --
HTTP/1.1 200 OK
Content-Length: 8
Content-Type: text/plain


example
```

Each `http/<step>/` group is a named subtest containing `req.http` and `res.http`.
Steps run in archive order, so response sequences are visible together without
numbering separate files. Scenarios run concurrently. Expected responses include
all headers except `Date`; redirects are checked directly.

All entries outside `http/`, except `scenario.json`, are extracted as setup files.
By default Killgrave uses the extracted `imposters` directory and an allocated
port. An optional `scenario.json` entry can replace startup arguments:

```text
-- scenario.json --
{"args":["--config","settings/config.yml"]}
-- settings/config.yml --
host: localhost
port: ${PORT}
imposters_path: imposters
```

Arguments and setup files support `${PORT}` and `${UPSTREAM}` substitution.
Arguments run directly without a shell. Config-file imposter paths resolve
relative to the config file; `config_precedence.txtar` records precedence over
host, port, and imposter flags.

Optional `scenario.json` settings:

| Setting | Meaning |
| --- | --- |
| `args` | Replace the default startup arguments. |
| `error` | Require an unsuccessful exit and this diagnostic substring. No HTTP steps are needed. |
| `occupyPort` | Hold the allocated port open to exercise bind failure. |
| `upstreamCalls` | Start a local upstream and assert this exact request count, including zero. |
| `upstreamDown` | Close that upstream before startup to exercise a connection failure. |
| `https` | Send fixture requests over HTTPS. Startup arguments or configuration must also enable secure mode. |

The upstream returns status 202, `X-Upstream: yes`, and a body echoing the method,
request URI, `X-Test` header, and body. TCP readiness does not call the upstream
or advance response sequences.

Watcher steps can include `http/<step>/write/<relative-path>` entries to atomically
create or replace files before the request. An `http/<step>/eventually` entry
containing `true` retries the response check for up to five seconds. Writes happen
once. Use retries only for stateless GET requests without bodies; sequence tests
must check every response without retries. See `watcher_flag.txtar` for an example.

Delay steps may include `http/<step>/timing.json`, for example
`{"min":"180ms","max":"2s"}`. The harness measures the complete response, with a
lower bound to detect missing delays and a generous upper allowance for CI load.
Fixed and ranged delays are covered in both JSON and YAML. Timing checks cannot
use retries.

HTTPS clients accept Killgrave's bundled dummy certificate only within the test
transport. HTTPS proxy fixtures still connect to Killgrave before reaching the
local upstream; the unit suite separately checks a trusted local TLS upstream.

`TestGracefulShutdown` is a Go table test for SIGINT and SIGTERM. It uses channels
to hold an upstream request open, checks that new connections are refused during
shutdown, then releases the response and verifies clean exit. This coordination
stays in Go rather than adding concurrency commands to the archive format.

HTTP requests time out after three seconds; readiness and startup-error commands
have five-second limits. Cleanup allows three seconds for an interrupt before
killing and reaping the process, and verifies that the listening port can be bound
again. Failed scenarios include server logs.
Loading
Loading