diff --git a/CHANGELOG.md b/CHANGELOG.md index 21741bf5..57b7fbaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,8 @@ heading, and (b) bumping `project.yaml` `version:` in the same commit. [ADR 0002](docs/ADRs/0002-sidecar-language-go.md) — Sidecar language: Go. [ADR 0003](docs/ADRs/0003-pql-as-supporter-tool.md) — pql as supporter tool; wrap, don't duplicate; pql is a Clide subsystem when present. [ADR 0004](docs/ADRs/0004-ignore-file-strategy.md) — Ignore file strategy (`ignore_files:` in `.pql/config.yaml`, layered). +- Root `Makefile` drives both the Go sidecar and the Flutter app under one toolchain. Version is read from `project.yaml` via awk and stamped into the sidecar via `-ldflags -X`. Flutter targets gracefully noop before the app is scaffolded so the Makefile is usable from day one. Pinned Go tooling (govulncheck, goimports, golangci-lint) installs via `make tools`. +- `ci/` entry scripts: `test.sh`, `lint.sh` (includes the supply-chain gate — no green lint without a green CVE scan), `security.sh`, `release.sh` (stub). - Project identity files for the Flutter rebuild at the repo root: `project.yaml` (single source of truth for version + module path, version 2.0.0-dev), a fresh `README.md`, MIT `LICENSE`, and `.editorconfig`. The Python clide's manifest and README are preserved under `legacy/`. - [`docs/initial-plan.md`](docs/initial-plan.md) — the north-star design document for the Flutter rebuild. Captures what we kept from Python Clide (pane model, git skills, Claude-always-visible), what we took from Obsidian (canvas and graph — no vault, no bases, no plugin inheritance), what Claudian's short experiment contributed (Go sidecar, CLI-first, pql-as-subsystem, ignore-file strategy), and the tier roadmap (Tier 0 app+sidecar handshake → Tier 5 canvas+graph). - [`CLAUDE.md`](CLAUDE.md) orientation doc for future Claude Code instances: project identity, guardrails as one-liners, tier ordering, parent-project pointers, commands, dependencies & supply chain, open questions. Points at the design doc and ADRs rather than restating their content. diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..6a8721bc --- /dev/null +++ b/Makefile @@ -0,0 +1,167 @@ +# clide — local dev targets. CI scripts in ci/ shell out to these. +# +# Two components under one Makefile: +# - Go sidecar/CLI under sidecar/ +# - Flutter desktop app under app/ (scaffolded once Flutter is installed +# locally; targets gracefully noop when app/ isn't present yet). + +GO ?= go +BIN_DIR ?= sidecar/bin +INSTALL_DIR ?= $(HOME)/.local/bin + +# Version stamping. Source of truth: project.yaml `version:` field. Local +# builds augment with git short SHA + dirty marker (semver build metadata). +# Tagged releases are handled by goreleaser using the git tag instead. +VERSION_BASE ?= $(shell awk -F': *' '/^version:/ {gsub(/[" ]/,"",$$2); print $$2; exit}' project.yaml) +COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown) +DIRTY := $(shell git diff --quiet HEAD 2>/dev/null || echo .dirty) +VERSION ?= $(VERSION_BASE)+$(COMMIT)$(DIRTY) +DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ) + +LDFLAGS := -s -w \ + -X 'git.schweitz.net/jpmschweitzer/clide/sidecar/internal/version.Version=$(VERSION)' \ + -X 'git.schweitz.net/jpmschweitzer/clide/sidecar/internal/version.Commit=$(COMMIT)' \ + -X 'git.schweitz.net/jpmschweitzer/clide/sidecar/internal/version.Date=$(DATE)' + +GO_PACKAGES := ./... + +.PHONY: help +help: ## Show this help. + @awk 'BEGIN {FS = ":.*##"} /^[a-zA-Z0-9_-]+:.*##/ {printf " \033[36m%-22s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) + +# -- sidecar (Go) -------------------------------------------------------- + +.PHONY: build +build: ## Build the clide sidecar/CLI binary into sidecar/bin/clide. + cd sidecar && $(GO) build -ldflags="$(LDFLAGS)" -o bin/clide ./cmd/clide + +.PHONY: install +install: build ## Install sidecar/bin/clide into $(INSTALL_DIR). + install -m 0755 $(BIN_DIR)/clide $(INSTALL_DIR)/clide + +.PHONY: test +test: ## Unit tests (Go), fast. + cd sidecar && $(GO) test $(GO_PACKAGES) + +.PHONY: test-race +test-race: ## Unit tests with the race detector. + cd sidecar && $(GO) test -race $(GO_PACKAGES) + +.PHONY: test-integration +test-integration: build ## Integration tests (binary + fixture repos). Tag: integration. + cd sidecar && $(GO) test -tags=integration ./... + +.PHONY: lint +lint: ## golangci-lint run on the sidecar. + cd sidecar && golangci-lint run + +.PHONY: vuln +vuln: ## govulncheck on all sidecar packages — Go CVE gate. + cd sidecar && govulncheck ./... + +.PHONY: fmt +fmt: ## gofmt + goimports on the sidecar. + cd sidecar && gofmt -w . + @command -v goimports >/dev/null && cd sidecar && goimports -w . || echo "(goimports not installed; skipping)" + +.PHONY: tidy +tidy: ## go mod tidy for the sidecar. + cd sidecar && $(GO) mod tidy + +.PHONY: snapshot +snapshot: ## GoReleaser snapshot build (dry-run, no publish). + goreleaser release --snapshot --clean + +# -- app (Flutter) ------------------------------------------------------- + +# App targets gracefully noop if app/ doesn't exist yet (pre-Tier-0 +# scaffold) or if flutter isn't installed locally. +APP_PRESENT := $(shell test -f app/pubspec.yaml && echo yes || echo no) +HAS_FLUTTER := $(shell command -v flutter >/dev/null && echo yes || echo no) + +.PHONY: app-pubget +app-pubget: ## flutter pub get (hydrate the app's pub cache). +ifeq ($(APP_PRESENT),yes) + cd app && flutter pub get +else + @echo "(app/ not scaffolded yet; skipping)" +endif + +.PHONY: app-analyze +app-analyze: ## dart analyze on the app. +ifeq ($(APP_PRESENT),yes) + cd app && flutter analyze +else + @echo "(app/ not scaffolded yet; skipping)" +endif + +.PHONY: app-format +app-format: ## dart format on the app. +ifeq ($(APP_PRESENT),yes) + cd app && dart format --set-exit-if-changed . +else + @echo "(app/ not scaffolded yet; skipping)" +endif + +.PHONY: app-test +app-test: ## flutter test on the app. +ifeq ($(APP_PRESENT),yes) + cd app && flutter test +else + @echo "(app/ not scaffolded yet; skipping)" +endif + +.PHONY: app-build-linux +app-build-linux: ## flutter build linux. +ifeq ($(APP_PRESENT),yes) + cd app && flutter build linux +else + @echo "(app/ not scaffolded yet; skipping)" +endif + +.PHONY: app-build-macos +app-build-macos: ## flutter build macos. +ifeq ($(APP_PRESENT),yes) + cd app && flutter build macos +else + @echo "(app/ not scaffolded yet; skipping)" +endif + +# -- security ------------------------------------------------------------ + +.PHONY: security +security: vuln ## Run all CVE gates. For now: Go (govulncheck). Dart CVE gate lands when a reliable tooling exists. + +# -- tooling ------------------------------------------------------------- + +# Pinned Go tool versions. Bump deliberately; never floating. +GOVULNCHECK_VERSION ?= v1.1.4 +GOIMPORTS_VERSION ?= v0.29.0 +GOLANGCI_LINT_VERSION ?= v2.11.4 + +.PHONY: tools +tools: ## Install Go tooling at pinned versions (govulncheck, goimports, golangci-lint). + $(GO) install golang.org/x/vuln/cmd/govulncheck@$(GOVULNCHECK_VERSION) + $(GO) install golang.org/x/tools/cmd/goimports@$(GOIMPORTS_VERSION) + $(GO) install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION) + +# -- pre-push gate ------------------------------------------------------- + +.PHONY: push-check +push-check: lint test test-race test-integration vuln app-analyze app-test ## Full pre-push gate — everything that must pass before a push. + +.PHONY: hooks +hooks: ## Install the repo's git hooks (points core.hooksPath at .githooks/). + git config core.hooksPath .githooks + @echo "hooks installed (core.hooksPath = .githooks)" + +# -- housekeeping -------------------------------------------------------- + +.PHONY: clean +clean: ## Remove build artefacts. + rm -rf sidecar/bin sidecar/dist +ifeq ($(APP_PRESENT),yes) + rm -rf app/build app/.dart_tool +endif + +.DEFAULT_GOAL := help diff --git a/ci/lint.sh b/ci/lint.sh new file mode 100755 index 00000000..2082ad70 --- /dev/null +++ b/ci/lint.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +# CI entry: lint + supply-chain gates. Security runs here too so a +# merge-blocking lint failure and a merge-blocking CVE failure share +# one CI job — there is no version of "lint passed but we shipped a +# known-vulnerable dep" that is acceptable in this repo. +set -euo pipefail + +cd "$(dirname "$0")/.." + +make lint +make app-analyze +make security diff --git a/ci/release.sh b/ci/release.sh new file mode 100755 index 00000000..83bd4b37 --- /dev/null +++ b/ci/release.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# CI entry: release pipeline. Stub — wire goreleaser + flutter build +# artifacts later. +set -euo pipefail + +cd "$(dirname "$0")/.." + +echo "TODO: goreleaser release (sidecar) + flutter build (app) + publish" +exit 64 diff --git a/ci/security.sh b/ci/security.sh new file mode 100755 index 00000000..6775d344 --- /dev/null +++ b/ci/security.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +# CI entry: supply-chain + CVE gate. Shells out to Makefile targets so +# local dev and CI run the same commands. +# +# See ~/.claude/projects/-var-mnt-data-projects-clide/memory/ for the +# standing requirements: Go deps must be version-locked and CVE-checked; +# Dart deps prefer zero, with what remains pinned and audited. +set -euo pipefail + +cd "$(dirname "$0")/.." + +make security diff --git a/ci/test.sh b/ci/test.sh new file mode 100755 index 00000000..385850ea --- /dev/null +++ b/ci/test.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# CI entry: run the full test matrix. Shells out to Makefile targets. +set -euo pipefail + +cd "$(dirname "$0")/.." + +make test +make test-race +make app-test