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
50 changes: 43 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ permissions:
contents: read

jobs:
test:
name: Build, vet, and test
lint:
name: Format, vet, and race tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -22,22 +22,58 @@ jobs:

- name: Check formatting
run: |
unformatted=$(gofmt -l ./cmd ./internal)
unformatted=$(gofmt -l ./cmd ./internal ./ui)
if [ -n "$unformatted" ]; then
echo "The following files are not gofmt-formatted:"
echo "$unformatted"
exit 1
fi

- name: Build
run: go build ./...

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

- name: Test
- name: Test (race)
run: go test -race ./...

test:
name: Build and test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true

- name: Build
run: go build ./...

- name: Test
run: go test ./...

cross-compile:
name: Cross-compile all targets
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true

- name: Build every OS/arch
run: |
for target in linux/amd64 linux/arm64 darwin/amd64 darwin/arm64 windows/amd64 windows/arm64; do
echo "building $target"
GOOS=${target%/*} GOARCH=${target#*/} CGO_ENABLED=0 go build -o /dev/null ./cmd/gateway
done

ui:
name: Build dashboard
runs-on: ubuntu-latest
Expand Down
65 changes: 65 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: Release

on:
push:
tags: ["v*"]

permissions:
contents: write
packages: write

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

- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true

- uses: goreleaser/goreleaser-action@v6
with:
version: "~> v2"
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

docker:
name: Multi-arch Docker image
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: docker/setup-qemu-action@v3

- uses: docker/setup-buildx-action@v3

- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- uses: docker/metadata-action@v5
id: meta
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest

- uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
VERSION=${{ github.ref_name }}
55 changes: 55 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
version: 2

project_name: gtc

before:
hooks:
- go mod tidy

builds:
- id: gtc
main: ./cmd/gateway
binary: gtc
env:
- CGO_ENABLED=0
goos:
- linux
- darwin
- windows
goarch:
- amd64
- arm64
ldflags:
- -s -w -X main.version={{.Version}}

archives:
- id: gtc
formats: ["tar.gz"]
format_overrides:
- goos: windows
formats: ["zip"]
name_template: >-
{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}
files:
- README.md
- LICENSE
- config/sinks.example.yaml

checksum:
name_template: checksums.txt

changelog:
use: github
filters:
exclude:
- "^docs"
- "^chore"
- "^test"

release:
footer: |
## Docker

```
docker pull ghcr.io/emoss08/gtc:{{ .Version }}
```
6 changes: 6 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ cd ui && npm ci && npm run build
cd ui && npm run dev
```

The codebase is pure Go (CGO_ENABLED=0) and must keep building for
linux/darwin/windows on amd64/arm64 — CI cross-compiles all six targets and
runs the test suite on Linux, Windows, and macOS. Releases are cut by
pushing a `v*` tag (goreleaser builds the binaries; a multi-arch Docker
image goes to ghcr.io).

## Delivery Semantics

GTC provides **at-least-once** delivery. The WAL position is only confirmed to
Expand Down
10 changes: 8 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
FROM golang:1.25.3-alpine AS builder
# Multi-arch build: docker buildx sets BUILDPLATFORM/TARGETOS/TARGETARCH so
# the (fast, native) builder cross-compiles for the requested target.
FROM --platform=$BUILDPLATFORM golang:1.25-alpine AS builder

WORKDIR /app

Expand All @@ -9,7 +11,11 @@ RUN go mod download

COPY . .

RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o /gateway ./cmd/gateway
ARG TARGETOS
ARG TARGETARCH
ARG VERSION=dev
RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH:-amd64} \
go build -ldflags="-s -w -X main.version=${VERSION}" -o /gateway ./cmd/gateway

FROM alpine:3.21

Expand Down
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ docker compose -f docker-compose-local.yml exec redis \

### Binary

GTC is a single, pure-Go binary and runs natively on **Linux, macOS, and
Windows** (amd64 and arm64). Grab a prebuilt binary from the
[releases page](https://github.com/emoss08/gtc/releases), pull the multi-arch
Docker image (`ghcr.io/emoss08/gtc`), or build from source:

```bash
go build -o gateway ./cmd/gateway

Expand All @@ -102,9 +107,19 @@ REDIS_URL="redis://localhost:6379" \
./gateway
```

On Windows (PowerShell):

```powershell
go build -o gateway.exe ./cmd/gateway

$env:DATABASE_URL = "postgres://postgres:postgres@localhost:5432/mydb?replication=database"
$env:REDIS_URL = "redis://localhost:6379"
.\gateway.exe
```

That's the whole setup: with no further configuration GTC mirrors every table
in the publication to Redis. Add `REDIS_JSON_URL` and/or `MEILISEARCH_URL` to
enable the other sinks.
enable the other sinks. `gateway --version` prints the build version.

### PostgreSQL requirements

Expand Down
16 changes: 15 additions & 1 deletion cmd/gateway/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"fmt"
"log/slog"
"os"
"strings"
Expand All @@ -9,11 +10,24 @@ import (
"go.uber.org/fx"
)

// Overridden at release time via -ldflags "-X main.version=...".
var version = "dev"

func main() {
for _, arg := range os.Args[1:] {
if arg == "--version" || arg == "-v" {
fmt.Printf("gtc %s\n", version)
return
}
}

_ = godotenv.Load()

logger := newLogger()
logger.Info("starting GTC", slog.String("version", version))

fx.New(
fx.Provide(newLogger),
fx.Provide(func() *slog.Logger { return logger }),
Module(),
).Run()
}
Expand Down
Loading