From 634bff44115e9752bd628ccc5a091beb2a4d8f65 Mon Sep 17 00:00:00 2001 From: trev Date: Wed, 14 May 2025 04:15:58 -0400 Subject: [PATCH] feat: readme --- .github/workflows/{lint.yaml => check.yaml} | 9 ++- .scripts/bump.sh | 15 +++- .vscode/settings.json | 1 + README.md | 83 +++++++++++++++++++++ flake.nix | 2 +- server/bobgen.yaml | 16 ---- server/cmd/trevstack/main.go | 14 ++-- 7 files changed, 110 insertions(+), 30 deletions(-) rename .github/workflows/{lint.yaml => check.yaml} (68%) create mode 100644 README.md delete mode 100644 server/bobgen.yaml diff --git a/.github/workflows/lint.yaml b/.github/workflows/check.yaml similarity index 68% rename from .github/workflows/lint.yaml rename to .github/workflows/check.yaml index 881ecd1..bb52158 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/check.yaml @@ -1,14 +1,19 @@ -name: Lint +name: Check on: push: branches: - main pull_request: + types: [opened, reopened, edited] jobs: - lint: + check: + name: check runs-on: ubuntu-latest + if: | + contains(github.event.head_commit.message, 'bump:') == false && + contains(github.event.head_commit.message, 'Merge pull request') == false steps: - name: Checkout uses: actions/checkout@v4 diff --git a/.scripts/bump.sh b/.scripts/bump.sh index 72ce560..a6c1e33 100755 --- a/.scripts/bump.sh +++ b/.scripts/bump.sh @@ -1,9 +1,20 @@ #!/usr/bin/env bash -git_root=$(git rev-parse --show-toplevel) +#git_root=$(git rev-parse --show-toplevel) git_version=$(git describe --tags --abbrev=0) version=${git_version#v} -next_version=$(echo "${version}" | awk -F. -v OFS=. '{$NF += 1 ; print}') + +major=$(echo "${version}" | cut -d . -f1) +minor=$(echo "${version}" | cut -d . -f2) +patch=$(echo "${version}" | cut -d . -f3) +case "${1}" in + major) major=$((major + 1)) ;; + minor) minor=$((minor + 1)) ;; + *) patch=$((patch + 1)) ;; +esac +next_version="${major}.${minor}.${patch}" + +echo "${version} -> ${next_version}" echo "bumping openapi" cd "${git_root}" diff --git a/.vscode/settings.json b/.vscode/settings.json index cd916c3..4075986 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,6 +4,7 @@ // Go "go.lintTool": "revive", "go.formatTool": "goimports", + "go.buildTags": "dev", "go.lintFlags": ["--config=server/revive.toml"], "gopls": { "ui.semanticTokens": true }, "[go]": { diff --git a/README.md b/README.md new file mode 100644 index 0000000..8915fa7 --- /dev/null +++ b/README.md @@ -0,0 +1,83 @@ +## TrevStack + +This is a CRUD app to use as a template for starting projects + +### Features + +- **Communicate anywhere**. Define a [protocol buffer](https://protobuf.dev/), and [Connect](https://connectrpc.com/) generates type-safe code to facilitate communication between the server and any client (web, mobile, embedded, etc). The protocol buffers can contain annotations to validate fields on the client and server. For clients that cannot use Connect, an OpenAPI spec is also generated +- **Build anywhere**. The dev environment, testing and building is all declared in a single [Nix](https://nixos.org/) flake. Every developer and server can use the same environment +- **Deploy anywhere**. CI/CD is already set up using github actions. New versions are automatically released for every major platform, along with a docker image. The binaries created require zero run-time dependencies and are relatively small (this app is 26 MiB) +- Authentication is rolled in, including API key, fingerprint & passkey +- Automatic database migration on startup +- Light & dark modes with the [catppuccin](https://catppuccin.com/palette/) color palette +- Really good at running as a [progressive web app](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps) + +While I personally prefer using a Svelte frontend and Go backend, feel free to swap them out with whatever you like, you'll be surprised how easy it is. + +## Getting Started + +1. [Install Nix](https://nixos.org/download/) +2. Run `nix develop` +3. Create a `server/.env` file that looks something like + +```env +KEY=changeme +PORT=8080 +URL=http://localhost:5173 +DATABASE_URL=sqlite:/home/trev/.config/trevstack/sqlite.db +``` + +4. Run `treli` + +It's that simple. If you're feeling fancy, install [direnv](https://direnv.net/) and the dev environment will load automatically. + +### Useful Commands + +- `nix run #update`: updates all of the dependencies + +- `nix run #bump [major | minor]`: bumps the current version up one. Defaults to "patch" (0.0.1 -> 0.0.2) + +- `buf lint` & `buf generate`: Lints and generates code from protocol buffers + +- `sqlc vet` & `sqlc generate`: Verifies and generates code from SQL files + +- `dbmate new` & `dbmate up`: Creates a new migration file and runs pending migrations + +## Components + +### Client + +- **svelte 5** [[docs](https://svelte.dev/docs/svelte)] UI framework +- **tailwind 4** [[docs](https://tailwindcss.com/)] CSS framework +- **bits ui** [[docs](https://bits-ui.com/docs/)] headless components +- [components](https://github.com/spotdemo4/trevstack/tree/main/client/src/lib/ui) from **shadcn-svelte** [[docs](https://www.shadcn-svelte.com/docs)] altered to work with tailwind 4, fit the [catppuccin](https://catppuccin.com/palette/) color palette, and use [shallow routing](https://svelte.dev/docs/kit/shallow-routing) +- **connect rpc** [[docs](https://connectrpc.com/docs/web/)] to communicating with the server +- **protovalidate-es** [[docs](https://github.com/bufbuild/protovalidate-es)], along with a function [coolforms](https://github.com/spotdemo4/trevstack/blob/main/client/src/lib/coolforms/) to emulate the library [sveltekit-superforms](https://superforms.rocks/) +- **simplewebauthn** [[docs](https://simplewebauthn.dev/docs/packages/browser)] for passkey authentication +- **scalar** [[docs](https://github.com/scalar/scalar)] for displaying openapi specs +- **tw-animate-css** [[docs](https://github.com/Wombosvideo/tw-animate-css)] to animate with just tailwind classes +- vite [[docs](https://vite.dev/)] for dev server and bundling +- eslint [[docs](https://eslint.org/)] for linting +- prettier [[docs](https://prettier.io/)] for formatting + - prettier-plugin-svelte [[docs](https://github.com/sveltejs/prettier-plugin-svelte)] + - prettier-plugin-tailwindcss [[docs](https://github.com/tailwindlabs/prettier-plugin-tailwindcss)] + - prettier-plugin-sort-imports [[docs](https://github.com/IanVS/prettier-plugin-sort-imports)] + +### Server + +- **go** [[docs](https://go.dev/doc/)] +- **connect rpc** [[docs](https://connectrpc.com/docs/go/)] to serve gRPC & HTTP requests +- **protovalidate-go** [[docs](https://github.com/bufbuild/protovalidate-go)] for validating those requests +- **sqlc** [[docs](https://docs.sqlc.dev/en/latest/)] because writing the SQL yourself is better than an ORM +- **go-webauthn** [[docs](https://github.com/go-webauthn/webauthn)] because webauthn is hard +- **dbmate** [[docs](https://github.com/amacneil/dbmate)] for database migrations +- revive [[docs](https://github.com/mgechev/revive)] for linting + +### Protocol Buffers / gRPC + +- **buf** [[docs](https://buf.build/docs/)] CLI for linting & code generation +- **protovalidate** [[docs](https://buf.build/docs/protovalidate/)] provides annotations to validate proto messages & fields +- **protoc-gen-connect-openapi** [[docs](https://github.com/sudorandom/protoc-gen-connect-openapi)] generates openapi specs +- protoc-gen-go [[docs](https://pkg.go.dev/google.golang.org/protobuf)] +- protoc-gen-connect-go [[docs](https://connectrpc.com/docs/go)] +- protoc-gen-es [[docs](https://connectrpc.com/docs/web/)] diff --git a/flake.nix b/flake.nix index 5c0987e..aeba27e 100644 --- a/flake.nix +++ b/flake.nix @@ -81,9 +81,9 @@ in { default = pkgs.mkShell { packages = with pkgs; [ + treli.packages."${system}".default git nix-update - treli.packages."${system}".default # Server go diff --git a/server/bobgen.yaml b/server/bobgen.yaml deleted file mode 100644 index 57b7eb5..0000000 --- a/server/bobgen.yaml +++ /dev/null @@ -1,16 +0,0 @@ -wipe: true - -replacements: - - match: - db_type: "INTEGER" - replace: "int64" - - - match: - db_type: "INTEGER" - nullable: true - replace: "int64" - -sql: - dialect: sqlite - dir: db - output: internal/models \ No newline at end of file diff --git a/server/cmd/trevstack/main.go b/server/cmd/trevstack/main.go index 7d32de9..5f3dbd9 100644 --- a/server/cmd/trevstack/main.go +++ b/server/cmd/trevstack/main.go @@ -29,6 +29,8 @@ import ( ) func main() { + name := "TrevStack" + // Get env env, err := getEnv() if err != nil { @@ -49,7 +51,7 @@ func main() { // Create webauthn webAuthn, err := webauthn.New(&webauthn.Config{ - RPDisplayName: env.Name, + RPDisplayName: name, RPID: env.URL.Hostname(), RPOrigins: []string{env.URL.String()}, }) @@ -63,9 +65,9 @@ func main() { log.Fatalf("failed to create validator: %s", err.Error()) } - // Serve GRPC Handlers + // Serve gRPC Handlers api := http.NewServeMux() - api.Handle(interceptors.WithCORS(user.NewAuthHandler(vi, sqlc, webAuthn, env.Name, env.Key))) + api.Handle(interceptors.WithCORS(user.NewAuthHandler(vi, sqlc, webAuthn, name, env.Key))) api.Handle(interceptors.WithCORS(user.NewHandler(vi, sqlc, webAuthn, env.Key))) api.Handle(interceptors.WithCORS(item.NewHandler(vi, sqlc, env.Key))) @@ -108,7 +110,6 @@ func main() { type env struct { Port string Key string - Name string URL *url.URL DatabaseURL string } @@ -123,7 +124,6 @@ func getEnv() (*env, error) { env := env{ Port: os.Getenv("PORT"), Key: os.Getenv("KEY"), - Name: os.Getenv("NAME"), DatabaseURL: os.Getenv("DATABASE_URL"), } @@ -135,10 +135,6 @@ func getEnv() (*env, error) { if env.Key == "" { return nil, errors.New("env 'KEY' not found") } - if env.Name == "" { - env.Name = "trevstack" - log.Printf("env 'NAME' not found, defaulting to %s\n", env.Name) - } if env.DatabaseURL == "" { return nil, errors.New("env 'DATABASE_URL' not found") }