Skip to content

CLI: Detect free port when running dev during initiate#33532

Merged
ndelangen merged 3 commits into
nextfrom
norbert/use-open-port-on-initiate
Jan 14, 2026
Merged

CLI: Detect free port when running dev during initiate#33532
ndelangen merged 3 commits into
nextfrom
norbert/use-open-port-on-initiate

Conversation

@ndelangen
Copy link
Copy Markdown
Member

@ndelangen ndelangen commented Jan 14, 2026

What I did

Discovered during QA: when runnign storybook initiate whilst another storybook is running on the default port, the user is asked if another port should be used.

This hinders the ease of onboarding, so it's better to detect if the port is open beforehand and set an alternative one (that's free) if that's the case.

Checklist for Contributors

Testing

The changes in this PR are covered in the following automated tests:

  • stories
  • unit tests
  • integration tests
  • end-to-end tests

Manual testing

Caution

This section is mandatory for all contributions. If you believe no manual test is necessary, please state so explicitly. Thanks!

Documentation

  • Add or update documentation reflecting your changes
  • If you are deprecating/removing a feature, make sure to update
    MIGRATION.MD

Checklist for Maintainers

  • When this PR is ready for testing, make sure to add ci:normal, ci:merged or ci:daily GH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found in code/lib/cli-storybook/src/sandbox-templates.ts

  • Make sure this PR contains one of the labels below:

    Available labels
    • bug: Internal changes that fixes incorrect behavior.
    • maintenance: User-facing maintenance tasks.
    • dependencies: Upgrading (sometimes downgrading) dependencies.
    • build: Internal-facing build tooling & test updates. Will not show up in release changelog.
    • cleanup: Minor cleanup style change. Will not show up in release changelog.
    • documentation: Documentation only changes. Will not show up in release changelog.
    • feature request: Introducing a new feature.
    • BREAKING CHANGE: Changes that break compatibility in some way with current major version.
    • other: Changes that don't fit in the above categories.

🦋 Canary release

This PR does not have a canary release associated. You can request a canary release of this pull request by mentioning the @storybookjs/core team here.

core team members can create a canary release here or locally with gh workflow run --repo storybookjs/storybook publish.yml --field pr=<PR_NUMBER>

Summary by CodeRabbit

Release Notes

  • New Features
    • Storybook dev server now automatically detects port availability and selects an alternative port if the default port (6006) is already in use.

✏️ Tip: You can customize this high-level summary in your review settings.

@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Jan 14, 2026

View your CI Pipeline Execution ↗ for commit 7781dea

Command Status Duration Result
nx run-many -t compile,check,knip,test,pretty-d... ✅ Succeeded 8m 20s View ↗

☁️ Nx Cloud last updated this comment at 2026-01-14 11:20:26 UTC

@storybook-app-bot
Copy link
Copy Markdown

storybook-app-bot Bot commented Jan 14, 2026

Package Benchmarks

Commit: 7781dea, ran on 14 January 2026 at 11:20:19 UTC

No significant changes detected, all good. 👏

Comment thread code/lib/create-storybook/src/initiate.ts Outdated
@ndelangen ndelangen marked this pull request as ready for review January 14, 2026 11:11
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 14, 2026

📝 Walkthrough

Walkthrough

A new getServerPort utility function is exported from the core-server module. The create-storybook initialization now uses this function to check if the default port 6006 is available; if occupied, it calculates an alternative port and passes it to the dev server startup command via a flag.

Changes

Cohort / File(s) Summary
Core server port utility export
code/core/src/core-server/index.ts
New public export of getServerPort function from ./utils/server-address module
Storybook dev server port configuration
code/lib/create-storybook/src/initiate.ts
Added port availability check using getServerPort(6006) before dev server launch. If default port is in use, computes alternative port and injects --port=<alternative> flag into startup arguments. Also changed executeCommand from non-awaited to awaited invocation.

Sequence Diagram

sequenceDiagram
    participant Init as create-storybook init
    participant PortUtil as getServerPort utility
    participant DevServer as Storybook dev server
    
    Init->>PortUtil: Check if port 6006 available
    PortUtil-->>Init: Port status (available/unavailable)
    alt Port 6006 unavailable
        Init->>Init: Compute alternative port
        Init->>DevServer: await executeCommand with --port=<alt>
    else Port 6006 available
        Init->>DevServer: await executeCommand with default port
    end
    DevServer-->>Init: Server started
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

✨ Finishing touches
  • 📝 Generate docstrings


📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4f4da7c and 7781dea.

📒 Files selected for processing (2)
  • code/core/src/core-server/index.ts
  • code/lib/create-storybook/src/initiate.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,ts,tsx,json,md,html,css,scss}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Format code using Prettier with yarn prettier --write <file>

Files:

  • code/lib/create-storybook/src/initiate.ts
  • code/core/src/core-server/index.ts
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Run ESLint checks using yarn lint:js:cmd <file> or the full command cross-env NODE_ENV=production eslint --cache --cache-location=../.cache/eslint --ext .js,.jsx,.json,.html,.ts,.tsx,.mjs --report-unused-disable-directives to fix linting errors before committing

Files:

  • code/lib/create-storybook/src/initiate.ts
  • code/core/src/core-server/index.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Enable TypeScript strict mode across all packages

Files:

  • code/lib/create-storybook/src/initiate.ts
  • code/core/src/core-server/index.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx,js,jsx}: Export functions from modules if they need to be tested
Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/lib/create-storybook/src/initiate.ts
  • code/core/src/core-server/index.ts
code/{core,lib,addons,builders,frameworks,presets}/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use logger from storybook/internal/node-logger for server-side logging in Node.js code

Files:

  • code/lib/create-storybook/src/initiate.ts
  • code/core/src/core-server/index.ts
🧠 Learnings (4)
📓 Common learnings
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-22T22:03:40.123Z
Learning: Applies to code/{core,lib,addons,builders,frameworks,presets}/**/*.{ts,tsx,js,jsx} : Use `logger` from `storybook/internal/node-logger` for server-side logging in Node.js code
📚 Learning: 2025-12-22T22:03:40.123Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-22T22:03:40.123Z
Learning: Applies to code/{core,lib,addons,builders,frameworks,presets}/**/*.{ts,tsx,js,jsx} : Use `logger` from `storybook/internal/node-logger` for server-side logging in Node.js code

Applied to files:

  • code/lib/create-storybook/src/initiate.ts
📚 Learning: 2025-11-05T09:38:47.712Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.

Applied to files:

  • code/lib/create-storybook/src/initiate.ts
📚 Learning: 2025-12-22T22:03:40.123Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-22T22:03:40.123Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Export functions from modules if they need to be tested

Applied to files:

  • code/core/src/core-server/index.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: normal
  • GitHub Check: Core Unit Tests, windows-latest
  • GitHub Check: nx
🔇 Additional comments (3)
code/lib/create-storybook/src/initiate.ts (2)

7-8: Import change looks fine; keep “node-logger” usage (no console)
You’re using logger from storybook/internal/node-logger in this Node path, which aligns with repo guidance.


210-221: Awaiting executeCommand is good; verify desired exit-code semantics
Awaiting the subprocess makes the parent initiate process behave more like “run dev now” (likely intended). One thing to confirm: the surrounding try/catch swallows failures, so the parent may exit 0 even if the child fails immediately. If you expect storybook initiate --dev to reflect child failure, propagate the error / exit code instead of swallowing.

code/core/src/core-server/index.ts (1)

33-34: The getServerPort export is already actively imported from the barrel in code/lib/create-storybook/src/initiate.ts, confirming this addition formalizes an existing expected API surface. No import-time side effects or circular dependencies detected. Implementation is clean, well-tested, and production-ready.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


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

Copy link
Copy Markdown
Contributor

@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.

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (1)
code/lib/create-storybook/src/initiate.ts (1)

179-209: Avoid adding a second --port when storybookCommand already specifies one
If storybookCommand ever contains --port …, --port=…, or -p …, this will append a conflicting --port=... and create ambiguous CLI behavior.

Proposed patch
-    const flags = [];
+    const flags: string[] = [];

     if (packageManager.type === 'npm') {
       flags.push('--silent');
     }

@@
     if (supportsOnboarding && shouldOnboard) {
       flags.push('--initial-path=/onboarding');
     }

-    // Check if default port 6006 is available
-    const defaultPort = 6006;
-    const availablePort = await getServerPort(defaultPort);
-    const useAlternativePort = availablePort !== defaultPort;
-
-    if (useAlternativePort) {
-      flags.push(`--port=${availablePort}`);
-    }
+    const commandParts = storybookCommand.split(' ');
+    const commandAlreadyHasPort =
+      commandParts.some((a) => a === '--port' || a === '-p' || a.startsWith('--port=')) ||
+      commandParts.some((a, i) => (a === '--port' || a === '-p') && Boolean(commandParts[i + 1]));
+
+    if (!commandAlreadyHasPort) {
+      // Check if default port 6006 is available
+      const defaultPort = 6006;
+      const availablePort = await getServerPort(defaultPort);
+      const useAlternativePort = availablePort !== defaultPort;
+
+      if (useAlternativePort) {
+        flags.push(`--port=${availablePort}`);
+      }
+    }

     flags.push('--quiet');

Also, given the PR description notes QA found this, please add at least explicit manual test steps in the PR description (or a small unit/integration test if there’s a suitable harness).

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4f4da7c and 7781dea.

📒 Files selected for processing (2)
  • code/core/src/core-server/index.ts
  • code/lib/create-storybook/src/initiate.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,ts,tsx,json,md,html,css,scss}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Format code using Prettier with yarn prettier --write <file>

Files:

  • code/lib/create-storybook/src/initiate.ts
  • code/core/src/core-server/index.ts
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Run ESLint checks using yarn lint:js:cmd <file> or the full command cross-env NODE_ENV=production eslint --cache --cache-location=../.cache/eslint --ext .js,.jsx,.json,.html,.ts,.tsx,.mjs --report-unused-disable-directives to fix linting errors before committing

Files:

  • code/lib/create-storybook/src/initiate.ts
  • code/core/src/core-server/index.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Enable TypeScript strict mode across all packages

Files:

  • code/lib/create-storybook/src/initiate.ts
  • code/core/src/core-server/index.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx,js,jsx}: Export functions from modules if they need to be tested
Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/lib/create-storybook/src/initiate.ts
  • code/core/src/core-server/index.ts
code/{core,lib,addons,builders,frameworks,presets}/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use logger from storybook/internal/node-logger for server-side logging in Node.js code

Files:

  • code/lib/create-storybook/src/initiate.ts
  • code/core/src/core-server/index.ts
🧠 Learnings (4)
📓 Common learnings
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-22T22:03:40.123Z
Learning: Applies to code/{core,lib,addons,builders,frameworks,presets}/**/*.{ts,tsx,js,jsx} : Use `logger` from `storybook/internal/node-logger` for server-side logging in Node.js code
📚 Learning: 2025-12-22T22:03:40.123Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-22T22:03:40.123Z
Learning: Applies to code/{core,lib,addons,builders,frameworks,presets}/**/*.{ts,tsx,js,jsx} : Use `logger` from `storybook/internal/node-logger` for server-side logging in Node.js code

Applied to files:

  • code/lib/create-storybook/src/initiate.ts
📚 Learning: 2025-11-05T09:38:47.712Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.

Applied to files:

  • code/lib/create-storybook/src/initiate.ts
📚 Learning: 2025-12-22T22:03:40.123Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-22T22:03:40.123Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Export functions from modules if they need to be tested

Applied to files:

  • code/core/src/core-server/index.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: normal
  • GitHub Check: Core Unit Tests, windows-latest
  • GitHub Check: nx
🔇 Additional comments (3)
code/lib/create-storybook/src/initiate.ts (2)

7-8: Import change looks fine; keep “node-logger” usage (no console)
You’re using logger from storybook/internal/node-logger in this Node path, which aligns with repo guidance.


210-221: Awaiting executeCommand is good; verify desired exit-code semantics
Awaiting the subprocess makes the parent initiate process behave more like “run dev now” (likely intended). One thing to confirm: the surrounding try/catch swallows failures, so the parent may exit 0 even if the child fails immediately. If you expect storybook initiate --dev to reflect child failure, propagate the error / exit code instead of swallowing.

code/core/src/core-server/index.ts (1)

33-34: The getServerPort export is already actively imported from the barrel in code/lib/create-storybook/src/initiate.ts, confirming this addition formalizes an existing expected API surface. No import-time side effects or circular dependencies detected. Implementation is clean, well-tested, and production-ready.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@ndelangen ndelangen merged commit a8906a2 into next Jan 14, 2026
69 checks passed
@ndelangen ndelangen deleted the norbert/use-open-port-on-initiate branch January 14, 2026 11:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants