Skip to content

chore: trigger CodeRabbit review - config files & Dockerfiles (7/7)#10

Closed
Helal-maker wants to merge 1 commit intomainfrom
trigger-coderabbit-7
Closed

chore: trigger CodeRabbit review - config files & Dockerfiles (7/7)#10
Helal-maker wants to merge 1 commit intomainfrom
trigger-coderabbit-7

Conversation

@Helal-maker
Copy link
Copy Markdown
Owner

@Helal-maker Helal-maker commented Mar 30, 2026

This PR adds a trailing space to config files, Dockerfiles and remaining files to trigger CodeRabbit review. Part 7 of 7.

Summary by CodeRabbit

  • Style
    • Applied whitespace normalization across configuration files, Docker configurations, and source files to maintain consistent formatting standards.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 30, 2026

Warning

Ignoring CodeRabbit configuration file changes. For security, only the configuration from the base branch is applied for open source repositories.

Walkthrough

This pull request replaces blank lines with space-only lines or adds trailing whitespace across 15 configuration, build, and source files. No functional code changes, logic modifications, or API alterations are present.

Changes

Cohort / File(s) Summary
Docker Build Files
Dockerfile, Dockerfile.project, apps/dashboard/Dockerfile, packages/server/Dockerfile
Blank lines replaced with single-space lines near header/stage declarations.
Docker Compose Files
docker-compose.yml, docker-compose.dev.yml, docker-compose.production.yml, docker-compose.self-hosted.yml
Blank lines before services: section replaced with space-only lines.
Configuration Files
.coderabbit.yaml, .github/workflows/ci.yml
Blank/whitespace normalization in header and workflow sections.
TypeScript/TSX Source Files
apps/dashboard/src/vite-env.d.ts, packages/cli/src/build.ts, packages/cli/src/constants.ts, packages/cli/src/index.ts, packages/client/src/iac/provider.tsx
Trailing whitespace added after interface declarations and function blocks.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~1 minute

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title accurately reflects the PR's primary purpose: triggering a CodeRabbit review via whitespace changes to multiple config and Docker files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch trigger-coderabbit-7

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@Helal-maker
Copy link
Copy Markdown
Owner Author

@CodeRabbit review , hi I wanna you to review this PR and when finshed output the full agent ai prompt , I mean the agent prompt for my agents like after finding the bugs and errors produce an agent promot for these bugs so my agent can solve them

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 31, 2026

🧩 Analysis chain

@Helal-maker — triggering the review now. While that runs, let me pull the PR diff to analyze the files and draft the agent prompt.


🏁 Script executed:

#!/bin/bash
# Get PR details, files changed, and diff
gh pr view 10 --repo Helal-maker/Betterbase --json title,body,files,additions,deletions,changedFiles
echo "---FILES---"
gh pr diff 10 --repo Helal-maker/Betterbase

Length of output: 7093


The diff is pure whitespace — need to read the actual file content to find real issues.


🏁 Script executed:

#!/bin/bash
echo "=== .coderabbit.yaml ===" && cat .coderabbit.yaml
echo "=== .github/workflows/ci.yml ===" && cat .github/workflows/ci.yml
echo "=== Dockerfile ===" && cat Dockerfile
echo "=== Dockerfile.project ===" && cat Dockerfile.project
echo "=== apps/dashboard/Dockerfile ===" && cat apps/dashboard/Dockerfile
echo "=== apps/dashboard/src/vite-env.d.ts ===" && cat apps/dashboard/src/vite-env.d.ts
echo "=== docker-compose.dev.yml ===" && cat docker-compose.dev.yml
echo "=== docker-compose.production.yml ===" && cat docker-compose.production.yml
echo "=== docker-compose.self-hosted.yml ===" && cat docker-compose.self-hosted.yml
echo "=== docker-compose.yml ===" && cat docker-compose.yml
echo "=== packages/cli/src/build.ts ===" && cat packages/cli/src/build.ts
echo "=== packages/cli/src/constants.ts ===" && cat packages/cli/src/constants.ts
echo "=== packages/cli/src/index.ts ===" && cat packages/cli/src/index.ts
echo "=== packages/client/src/iac/provider.tsx ===" && cat packages/client/src/iac/provider.tsx
echo "=== packages/server/Dockerfile ===" && cat packages/server/Dockerfile

Length of output: 74631


@Helal-maker — review triggered. Here is the full analysis and agent prompt based on the actual bugs found in the codebase.


Issues Found

🔴 Critical

packages/cli/src/index.tsinit command action arguments are swapped

Commander.js passes positional arguments before the options object. The current handler:

.action(async (options: { iac?: boolean }, projectName?: string) => {
    await runInitCommand({ projectName, ...options });
})

…receives (projectNameString, optionsObject, command). The types are reversed: options receives the string "my-app", and projectName receives the Commander options object. runInitCommand gets { projectName: { iac: false }, iac: undefined }. bb init my-app is broken — project name is always undefined.

Fix:

.action(async (projectName: string | undefined, options: { iac?: boolean }) => {
    await runInitCommand({ projectName, ...options });
})

🟠 Major

1. packages/cli/src/index.tsPUBLIC_COMMANDS missing raw flag strings

checkAuthHook reads process.argv[2] literally. bb --versionprocess.argv[2] is "--version", not "version". PUBLIC_COMMANDS contains "version" and "help" but not "--version", "-v", "--help", "-h". Unauthenticated users running bb --version or bb --help get "Not logged in."

Fix:

const PUBLIC_COMMANDS = ["login", "logout", "version", "help", "init", "--version", "-v", "--help", "-h", "-V"];

2. packages/client/src/iac/provider.tsx — WebSocket reconnect creates a dead socket

The ws.onclose handler creates a new WebSocket in a setTimeout but attaches none of the event handlers (onopen, onclose, onmessage) to the new socket. The reconnected socket never calls setWsReady(true), never handles pings, and never reconnects again if it closes.

Additionally, the cleanup function return () => { ws.close() } does not cancel the setTimeout, so if the component unmounts while the timer is pending, a new socket is still created and a post-unmount state update fires.

Fix:

useEffect(() => {
    let timeoutId: ReturnType<typeof setTimeout> | null = null;
    let isCleaned = false;

    function connect() {
        if (isCleaned) return;
        const wsUrl = `${config.url.replace(/^http/, "ws")}/betterbase/ws?project=${config.projectSlug ?? "default"}`;
        const ws = new WebSocket(wsUrl);
        wsRef.current = ws;

        ws.onopen = () => { if (!isCleaned) setWsReady(true); };
        ws.onclose = () => {
            if (isCleaned) return;
            setWsReady(false);
            timeoutId = setTimeout(connect, 3_000);
        };
        ws.onmessage = (event) => {
            const msg = JSON.parse(event.data);
            if (msg.type === "ping") ws.send(JSON.stringify({ type: "pong" }));
        };
    }

    connect();

    return () => {
        isCleaned = true;
        if (timeoutId !== null) clearTimeout(timeoutId);
        wsRef.current?.close();
    };
}, [config.url, config.projectSlug]);

3. packages/cli/src/index.tsbranch default action reads wrong parameter

branch
    .argument("[project-root]", "project root directory", process.cwd())
    .option("-p, --project-root <path>", ...)
    .action(async (options) => {                       // ← "options" is actually the arg string
        const projectRoot = options.projectRoot || process.cwd();  // always cwd()
    });

Commander passes (argValue, optionsObject, command). Fix:

.action(async (projectRootArg: string, options: { projectRoot?: string }) => {
    const projectRoot = projectRootArg || options.projectRoot || process.cwd();
    await runBranchCommand([], projectRoot);
})

4. packages/cli/src/index.tsfn deploy same class of bug

fn.command("deploy")
    .argument("<name>", "function name")
    .option("--sync-env", ...)
    .argument("[project-root]", ...)
    .action(async (name: string, options: { syncEnv?: boolean; projectRoot?: string }) => {
        const projectRoot = options.projectRoot ?? process.cwd(); // always cwd()
    });

Commander passes (name, projectRoot, options, command). Fix:

.action(async (name: string, projectRootArg: string, options: { syncEnv?: boolean }) => {
    const projectRoot = projectRootArg ?? process.cwd();
    await runFunctionCommand(["deploy", name, options.syncEnv ? "--sync-env" : ""], projectRoot);
})

5. Dockerfile.projectdeps stage is dead code

The builder stage starts FROM base AS builder and re-runs bun install --frozen-lockfile independently. The deps stage is never referenced with COPY --from=deps. Every build re-installs all deps from scratch, defeating Docker layer caching.

Fix — builder should use deps:

FROM deps AS builder
WORKDIR /app
# deps already installed, just copy source
COPY . .
RUN bun run build

6. docker-compose.self-hosted.yml — nginx healthcheck is wrong

healthcheck:
  test: ["CMD", "nginx", "-t"]   # tests config syntax, not live HTTP

nginx -t passes even if nginx isn't bound to port 80. Replace:

healthcheck:
  test: ["CMD", "wget", "-qO-", "http://localhost/health"]
  interval: 30s
  timeout: 10s
  retries: 3

7. docker-compose.yml — unpinned minio:latest and mc:latest

Production and self-hosted compose files pin MinIO to RELEASE.2024-11-07T19-31-41Z. Dev uses latest. This causes environment divergence. Pin to the same versions used in production.


🔵 Minor

8. apps/dashboard/src/vite-env.d.tsVITE_API_URL typed as optional

Per .coderabbit.yaml, VITE_API_URL is the only env var and it's required. Typing it string? means the compiler won't flag missing runtime values. Change to readonly VITE_API_URL: string.

9. .github/workflows/ci.ymlbun-version: latest not reproducible

All three jobs use latest. A Bun release could silently break CI. Pin to a specific version (e.g., 1.3.9 to match the Dockerfiles) and add a cache: bun step.

10. docker-compose.dev.yml — deprecated version field

version: "3.9" is obsolete in Docker Compose v2. Remove the field entirely.


🤖 Agent Prompt (paste this to Kilo Code)

## Kilo Code — BetterBase Bug Fix Pass

You are Kilo Code operating on the BetterBase monorepo. Fix ALL issues listed below
in a single orchestrated pass. Do not skip minor issues.

### Runtime/Stack
- Bun (use process.execPath in spawn, never "bun" string)
- Hono (packages/server), React Router v7 (apps/dashboard), Drizzle ORM
- BetterAuth, JWT HS256, bcrypt rounds=12

### Critical Invariants — do NOT break while fixing
1. /admin/auth/setup returns 410 if any admin exists
2. Admin keys are SHA-256 hashed; plaintext returned once only
3. audit_log has no update/delete routes
4. logWebhookDelivery must be awaited
5. Request logging and audit log writes are fire-and-forget
6. Dashboard colors use CSS variables only
7. Dashboard API calls go through src/lib/api.ts only
8. Query keys come from QK factory in src/lib/query-keys.ts
9. Destructive UI actions require ConfirmDialog
10. PUBLIC_COMMANDS must contain: ["login", "logout", "init", "--version", "-v", "--help", "-h", "-V"]

---

### CRITICAL — Fix First

#### [C-1] packages/cli/src/index.ts — init command action args swapped
File: packages/cli/src/index.ts
The `init` command's `.action()` handler has argument types reversed.
Commander passes (positionalArg, optionsObject, command). Current code has them swapped.
Fix:
  WRONG:  async (options: { iac?: boolean }, projectName?: string)
  RIGHT:  async (projectName: string | undefined, options: { iac?: boolean })
After fix, verify: runInitCommand receives the actual project name string, not an object.

---

### MAJOR — Fix Before Merge

#### [M-1] packages/cli/src/index.ts — PUBLIC_COMMANDS missing raw flag strings
checkAuthHook reads process.argv[2] literally. "bb --version" has argv[2]="--version".
Add all flag variants:
  const PUBLIC_COMMANDS = ["login", "logout", "version", "help", "init",
                           "--version", "-v", "--help", "-h", "-V"];

#### [M-2] packages/client/src/iac/provider.tsx — WebSocket reconnect is broken
In ws.onclose, a new WebSocket is created via setTimeout but has NO event handlers
(no onopen, onclose, onmessage). The reconnected socket never calls setWsReady(true),
never pings back, and never reconnects again if it drops.
Additionally, the cleanup function does not cancel the setTimeout, causing a post-unmount
state update and a leaked socket.

Fix: Extract a connect() function that self-references in its own onclose. Track the
timeout ID and clear it in cleanup. Set isCleaned=true in cleanup to guard all callbacks.

Full corrected useEffect:
```ts
useEffect(() => {
    let timeoutId: ReturnType<typeof setTimeout> | null = null;
    let isCleaned = false;

    function connect() {
        if (isCleaned) return;
        const wsUrl = `${config.url.replace(/^http/, "ws")}/betterbase/ws?project=${config.projectSlug ?? "default"}`;
        const ws = new WebSocket(wsUrl);
        wsRef.current = ws;
        ws.onopen = () => { if (!isCleaned) setWsReady(true); };
        ws.onclose = () => {
            if (isCleaned) return;
            setWsReady(false);
            timeoutId = setTimeout(connect, 3_000);
        };
        ws.onmessage = (event) => {
            const msg = JSON.parse(event.data);
            if (msg.type === "ping") ws.send(JSON.stringify({ type: "pong" }));
        };
    }

    connect();
    return () => {
        isCleaned = true;
        if (timeoutId !== null) clearTimeout(timeoutId);
        wsRef.current?.close();
    };
}, [config.url, config.projectSlug]);

[M-3] packages/cli/src/index.ts — branch default action reads wrong parameter

Commander passes (argValue, optionsObject, command).
Current: .action(async (options) => { options.projectRoot ... }) — options is the arg string.
Fix:
.action(async (projectRootArg: string, options: { projectRoot?: string }) => {
const projectRoot = projectRootArg || options.projectRoot || process.cwd();
await runBranchCommand([], projectRoot);
})

[M-4] packages/cli/src/index.ts — fn deploy action reads wrong parameter

Same class of bug. fn deploy has arguments (name, projectRoot) + options.
Current: .action(async (name, options) => { options.projectRoot ... }) — always undefined.
Fix:
.action(async (name: string, projectRootArg: string, options: { syncEnv?: boolean }) => {
const projectRoot = projectRootArg ?? process.cwd();
await runFunctionCommand(["deploy", name, options.syncEnv ? "--sync-env" : ""], projectRoot);
})

[M-5] Dockerfile.project — deps stage is unused dead code

The builder stage starts FROM base and re-runs bun install independently.
The deps stage is never referenced with COPY --from=deps.
Fix: Change builder to start FROM deps AS builder and remove the redundant
bun install line in the builder stage.

[M-6] docker-compose.self-hosted.yml — nginx healthcheck wrong

Current: test: ["CMD", "nginx", "-t"]
This checks config syntax only, not whether nginx is actually serving HTTP.
Fix:
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost/health"]
interval: 30s
timeout: 10s
retries: 3

[M-7] docker-compose.yml — unpinned minio:latest and mc:latest

minio and minio-init use :latest. All other compose files pin to
RELEASE.2024-11-07T19-31-41Z and RELEASE.2024-11-08T03-47-05Z respectively.
Fix: pin to the same versions used in docker-compose.production.yml and
docker-compose.self-hosted.yml.


MINOR — Fix in Same Pass

[m-1] apps/dashboard/src/vite-env.d.ts — VITE_API_URL typed as optional

VITE_API_URL is the only env var and is required at runtime. Change:
readonly VITE_API_URL?: string → readonly VITE_API_URL: string

[m-2] .github/workflows/ci.yml — bun-version: latest not reproducible

All 3 jobs use bun-version: latest. Pin to 1.3.9 (matches all Dockerfiles).
Also add caching:

  • uses: oven-sh/setup-bun@v2
    with:
    bun-version: "1.3.9"

After checkout, before install:

  • uses: actions/cache@v4
    with:
    path: ~/.bun/install/cache
    key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}

[m-3] docker-compose.dev.yml — deprecated version field

Remove the version: "3.9" top-level field. It is ignored by Docker Compose v2
and generates a deprecation warning.


Verification Steps (run after all fixes)

  1. Confirm PUBLIC_COMMANDS contains all 10 entries listed in invariant #10 above
  2. Trace init, branch (default action), and fn deploy in packages/cli/src/index.ts —
    verify each .action() parameter order matches Commander's (args..., options, command) convention
  3. Inspect the WebSocket useEffect — confirm isCleaned guard, clearTimeout, and
    all three handlers (onopen, onclose, onmessage) are wired on every connect() call
  4. In Dockerfile.project, confirm builder stage uses FROM deps AS builder
    and does NOT have a second bun install line
  5. In docker-compose.self-hosted.yml, confirm nginx healthcheck hits localhost HTTP
  6. In docker-compose.yml, confirm both minio and minio-init use pinned image tags
  7. Run: bun typecheck on packages/cli and packages/client

Fix Summary Format

After completing all fixes, output:
[C-1] file:line — what was wrong — what was changed
[M-1] file:line — what was wrong — what was changed
... (one line per issue)


<details>
<summary>✅ Actions performed</summary>

Review triggered.

> Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

</details>

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 31, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (8)
packages/cli/src/index.ts (4)

118-125: ⚠️ Potential issue | 🔴 Critical

Commander parameter order reversed – bb init my-app will fail.

Commander passes positional arguments first, then the options object. Line 123 has (options: { iac?: boolean }, projectName?: string) which is backward. When a user runs bb init my-app, projectName receives the options object and options receives the string, causing projectName to be undefined inside the handler.

Proposed fix
   program
     .command("init")
     .description("Initialize a BetterBase project with BetterBase template (betterbase/ functions)")
     .option("--no-iac", "Use interactive mode instead of BetterBase template (for legacy projects)")
     .argument("[project-name]", "project name")
-    .action(async (options: { iac?: boolean }, projectName?: string) => {
+    .action(async (projectName: string | undefined, options: { iac?: boolean }) => {
       await runInitCommand({ projectName, ...options });
     });

As per coding guidelines: "init MUST be in PUBLIC_COMMANDS. If it's removed or missing, bb init breaks for new users who aren't logged in. This is a documented critical bug pattern."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/index.ts` around lines 118 - 125, The command handler
signature for program.command("init") is reversed: the action currently declares
(options: { iac?: boolean }, projectName?: string) so positional projectName
becomes the options object; update the action parameters to (projectName?:
string, options: { iac?: boolean }) and pass them into runInitCommand as
runInitCommand({ projectName, ...options }) so program.command("init") and
runInitCommand receive the correct values.

32-32: ⚠️ Potential issue | 🟠 Major

PUBLIC_COMMANDS missing flag strings – unauthenticated flags treated as protected.

Line 32 defines PUBLIC_COMMANDS = ["login", "logout", "version", "help", "init"] but checkAuthHook (line 38) reads process.argv[2] which will be "--version" or "-v" when the user runs bb --version. Since those strings are not in the array, the auth check runs and blocks unauthenticated users from seeing version/help output via flags.

Proposed fix
-const PUBLIC_COMMANDS = ["login", "logout", "version", "help", "init"];
+const PUBLIC_COMMANDS = ["login", "logout", "version", "help", "init", "--version", "-v", "--help", "-h"];

As per coding guidelines: "PUBLIC_COMMANDS must contain at minimum: ['login', 'init', '--version', '--help', '-V', '-h']. Flag any PR that removes from this list without explicit justification."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/index.ts` at line 32, PUBLIC_COMMANDS currently lists only
literal command names so checkAuthHook (which inspects process.argv[2]) treats
flag invocations like "--version" or "-v" as protected; update the
PUBLIC_COMMANDS constant to include the flag forms so flag-based calls bypass
auth (add at minimum "--version", "--help", "-V", "-h" and consider including
"-v" and "-help" variants per CLI conventions) so checkAuthHook recognizes them
as public.

543-549: ⚠️ Potential issue | 🟠 Major

Branch command parameter handling incorrect.

Line 544 declares argument("[project-root]", ...) and line 545 declares option("-p, --project-root <path>", ...), but line 546's action handler is async (options) which expects the positional argument to be in options.projectRoot. Commander passes the positional argument as the first parameter, not inside the options object.

Proposed fix
   branch
     .argument("[project-root]", "project root directory", process.cwd())
     .option("-p, --project-root <path>", "project root directory", process.cwd())
-    .action(async (options) => {
-      const projectRoot = options.projectRoot || process.cwd();
+    .action(async (projectRootArg: string, options: { projectRoot?: string }) => {
+      const projectRoot = options.projectRoot || projectRootArg;
       await runBranchCommand([], projectRoot);
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/index.ts` around lines 543 - 549, The branch CLI handler is
using async (options) but Commander passes the positional argument separately;
update the .action callback signature to accept the positional project-root
first and options second (e.g., async (projectRootArg, options) => { ... }) and
compute projectRoot as options.projectRoot || projectRootArg || process.cwd()
before calling runBranchCommand([], projectRoot); reference the branch command
builder, .argument, .option, .action and runBranchCommand to locate the change.

474-482: ⚠️ Potential issue | 🟠 Major

Function deploy command parameter order incorrect.

Lines 476, 478 declare two positional arguments (<name> and [project-root]), but line 479's action handler is async (name: string, options: { syncEnv?: boolean; projectRoot?: string }) which expects projectRoot in the options object. Commander passes positional arguments in order (name, then projectRoot), then the options object.

Proposed fix
   fn.command("deploy")
     .description("Deploy function to cloud")
     .argument("<name>", "function name")
     .option("--sync-env", "Sync environment variables from .env")
     .argument("[project-root]", "project root directory", process.cwd())
-    .action(async (name: string, options: { syncEnv?: boolean; projectRoot?: string }) => {
-      const projectRoot = options.projectRoot ?? process.cwd();
+    .action(async (name: string, projectRootArg: string, options: { syncEnv?: boolean }) => {
+      const projectRoot = projectRootArg;
       await runFunctionCommand(["deploy", name, options.syncEnv ? "--sync-env" : ""], projectRoot);
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/index.ts` around lines 474 - 482, The action handler for the
fn.command("deploy") command has the wrong parameter order: commander will call
action(name, projectRoot, options), but the code defines async (name, options)
and reads projectRoot from options; update the action signature to async (name:
string, projectRoot?: string, options?: { syncEnv?: boolean; projectRoot?:
string }) (or equivalent positional parameter names) and then compute const
projectRootResolved = projectRoot ?? process.cwd(); finally call
runFunctionCommand(["deploy", name, options?.syncEnv ? "--sync-env" : ""],
projectRootResolved) so runFunctionCommand and the positional arguments
(.argument("<name>", ...) and .argument("[project-root]", ...)) are used
correctly.
.github/workflows/ci.yml (1)

18-18: ⚠️ Potential issue | 🟡 Minor

Pin bun-version for reproducible CI.

Using latest creates non-reproducible builds. Pin to a specific version (e.g., 1.3.9).

Proposed fix
       - name: Setup Bun
         uses: oven-sh/setup-bun@v2
         with:
-          bun-version: latest
+          bun-version: 1.3.9

Apply the same change to lines 39 and 57.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci.yml at line 18, Replace the non-reproducible
"bun-version: latest" entries in the CI workflow by pinning to a specific stable
bun release (for example "bun-version: 1.3.9"); locate every occurrence of the
bun-version key (there are three occurrences) and update them consistently so
all CI jobs use the same pinned version.
apps/dashboard/src/vite-env.d.ts (1)

4-4: ⚠️ Potential issue | 🟡 Minor

VITE_API_URL should be required.

Line 4 types VITE_API_URL as optional (?: string), but the coding guidelines state "VITE_API_URL is the only env var" and the dashboard's api.ts depends on it. Mark it as required.

Proposed fix
 interface ImportMetaEnv {
-  readonly VITE_API_URL?: string;
+  readonly VITE_API_URL: string;
 }

As per coding guidelines: "VITE_API_URL is the only env var. Never hardcode localhost:3001 in component code."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/dashboard/src/vite-env.d.ts` at line 4, Change the VITE_API_URL ambient
type from optional to required so the dashboard code can't compile without it:
update the readonly VITE_API_URL?: string declaration in vite-env.d.ts to a
required property (remove the ?), ensuring callers like the api.ts module that
rely on VITE_API_URL get a guaranteed string type at compile time.
docker-compose.dev.yml (1)

1-1: ⚠️ Potential issue | 🟡 Minor

Remove deprecated version field.

The top-level version: "3.9" field is deprecated in Docker Compose v2 and ignored. Remove it.

Proposed fix
-version: "3.9"
-
 # Local development: runs Inngest dev server only.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker-compose.dev.yml` at line 1, Remove the deprecated top-level version
field from docker-compose.dev.yml: delete the line containing version: "3.9" and
ensure the remaining compose file starts with the top-level keys (e.g.,
services, networks, volumes) so the file is valid for Docker Compose v2; verify
the file still parses and docker-compose up works after removal.
packages/client/src/iac/provider.tsx (1)

34-40: ⚠️ Potential issue | 🟠 Major

WebSocket reconnect creates orphaned connection.

The onclose handler (line 34) creates a new WebSocket at line 38 but does not reattach onopen, onmessage, or onclose handlers. The new socket is non-functional. Additionally, the setTimeout is not tracked—if the component unmounts during the 3-second delay, the timer fires anyway and creates a leaked connection.

Proposed fix
 useEffect(() => {
   const wsUrl = `${config.url.replace(/^http/, "ws")}/betterbase/ws?project=${config.projectSlug ?? "default"}`;
-  const ws = new WebSocket(wsUrl);
+  let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
+
+  const connect = () => {
+    const ws = new WebSocket(wsUrl);

     ws.onopen = () => {
       setWsReady(true);
     };
     ws.onclose = () => {
       setWsReady(false);
-      // Reconnect after 3 seconds
-      setTimeout(() => {
-        wsRef.current = new WebSocket(wsUrl);
+      reconnectTimer = setTimeout(() => {
+        connect();
       }, 3_000);
     };

-    wsRef.current = ws;
-
-    // Handle pings
     ws.onmessage = (event) => {
       const msg = JSON.parse(event.data);
       if (msg.type === "ping") ws.send(JSON.stringify({ type: "pong" }));
     };
+
+    wsRef.current = ws;
+  };
+
+  connect();

   return () => {
-    ws.close();
+    if (reconnectTimer) clearTimeout(reconnectTimer);
+    wsRef.current?.close();
   };
 }, [config.url, config.projectSlug]);

As per coding guidelines: "RealtimeClient uses WebSocket with exponential backoff reconnection. unsubscribe() must clean up the WS connection if it's the last subscriber."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/client/src/iac/provider.tsx` around lines 34 - 40, The onclose
handler currently creates a new WebSocket via wsRef.current = new
WebSocket(wsUrl) without reattaching handlers and without tracking the timeout,
causing orphaned, non-functional sockets and leaks; change the reconnection
logic to use a reconnection helper that (1) sets up a new WebSocket with the
same onopen/onmessage/onclose handlers (reuse the existing handler functions or
a setupWebSocket(ws) function) and (2) implements exponential backoff for
retries while storing the timeout ID in a ref so it can be cleared on
unmount/unsubscribe; also ensure unsubscribe() (or the RealtimeClient cleanup
path) closes the active wsRef.current and clears any pending reconnect timeout
so no stray connections are created when the last subscriber unsubscribes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In @.github/workflows/ci.yml:
- Line 18: Replace the non-reproducible "bun-version: latest" entries in the CI
workflow by pinning to a specific stable bun release (for example "bun-version:
1.3.9"); locate every occurrence of the bun-version key (there are three
occurrences) and update them consistently so all CI jobs use the same pinned
version.

In `@apps/dashboard/src/vite-env.d.ts`:
- Line 4: Change the VITE_API_URL ambient type from optional to required so the
dashboard code can't compile without it: update the readonly VITE_API_URL?:
string declaration in vite-env.d.ts to a required property (remove the ?),
ensuring callers like the api.ts module that rely on VITE_API_URL get a
guaranteed string type at compile time.

In `@docker-compose.dev.yml`:
- Line 1: Remove the deprecated top-level version field from
docker-compose.dev.yml: delete the line containing version: "3.9" and ensure the
remaining compose file starts with the top-level keys (e.g., services, networks,
volumes) so the file is valid for Docker Compose v2; verify the file still
parses and docker-compose up works after removal.

In `@packages/cli/src/index.ts`:
- Around line 118-125: The command handler signature for program.command("init")
is reversed: the action currently declares (options: { iac?: boolean },
projectName?: string) so positional projectName becomes the options object;
update the action parameters to (projectName?: string, options: { iac?: boolean
}) and pass them into runInitCommand as runInitCommand({ projectName, ...options
}) so program.command("init") and runInitCommand receive the correct values.
- Line 32: PUBLIC_COMMANDS currently lists only literal command names so
checkAuthHook (which inspects process.argv[2]) treats flag invocations like
"--version" or "-v" as protected; update the PUBLIC_COMMANDS constant to include
the flag forms so flag-based calls bypass auth (add at minimum "--version",
"--help", "-V", "-h" and consider including "-v" and "-help" variants per CLI
conventions) so checkAuthHook recognizes them as public.
- Around line 543-549: The branch CLI handler is using async (options) but
Commander passes the positional argument separately; update the .action callback
signature to accept the positional project-root first and options second (e.g.,
async (projectRootArg, options) => { ... }) and compute projectRoot as
options.projectRoot || projectRootArg || process.cwd() before calling
runBranchCommand([], projectRoot); reference the branch command builder,
.argument, .option, .action and runBranchCommand to locate the change.
- Around line 474-482: The action handler for the fn.command("deploy") command
has the wrong parameter order: commander will call action(name, projectRoot,
options), but the code defines async (name, options) and reads projectRoot from
options; update the action signature to async (name: string, projectRoot?:
string, options?: { syncEnv?: boolean; projectRoot?: string }) (or equivalent
positional parameter names) and then compute const projectRootResolved =
projectRoot ?? process.cwd(); finally call runFunctionCommand(["deploy", name,
options?.syncEnv ? "--sync-env" : ""], projectRootResolved) so
runFunctionCommand and the positional arguments (.argument("<name>", ...) and
.argument("[project-root]", ...)) are used correctly.

In `@packages/client/src/iac/provider.tsx`:
- Around line 34-40: The onclose handler currently creates a new WebSocket via
wsRef.current = new WebSocket(wsUrl) without reattaching handlers and without
tracking the timeout, causing orphaned, non-functional sockets and leaks; change
the reconnection logic to use a reconnection helper that (1) sets up a new
WebSocket with the same onopen/onmessage/onclose handlers (reuse the existing
handler functions or a setupWebSocket(ws) function) and (2) implements
exponential backoff for retries while storing the timeout ID in a ref so it can
be cleared on unmount/unsubscribe; also ensure unsubscribe() (or the
RealtimeClient cleanup path) closes the active wsRef.current and clears any
pending reconnect timeout so no stray connections are created when the last
subscriber unsubscribes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f4577f0f-6492-47d4-8b2d-6d6be16ef327

📥 Commits

Reviewing files that changed from the base of the PR and between 3ebfdf2 and 44d3d9e.

📒 Files selected for processing (15)
  • .coderabbit.yaml
  • .github/workflows/ci.yml
  • Dockerfile
  • Dockerfile.project
  • apps/dashboard/Dockerfile
  • apps/dashboard/src/vite-env.d.ts
  • docker-compose.dev.yml
  • docker-compose.production.yml
  • docker-compose.self-hosted.yml
  • docker-compose.yml
  • packages/cli/src/build.ts
  • packages/cli/src/constants.ts
  • packages/cli/src/index.ts
  • packages/client/src/iac/provider.tsx
  • packages/server/Dockerfile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant