Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions internal/documentation/docs/pages/Server.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,19 @@ Please be aware of the following risks when using the server:

## Live Status Banner

When started in an interactive terminal, `ui5 serve` renders a live status banner that shows the server URLs, the root project's name/type/version, the configured UI5 framework, and a single-line status indicator that cycles between three states as you work:
When started in an interactive terminal, `ui5 serve` renders a live status banner that shows the server URLs, the root project's name/type/version, the configured UI5 framework, and a single status line that reflects what the server is doing:

- **● ready** — the server is idle; no source changes are pending and no build is in flight.
- **○ stale · files changed, rebuild on next request** — one or more watched source files changed; the next request will trigger a rebuild.
- **◐ building — *N*/*M* projects · *current project* · *current task*** — a build cycle is running. The counter, project name, and task name update in place.
- **ready** — the server is idle. Once source files change, a `N projects stale` count appears here until the next request rebuilds them.
- **building** — a build cycle is running; the project counter, current project, and current task update in place.
- **validating cache** / **settling** — the server is checking dependency caches or waiting for a burst of file changes to settle before rebuilding.
- **error** — the last build failed. The error message is logged above the banner.

The status line stays pinned at the bottom of the terminal; warnings and errors scroll above it and remain in your terminal scrollback. While the banner is active, `info`-level log messages are suppressed — the status line already shows what they would report. The header repaints in place as more information becomes known (project graph resolved, server bound), so early frames may show dim placeholders for sections that have not been populated yet.
The status line stays pinned at the bottom of the terminal; log messages scroll above it and remain in your terminal scrollback. Log lines duplicating what the banner already shows are suppressed. The banner repaints in place as more information becomes known (project graph resolved, server bound), so early frames may show dim placeholders for sections that have not been populated yet.

The banner is automatically disabled and `ui5 serve` falls back to plain log output when:

- `stderr` is not a TTY (e.g. output is piped to a file or another process).
- The log level is set to `--silent`, `--perf`, `--verbose`, or `--silly` — these levels emit per-task chatter that the status line cannot represent.

In every other case (the default `info` level, `--loglevel warn`, and `--loglevel error`), the banner is active.
- The log level is set to `--silent` or `--silly`.

### Plain Output (Non-TTY)

Expand All @@ -49,7 +48,7 @@ Server started
URL: <url>
```

The remote-connections warning (when `--accept-remote-connections` is used) is written to `stderr`. Tools and scripts that capture the URL by parsing `ui5 serve` output should run the command in a non-TTY context (pipe, redirect, or CI environment), or pass `--verbose` to force plain output in an interactive terminal.
The remote-connections warning (when `--accept-remote-connections` is used) is written to `stderr`. Tools and scripts that capture the URL by parsing `ui5 serve` output should run the command in a non-TTY context (for example, with `stderr` redirected, or in CI), or set `UI5_CLI_NO_INTERACTIVE=1` to force plain output in an interactive terminal.

## Standard Middleware

Expand Down
24 changes: 24 additions & 0 deletions internal/documentation/docs/pages/Troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,30 @@ The combination of the `UI5_LOG_LVL` environment variable with the `--log-level`

:::

### Disabling Interactive `ui5 serve` Output

When `ui5 serve` runs in an interactive terminal, it can render a live status banner instead of plain log output.

#### Resolution

Set the `UI5_CLI_NO_INTERACTIVE` environment variable to any value to force plain output.

Unix:
```sh
UI5_CLI_NO_INTERACTIVE=1 ui5 serve
```

Windows:
```sh
set UI5_CLI_NO_INTERACTIVE=1 ui5 serve
```

Cross Environment via [cross-env](https://www.npmjs.com/package/cross-env):

```sh
cross-env UI5_CLI_NO_INTERACTIVE=1 ui5 serve
```

### Changing UI5 CLI's Data Directory

UI5 CLI's data directory is by default at `~/.ui5`. It's the place where the framework artifacts are stored.
Expand Down
2 changes: 1 addition & 1 deletion internal/documentation/docs/updates/migrate-v5.md
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ When `ui5 serve` is started in an interactive terminal, it now renders a live st
See [Live Status Banner](../pages/Server.md#live-status-banner) for details on what is shown and when the banner is active.

::: tip Parsing `ui5 serve` output
The banner is automatically disabled in non-TTY contexts (pipes, redirects, CI logs). The plain output — `Server started` followed by `URL: <url>` on `stdout` — is unchanged from previous versions, so scripts that parse the URL from a non-interactive `ui5 serve` continue to work. To force plain output inside an interactive terminal, pass `--verbose` or pipe the output (e.g. `ui5 serve | cat`).
The banner is automatically disabled in non-TTY contexts. The plain output — `Server started` followed by `URL: <url>` on `stdout` — is unchanged from previous versions, so scripts that parse the URL from a non-interactive `ui5 serve` continue to work. To force plain output inside an interactive terminal, set `UI5_CLI_NO_INTERACTIVE=1`.
:::

## Removal of Standard Server Middleware
Expand Down
9 changes: 5 additions & 4 deletions packages/cli/lib/cli/middlewares/logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import {setLogLevel, isLogLevelEnabled, getLogger, getLogLevel} from "@ui5/logge
import ConsoleWriter from "@ui5/logger/writers/Console";
import {getVersion, getVersionWithLocation} from "../version.js";

// Log levels that the interactive writer cannot represent (firehose verbose
// logging, or user-requested silence). Fall back to the plain Console writer
// in those cases.
const NON_INTERACTIVE_LEVELS = new Set(["perf", "verbose", "silly", "silent"]);
// Log levels at which the interactive banner is counterproductive, so we fall
// back to the plain Console writer:
// - silent: the user asked for minimal or no output, so we honor that intent
// - silly: high-volume tracing where a pinned status region hinders debugging
const NON_INTERACTIVE_LEVELS = new Set(["silly", "silent"]);
Comment thread
d3xter666 marked this conversation as resolved.

/**
* Logger middleware to enable logging capabilities
Expand Down
22 changes: 21 additions & 1 deletion packages/cli/test/lib/cli/middlewares/logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,12 +187,32 @@ test.serial("Interactive writer: skipped when stderr is not a TTY", async (t) =>
t.is(consoleInitStub.callCount, 1, "Plain ConsoleWriter.init called instead");
});

test.serial("Interactive writer: skipped for non-interactive log level (verbose)", async (t) => {
test.serial("Interactive writer: enabled for log level verbose", async (t) => {
const {logger, consoleInitStub, interactiveInitStub, getLogLevelStub} = t.context;
getLogLevelStub.returns("verbose");
await withInteractiveEnv({isTTY: true, noInteractive: undefined}, () =>
logger.initLogger({_: ["serve"]})
);
t.is(interactiveInitStub.callCount, 1, "InteractiveConsole.init called");
t.is(consoleInitStub.callCount, 0, "Plain ConsoleWriter.init not called");
});

test.serial("Interactive writer: enabled for log level perf", async (t) => {
const {logger, consoleInitStub, interactiveInitStub, getLogLevelStub} = t.context;
getLogLevelStub.returns("perf");
await withInteractiveEnv({isTTY: true, noInteractive: undefined}, () =>
logger.initLogger({_: ["serve"]})
);
t.is(interactiveInitStub.callCount, 1, "InteractiveConsole.init called");
t.is(consoleInitStub.callCount, 0, "Plain ConsoleWriter.init not called");
});

test.serial("Interactive writer: skipped for non-interactive log level (silly)", async (t) => {
const {logger, consoleInitStub, interactiveInitStub, getLogLevelStub} = t.context;
getLogLevelStub.returns("silly");
await withInteractiveEnv({isTTY: true, noInteractive: undefined}, () =>
logger.initLogger({_: ["serve"]})
);
t.is(interactiveInitStub.callCount, 0, "InteractiveConsole.init not called");
t.is(consoleInitStub.callCount, 1, "Plain ConsoleWriter.init called instead");
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import figures from "figures";
export const REMOTE_CONNECTIONS_WARNING_LINES = Object.freeze([
chalk.bold.yellow(
`${figures.warning} This server is accepting connections from all hosts on your network`),
chalk.dim.underline("Please Note:"),
chalk.dim.underline("Warning:"),
chalk.dim(`${figures.pointerSmall} `) +
chalk.dim.bold("This server is intended for development purposes only. Do not use it in production."),
chalk.dim(`${figures.pointerSmall} ` +
Expand Down
Loading