diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..2a1e060 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,42 @@ +name: Release + +on: + push: + tags: + - "v*" + +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: "1.20" + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v6 + with: + distribution: goreleaser + version: "~> v2" + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..cbfec50 --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,53 @@ +version: 2 + +before: + hooks: + - go mod tidy + # Build frontend before Go build + - sh -c "cd web && pnpm install && pnpm run build" + +builds: + - id: devrouter + main: ./cmd/devrouter + binary: devrouter + env: + - CGO_ENABLED=0 + goos: + - linux + - darwin + goarch: + - amd64 + - arm64 + ldflags: + - -s -w + - -X main.version={{ .Version }} + - -X main.commit={{ .ShortCommit }} + - -X main.buildDate={{ .Date }} + +archives: + - id: default + format: tar.gz + name_template: "{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}" + files: + - LICENSE + - README.md + +checksum: + name_template: "checksums.txt" + +changelog: + sort: asc + filters: + exclude: + - "^docs:" + - "^test:" + - "^ci:" + - "^chore:" + +release: + github: + owner: simota + name: devrouter + draft: false + prerelease: auto + name_template: "v{{ .Version }}" diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b3b99fb --- /dev/null +++ b/Makefile @@ -0,0 +1,66 @@ +.PHONY: all build build-frontend build-go clean dev install test lint help + +# Version info +VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev") +COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown") +DATE ?= $(shell date -u +"%Y-%m-%dT%H:%M:%SZ") + +# Build flags +LDFLAGS := -s -w \ + -X main.version=$(VERSION) \ + -X main.commit=$(COMMIT) \ + -X main.buildDate=$(DATE) + +# Default target +all: build + +# Build everything (frontend + Go binary) +build: build-frontend build-go + +# Build frontend assets +build-frontend: + @echo "Building frontend..." + cd web && pnpm install && pnpm run build + +# Build Go binary (requires frontend to be built first) +build-go: + @echo "Building Go binary..." + go build -ldflags "$(LDFLAGS)" -o devrouter ./cmd/devrouter + +# Install binary to $GOPATH/bin +install: build + @echo "Installing devrouter..." + go install -ldflags "$(LDFLAGS)" ./cmd/devrouter + +# Clean build artifacts +clean: + @echo "Cleaning..." + rm -f devrouter + rm -rf internal/devrouter/web/dist/assets + rm -f internal/devrouter/web/dist/index.html + +# Run frontend dev server +dev: + cd web && pnpm run dev + +# Run tests +test: + go test ./... + cd web && pnpm run test + +# Run linters +lint: + go vet ./... + cd web && pnpm run check + +# Help +help: + @echo "Available targets:" + @echo " make build - Build frontend and Go binary" + @echo " make build-frontend - Build frontend only" + @echo " make build-go - Build Go binary only (requires frontend)" + @echo " make install - Build and install to GOPATH/bin" + @echo " make clean - Remove build artifacts" + @echo " make dev - Run frontend dev server" + @echo " make test - Run all tests" + @echo " make lint - Run linters" diff --git a/README.md b/README.md index de2c410..fb963b9 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ A local development router that eliminates port conflicts without modifying your ## Quick Start ```bash -# Install -go install github.com/simota/devrouter/cmd/devrouter@latest +# Install (download binary from GitHub Releases or build from source) +# See Installation section below # Start the global Traefik proxy devrouter daemon up @@ -39,25 +39,51 @@ devrouter down /path/to/your/monorepo - macOS / Linux - Docker (Docker Desktop or Colima) - `docker` CLI with `docker compose` -- Go 1.20+ (for building from source) + +### Build Requirements (for building from source) + +- Go 1.20+ +- Node.js 18+ +- pnpm (`npm install -g pnpm`) ## Installation -### From Source +### Download Binary (Recommended) + +Download the latest binary from [GitHub Releases](https://github.com/simota/devrouter/releases): ```bash -go install github.com/simota/devrouter/cmd/devrouter@latest +# macOS (Apple Silicon) +curl -L https://github.com/simota/devrouter/releases/latest/download/devrouter_darwin_arm64.tar.gz | tar xz +sudo mv devrouter /usr/local/bin/ + +# macOS (Intel) +curl -L https://github.com/simota/devrouter/releases/latest/download/devrouter_darwin_amd64.tar.gz | tar xz +sudo mv devrouter /usr/local/bin/ + +# Linux (amd64) +curl -L https://github.com/simota/devrouter/releases/latest/download/devrouter_linux_amd64.tar.gz | tar xz +sudo mv devrouter /usr/local/bin/ ``` -### Build Locally +### Build from Source + +> **Note**: `go install github.com/simota/devrouter/cmd/devrouter@latest` does not work because the Web UI frontend assets need to be built first and embedded into the binary. ```bash git clone https://github.com/simota/devrouter.git cd devrouter -go build -o devrouter ./cmd/devrouter +make build +sudo mv devrouter /usr/local/bin/ +``` + +Or install directly to `$GOPATH/bin`: + +```bash +make install ``` -The binary is placed in `$GOBIN` (or `$GOPATH/bin` if `GOBIN` is not set). Ensure it's in your `PATH`. +Ensure `/usr/local/bin` or `$GOPATH/bin` is in your `PATH`. ## CLI Commands diff --git a/cmd/devrouter/main.go b/cmd/devrouter/main.go index 59f93c3..a80bfab 100644 --- a/cmd/devrouter/main.go +++ b/cmd/devrouter/main.go @@ -7,6 +7,7 @@ import ( "os" "os/signal" "path/filepath" + "runtime" "strings" "syscall" "time" @@ -14,7 +15,12 @@ import ( "devrouter/internal/devrouter" ) -const version = "0.1.0" +// Build-time variables (injected via ldflags) +var ( + version = "dev" + commit = "unknown" + buildDate = "unknown" +) func main() { os.Exit(run()) @@ -31,7 +37,10 @@ func run() int { usage() return 0 case "-v", "--version", "version": - fmt.Println(version) + fmt.Printf("devrouter %s\n", version) + fmt.Printf(" commit: %s\n", commit) + fmt.Printf(" built: %s\n", buildDate) + fmt.Printf(" go: %s\n", runtime.Version()) return 0 case "up": return runUp(os.Args[2:])