Skip to content

Instantly share code, notes, and snippets.

@saifsmailbox98
Last active April 14, 2026 15:21
Show Gist options
  • Select an option

  • Save saifsmailbox98/12f77b806e0823122fc6997b827e544b to your computer and use it in GitHub Desktop.

Select an option

Save saifsmailbox98/12f77b806e0823122fc6997b827e544b to your computer and use it in GitHub Desktop.

Enhanced Dev Environment Setup

This guide sets up an improved local development environment with:

  • just as a task runner (replacement for make)
  • portless for .test domain names instead of localhost:port
  • Git worktrees for working on multiple tasks in parallel, each with its own isolated Docker stack
  • mitmproxy for inspecting HTTP requests between frontend and backend
  • Mailpit as a standalone SMTP server shared across all stacks
  • tsgo (native TypeScript compiler) for 10x faster type checking

Prerequisites

brew install just
npm install -g portless

Just shell completions (zsh)

mkdir -p ~/.zsh/completions
just --completions zsh > ~/.zsh/completions/_just

Add to ~/.zshrc:

fpath=(~/.zsh/completions $fpath)
autoload -U compinit
compinit

Start portless proxy (one-time, persists in background)

portless proxy start --tld test

Docker Desktop settings (recommended)

  • Disk image size: 120 GB (Settings → Resources)
  • Memory: 16 GB (Settings → Resources)

Files to Create

All files are local to your clone — nothing gets committed. They are ignored via .git/info/exclude.

1. .git/info/exclude

Append these lines to your existing .git/info/exclude:

# Personal dev setup
docker-compose.dev.override.yml
docker-compose.worktree.yml
docker-compose.worktree.debug.yml
justfile
README.dev.md
nginx/default.dev.debug.conf

2. justfile (project root)

# Default project name is the current directory name
default_name := `basename "$PWD"`

# --- Main dev environment (existing workflow) ---

up-dev:
    #!/usr/bin/env bash
    set -euo pipefail
    # Update .env with the correct site URL
    grep -q '^SITE_URL=' .env && sed -i '' 's|^SITE_URL=.*|SITE_URL=https://infisical.test|' .env || echo 'SITE_URL=https://infisical.test' >> .env
    grep -q '^VITE_ALLOWED_HOSTS=' .env && sed -i '' 's|^VITE_ALLOWED_HOSTS=.*|VITE_ALLOWED_HOSTS=infisical.test|' .env || echo 'VITE_ALLOWED_HOSTS=infisical.test' >> .env
    docker compose -f docker-compose.dev.yml -f docker-compose.dev.override.yml up --build -d
    portless alias infisical 8080 --tld test --force 2>/dev/null || true
    portless alias db.infisical 5432 --tld test --force 2>/dev/null || true
    portless alias redis.infisical 6379 --tld test --force 2>/dev/null || true
    @echo ""
    @echo "App:      https://infisical.test"
    @echo "Postgres: db.infisical.test (port 5432)"
    @echo "Redis:    redis.infisical.test (port 6379)"
    @echo "psql:     psql -h db.infisical.test -U infisical -d infisical"

up-dev-ldap:
    docker compose -f docker-compose.dev.yml -f docker-compose.dev.override.yml --profile ldap up --build -d
    portless alias infisical 8080 --tld test --force 2>/dev/null || true
    portless alias db.infisical 5432 --tld test --force 2>/dev/null || true
    portless alias redis.infisical 6379 --tld test --force 2>/dev/null || true
    @echo ""
    @echo "App:      https://infisical.test"
    @echo "Postgres: db.infisical.test (port 5432)"
    @echo "Redis:    redis.infisical.test (port 6379)"

up-dev-metrics:
    docker compose -f docker-compose.dev.yml -f docker-compose.dev.override.yml --profile metrics up --build -d
    portless alias infisical 8080 --tld test --force 2>/dev/null || true
    portless alias db.infisical 5432 --tld test --force 2>/dev/null || true
    portless alias redis.infisical 6379 --tld test --force 2>/dev/null || true
    @echo ""
    @echo "App:      https://infisical.test"
    @echo "Postgres: db.infisical.test (port 5432)"
    @echo "Redis:    redis.infisical.test (port 6379)"

up-dev-sso:
    docker compose -f docker-compose.dev.yml -f docker-compose.dev.override.yml --profile sso up --build -d
    portless alias infisical 8080 --tld test --force 2>/dev/null || true
    portless alias db.infisical 5432 --tld test --force 2>/dev/null || true
    portless alias redis.infisical 6379 --tld test --force 2>/dev/null || true
    @echo ""
    @echo "App:      https://infisical.test"
    @echo "Postgres: db.infisical.test (port 5432)"
    @echo "Redis:    redis.infisical.test (port 6379)"

up-prod:
    docker compose -f docker-compose.prod.yml up --build

down-dev:
    docker compose -f docker-compose.dev.yml down

reviewable-ui:
    cd frontend && npm run lint:fix && npm run type:check

reviewable-api:
    cd backend && npm run lint:fix && npm run type:check

reviewable: reviewable-ui reviewable-api

# --- Worktree stacks ---

# Start a full isolated stack for the current worktree
# Usage: just up [name] [--no-fips]
up name=default_name *flags="":
    #!/usr/bin/env bash
    set -euo pipefail

    NAME="{{name}}"
    PG_SOURCE="infisical_postgres-data1"

    # Check for --no-fips flag
    for flag in {{flags}}; do
        if [ "$flag" = "--no-fips" ]; then
            PG_SOURCE="infisical_postgres-data"
        fi
    done

    PG_VOLUME="${NAME}_postgres-data"

    # Clone the postgres volume if this task doesn't have one yet
    if ! docker volume inspect "$PG_VOLUME" > /dev/null 2>&1; then
        echo "Cloning $PG_SOURCE → $PG_VOLUME..."
        docker volume create "$PG_VOLUME" > /dev/null
        docker run --rm \
            -v "$PG_SOURCE":/from:ro \
            -v "$PG_VOLUME":/to \
            alpine sh -c "cp -a /from/. /to/"
        echo "Done."
    else
        echo "Volume $PG_VOLUME already exists, reusing."
    fi

    # Update .env with the correct site URL
    if [ -f .env ]; then
        grep -q '^SITE_URL=' .env && sed -i '' "s|^SITE_URL=.*|SITE_URL=https://$NAME.test|" .env || echo "SITE_URL=https://$NAME.test" >> .env
        grep -q '^VITE_ALLOWED_HOSTS=' .env && sed -i '' "s|^VITE_ALLOWED_HOSTS=.*|VITE_ALLOWED_HOSTS=$NAME.test|" .env || echo "VITE_ALLOWED_HOSTS=$NAME.test" >> .env
    fi

    # Start the stack
    PG_VOLUME="$PG_VOLUME" docker compose \
        -p "$NAME" \
        -f docker-compose.worktree.yml \
        up --build -d

    # Wait a moment for containers to get ports assigned
    sleep 2

    # Get the nginx container's host port
    NGINX_CONTAINER="${NAME}-nginx-1"
    NGINX_PORT=$(docker port "$NGINX_CONTAINER" 80 2>/dev/null | head -1 | cut -d: -f2)

    # Get the postgres container's host port
    DB_CONTAINER="${NAME}-db-1"
    DB_PORT=$(docker port "$DB_CONTAINER" 5432 2>/dev/null | head -1 | cut -d: -f2)

    # Get the redis container's host port
    REDIS_CONTAINER="${NAME}-redis-1"
    REDIS_PORT=$(docker port "$REDIS_CONTAINER" 6379 2>/dev/null | head -1 | cut -d: -f2)

    # Register with portless
    portless alias "$NAME" "$NGINX_PORT" --tld test --force 2>/dev/null || true
    portless alias "db.$NAME" "$DB_PORT" --tld test --force 2>/dev/null || true
    portless alias "redis.$NAME" "$REDIS_PORT" --tld test --force 2>/dev/null || true

    echo ""
    echo "Stack '$NAME' is running:"
    echo "  App:      https://$NAME.test"
    echo "  Postgres: db.$NAME.test (port $DB_PORT)"
    echo "  Redis:    redis.$NAME.test (port $REDIS_PORT)"
    echo "  psql:     psql -h db.$NAME.test -U infisical -d infisical"

# Stop a worktree stack (keeps volumes)
down name=default_name:
    #!/usr/bin/env bash
    set -euo pipefail

    NAME="{{name}}"

    docker compose \
        -p "$NAME" \
        -f docker-compose.worktree.yml \
        -f docker-compose.worktree.debug.yml \
        down 2>/dev/null || \
    docker compose \
        -p "$NAME" \
        -f docker-compose.worktree.yml \
        down

    # Remove portless aliases
    portless alias --remove "$NAME" 2>/dev/null || true
    portless alias --remove "debug.$NAME" 2>/dev/null || true
    portless alias --remove "db.$NAME" 2>/dev/null || true
    portless alias --remove "redis.$NAME" 2>/dev/null || true

    echo "Stack '$NAME' stopped. Volumes preserved."

# Remove a worktree stack's containers AND volumes
rm name=default_name:
    #!/usr/bin/env bash
    set -euo pipefail

    NAME="{{name}}"

    docker compose \
        -p "$NAME" \
        -f docker-compose.worktree.yml \
        -f docker-compose.worktree.debug.yml \
        down -v 2>/dev/null || \
    docker compose \
        -p "$NAME" \
        -f docker-compose.worktree.yml \
        down -v

    # Remove the cloned postgres volume
    docker volume rm "${NAME}_postgres-data" 2>/dev/null || true

    # Remove portless aliases
    portless alias --remove "$NAME" 2>/dev/null || true
    portless alias --remove "debug.$NAME" 2>/dev/null || true
    portless alias --remove "db.$NAME" 2>/dev/null || true
    portless alias --remove "redis.$NAME" 2>/dev/null || true

    echo "Stack '$NAME' removed (containers + volumes)."

# Start a worktree stack with mitmproxy (request inspector)
up-debug name=default_name *flags="":
    #!/usr/bin/env bash
    set -euo pipefail

    NAME="{{name}}"
    PG_SOURCE="infisical_postgres-data1"

    # Check for --no-fips flag
    for flag in {{flags}}; do
        if [ "$flag" = "--no-fips" ]; then
            PG_SOURCE="infisical_postgres-data"
        fi
    done

    PG_VOLUME="${NAME}_postgres-data"

    # Clone the postgres volume if this task doesn't have one yet
    if ! docker volume inspect "$PG_VOLUME" > /dev/null 2>&1; then
        echo "Cloning $PG_SOURCE → $PG_VOLUME..."
        docker volume create "$PG_VOLUME" > /dev/null
        docker run --rm \
            -v "$PG_SOURCE":/from:ro \
            -v "$PG_VOLUME":/to \
            alpine sh -c "cp -a /from/. /to/"
        echo "Done."
    else
        echo "Volume $PG_VOLUME already exists, reusing."
    fi

    # Update .env with the correct site URL
    if [ -f .env ]; then
        grep -q '^SITE_URL=' .env && sed -i '' "s|^SITE_URL=.*|SITE_URL=https://$NAME.test|" .env || echo "SITE_URL=https://$NAME.test" >> .env
        grep -q '^VITE_ALLOWED_HOSTS=' .env && sed -i '' "s|^VITE_ALLOWED_HOSTS=.*|VITE_ALLOWED_HOSTS=$NAME.test|" .env || echo "VITE_ALLOWED_HOSTS=$NAME.test" >> .env
    fi

    # Start the stack with mitmproxy
    PG_VOLUME="$PG_VOLUME" docker compose \
        -p "$NAME" \
        -f docker-compose.worktree.yml \
        -f docker-compose.worktree.debug.yml \
        up --build -d

    # Wait a moment for containers to get ports assigned
    sleep 2

    # Get the nginx container's host port
    NGINX_CONTAINER="${NAME}-nginx-1"
    NGINX_PORT=$(docker port "$NGINX_CONTAINER" 80 2>/dev/null | head -1 | cut -d: -f2)

    # Get the mitmweb UI port
    MITM_CONTAINER="${NAME}-mitmproxy-1"
    MITM_PORT=$(docker port "$MITM_CONTAINER" 8081 2>/dev/null | head -1 | cut -d: -f2)

    # Get the postgres container's host port
    DB_CONTAINER="${NAME}-db-1"
    DB_PORT=$(docker port "$DB_CONTAINER" 5432 2>/dev/null | head -1 | cut -d: -f2)

    # Get the redis container's host port
    REDIS_CONTAINER="${NAME}-redis-1"
    REDIS_PORT=$(docker port "$REDIS_CONTAINER" 6379 2>/dev/null | head -1 | cut -d: -f2)

    # Register with portless
    portless alias "$NAME" "$NGINX_PORT" --tld test --force 2>/dev/null || true
    portless alias "debug.$NAME" "$MITM_PORT" --tld test --force 2>/dev/null || true
    portless alias "db.$NAME" "$DB_PORT" --tld test --force 2>/dev/null || true
    portless alias "redis.$NAME" "$REDIS_PORT" --tld test --force 2>/dev/null || true

    echo ""
    echo "Stack '$NAME' is running with mitmproxy:"
    echo "  App:      https://$NAME.test"
    echo "  mitmweb:  https://debug.$NAME.test (request inspector, password: password)"
    echo "  Postgres: db.$NAME.test (port $DB_PORT)"
    echo "  Redis:    redis.$NAME.test (port $REDIS_PORT)"
    echo "  psql:     psql -h db.$NAME.test -U infisical -d infisical"

# List all running worktree stacks
ls:
    @docker ps --filter "label=com.docker.compose.project" --format "table {{{{.Names}}}}\t{{{{.Status}}}}\t{{{{.Ports}}}}" | head -50

# --- Standalone tools ---

mailpit:
    docker run -d --name mailpit -p 1025:1025 -p 8025:8025 --restart unless-stopped axllent/mailpit

mailpit-stop:
    docker stop mailpit && docker rm mailpit

# --- TypeScript native compiler (tsgo) ---

tsgo-fe:
    cd frontend && npm install --save-dev @typescript/native-preview@latest

tsgo-be:
    cd backend && npm install --save-dev @typescript/native-preview@latest

tsgo: tsgo-fe tsgo-be

3. docker-compose.dev.override.yml (project root)

Profiles out pgAdmin, redis-commander, and smtp-server from the default dev stack so they only start with --profile tools.

services:
  pgadmin:
    profiles: [tools]
  redis-commander:
    profiles: [tools]
  smtp-server:
    profiles: [tools]

4. docker-compose.worktree.yml (project root)

Full isolated stack for worktrees. All host ports are random (0:port) to avoid conflicts when running multiple stacks.

services:
  nginx:
    image: nginx
    restart: "always"
    ports:
      - "0:80"
    volumes:
      - ./nginx/default.dev.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - backend
      - frontend

  db:
    image: postgres:14-alpine
    ports:
      - "0:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: infisical
      POSTGRES_USER: infisical
      POSTGRES_DB: infisical

  redis:
    image: redis
    environment:
      - ALLOW_EMPTY_PASSWORD=yes
    ports:
      - "0:6379"
    volumes:
      - redis_data:/data

  clickhouse:
    image: clickhouse/clickhouse-server:25.12.5
    restart: unless-stopped
    ports:
      - "0:8123"
      - "0:9000"
    volumes:
      - clickhouse_data:/var/lib/clickhouse
      - clickhouse_logs:/var/log/clickhouse-server
    environment:
      - CLICKHOUSE_DB=infisical
      - CLICKHOUSE_USER=infisical
      - CLICKHOUSE_PASSWORD=infisical
      - CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT=1
    ulimits:
      nofile:
        soft: 262144
        hard: 262144

  backend:
    image: infisical-dev-backend:latest
    build:
      context: ./backend
      dockerfile: Dockerfile.dev.fips
    depends_on:
      db:
        condition: service_started
      redis:
        condition: service_started
      clickhouse:
        condition: service_started
    env_file:
      - .env
    ports:
      - "0:4000"
    environment:
      - NODE_ENV=development
      - DB_CONNECTION_URI=postgres://infisical:infisical@db/infisical?sslmode=disable
      - TELEMETRY_ENABLED=false
    volumes:
      - ./backend/src:/app/src
      - softhsm_tokens:/etc/softhsm2/tokens
    extra_hosts:
      - "host.docker.internal:host-gateway"

  frontend:
    image: infisical-dev-frontend:latest
    restart: unless-stopped
    depends_on:
      - backend
    build:
      context: ./frontend
      dockerfile: Dockerfile.dev
    volumes:
      - ./frontend/src:/app/src/
      - ./frontend/public:/app/public
    env_file: .env

volumes:
  postgres_data:
    external: true
    name: ${PG_VOLUME}
  redis_data:
    driver: local
  clickhouse_data:
    driver: local
  clickhouse_logs:
    driver: local
  softhsm_tokens:
    driver: local

5. docker-compose.worktree.debug.yml (project root)

Compose override that adds mitmproxy between nginx and backend for request inspection.

services:
  mitmproxy:
    image: mitmproxy/mitmproxy
    command: mitmweb --mode reverse:http://backend:4000 --web-host 0.0.0.0 --listen-port 4000 --no-web-open-browser --set web_password=password
    restart: unless-stopped
    ports:
      - "0:8081"
    depends_on:
      - backend

  nginx:
    volumes:
      - ./nginx/default.dev.debug.conf:/etc/nginx/conf.d/default.conf:ro

6. nginx/default.dev.debug.conf

Same as nginx/default.dev.conf but routes API traffic through mitmproxy instead of directly to backend. Only difference is line 2:

upstream api {
    server mitmproxy:4000;
}

server {
    listen 80;

    large_client_header_buffers 8 128k;
    client_header_buffer_size 128k;

    # WebSocket endpoint for PAM web access
    location ~ ^/api/v1/pam/accounts/[^/]+/web-access$ {
        proxy_set_header X-Real-RIP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_buffering off;
        proxy_cache off;

        proxy_read_timeout 4200s;
        proxy_send_timeout 4200s;

        proxy_pass http://api;
        proxy_redirect off;

        proxy_cookie_path / "/; SameSite=strict";
    }

    location ~ ^/(api|secret-scanning/webhooks) {
        proxy_set_header X-Real-RIP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;

        proxy_pass http://api;
        proxy_redirect off;

        proxy_cookie_path / "/; SameSite=strict";
    }

    location /runtime-ui-env.js {
        proxy_set_header X-Real-RIP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;

        proxy_pass http://api;
        proxy_redirect off;

        proxy_cookie_path / "/; HttpOnly; SameSite=strict";
    }

    location /api/v3/migrate {
        client_max_body_size 25M;

        proxy_set_header X-Real-RIP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;

        proxy_pass http://api;
        proxy_redirect off;

        proxy_cookie_path / "/; HttpOnly; SameSite=strict";
    }

    location /scep {
        proxy_set_header X-Real-RIP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;

        proxy_pass http://api;
        proxy_redirect off;

        proxy_cookie_path / "/; HttpOnly; SameSite=strict";
    }

    location ~ /\.well-known {
        proxy_set_header X-Real-RIP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;

        proxy_set_header X-SSL-Client-Cert $ssl_client_escaped_cert;

        proxy_pass http://api;
        proxy_redirect off;

        proxy_cookie_path / "/; HttpOnly; SameSite=strict";
    }

    location / {
        include /etc/nginx/mime.types;

        proxy_set_header X-Real-RIP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;

        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_pass http://frontend:3000;
        proxy_redirect off;
    }
}

7. .git/local-hooks/post-checkout

Git hook that automatically copies all dev files into new worktrees. Must be executable (chmod +x).

#!/bin/bash
OLD_REF=$1

# Only run for new worktrees (old ref is all zeros)
if [ "$OLD_REF" = "0000000000000000000000000000000000000000" ]; then
    MAIN_REPO="$(git rev-parse --git-common-dir)/.."
    WORKTREE="$(git rev-parse --show-toplevel)"

    cp "$MAIN_REPO/justfile" "$WORKTREE/" 2>/dev/null
    cp "$MAIN_REPO/docker-compose.worktree.yml" "$WORKTREE/" 2>/dev/null
    cp "$MAIN_REPO/docker-compose.worktree.debug.yml" "$WORKTREE/" 2>/dev/null
    cp "$MAIN_REPO/docker-compose.dev.override.yml" "$WORKTREE/" 2>/dev/null
    cp "$MAIN_REPO/README.dev.md" "$WORKTREE/" 2>/dev/null
    cp "$MAIN_REPO/.env" "$WORKTREE/" 2>/dev/null
    mkdir -p "$WORKTREE/nginx"
    cp "$MAIN_REPO/nginx/default.dev.debug.conf" "$WORKTREE/nginx/" 2>/dev/null
    echo "Dev files copied into worktree: $WORKTREE"
fi

8. Git hooks path config

Husky overrides the default hooks directory. To use our custom hook alongside Husky's pre-commit:

# Create local hooks directory
mkdir -p .git/local-hooks/_

# Copy Husky's pre-commit hook and helper
cp .husky/pre-commit .git/local-hooks/
cp .husky/_/husky.sh .git/local-hooks/_/

# Copy the post-checkout hook (from step 7 above)
# Make it executable
chmod +x .git/local-hooks/post-checkout
chmod +x .git/local-hooks/pre-commit

# Point git to the local hooks directory
git config --local core.hooksPath .git/local-hooks

Workflow

Main repo (day-to-day)

just up-dev                   # → https://infisical.test
just down-dev                 # stop

New task in a worktree

# Create worktree (hook auto-copies dev files + .env)
git fetch origin
git worktree add ../my-feature -b feat/my-feature origin/main
cd ../my-feature

# Optional: enable fast TS compiler
just tsgo

# Start isolated stack
just up                       # → https://my-feature.test

Review a PR

git fetch origin
git worktree add ../review-pr origin/feat/some-feature
cd ../review-pr
just up                       # → https://review-pr.test

Multiple stacks simultaneously

Both run at the same time on different domains:

  • https://my-feature.test
  • https://review-pr.test

Inspect API requests

just up-debug                 # → app at https://my-feature.test
                              # → mitmweb UI at https://debug.my-feature.test (password: password)

Stop / clean up

just down                     # stop containers, keep volumes (resume later)
just up                       # resume with same DB state

just rm                       # remove containers + volumes (permanent)

# Remove the worktree
cd ~/infi/repos/infisical
git worktree remove ../review-pr

Standalone mailpit (shared SMTP)

just mailpit                  # start — SMTP on 1025, UI on http://localhost:8025
just mailpit-stop             # stop

All Commands

Command What it does
just up-dev Start main dev stack → https://infisical.test
just up-dev-ldap Start with LDAP profile
just up-dev-sso Start with SSO profile
just up-dev-metrics Start with metrics profile
just down-dev Stop main dev stack
just up Start worktree stack (uses directory name)
just up my-name Start with custom name
just up --no-fips Clone non-FIPS postgres volume
just up-debug Start with mitmproxy request inspector
just down Stop worktree stack, keep volumes
just rm Remove worktree stack + volumes
just ls List all running stacks
just mailpit Start standalone mailpit
just mailpit-stop Stop mailpit
just tsgo Install latest tsgo in both frontend and backend
just tsgo-fe Install latest tsgo in frontend only
just tsgo-be Install latest tsgo in backend only
just reviewable Lint + typecheck both frontend and backend
just reviewable-ui Lint + typecheck frontend
just reviewable-api Lint + typecheck backend
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment