Skip to content

Commit b82248c

Browse files
bmiddhaCopilot
andcommitted
feat(rush-serve): log dashboard URL
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 24508b3 commit b82248c

8 files changed

Lines changed: 48 additions & 10 deletions

File tree

common/changes/@microsoft/rush/rush-serve-dashboard-select-all_2026-08-07-15-26-00.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"changes": [
33
{
44
"packageName": "@microsoft/rush",
5-
"comment": "Allow Ctrl+A and Command+A to select text in Rush serve dashboard fields.",
5+
"comment": "Allow Ctrl+A and Command+A to select text in Rush serve dashboard fields, and log a clickable dashboard URL when the server starts.",
66
"type": "patch"
77
}
88
],

common/config/rush-plugins/rush-serve-plugin.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"phasedCommands": ["start"],
44
"portParameterLongName": "--port",
55
"buildStatusWebSocketPath": "/ws",
6+
"dashboardServePath": "/dashboard/dashboard.html",
67
"logServePath": "/logs",
78
"globalRouting": [
89
{

rush-plugins/rush-serve-plugin/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ This package includes the reference `@rushstack/rush-serve-dashboard` web app, w
2929
"$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush-serve-plugin-options.schema.json",
3030
"phasedCommands": ["start"],
3131
"buildStatusWebSocketPath": "/ws",
32+
"dashboardServePath": "/dashboard/dashboard.html",
3233
"globalRouting": [
3334
{
3435
"workspaceRelativeFolder": "rush-plugins/rush-serve-plugin/lib-esm/dashboard",
@@ -39,7 +40,8 @@ This package includes the reference `@rushstack/rush-serve-dashboard` web app, w
3940
}
4041
```
4142

42-
Then open `https://localhost:<port>/dashboard/dashboard.html`.
43+
After the server starts, the plugin logs a clickable dashboard URL such as
44+
`https://localhost:<port>/dashboard/dashboard.html`.
4345

4446
### Dashboard
4547

rush-plugins/rush-serve-plugin/src/RushServePlugin.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ export interface IRushServePluginOptions {
3131
*/
3232
buildStatusWebSocketPath?: string;
3333

34+
/**
35+
* The URL path for the Rush serve dashboard. If specified, the plugin logs the full dashboard URL
36+
* after the server starts.
37+
*/
38+
dashboardServePath?: string;
39+
3440
/**
3541
* The name of a parameter that Rush is configured to use to pass a port number to underlying operations.
3642
* If specified, the plugin will ensure the value is synchronized with the port used for its server.
@@ -59,20 +65,23 @@ export class RushServePlugin implements IRushPlugin {
5965
private readonly _globalRoutingRules: IGlobalRoutingRuleJson[];
6066
private readonly _logServePath: string | undefined;
6167
private readonly _buildStatusWebSocketPath: string | undefined;
68+
private readonly _dashboardServePath: string | undefined;
6269

6370
public constructor(options: IRushServePluginOptions) {
6471
const {
6572
phasedCommands,
6673
portParameterLongName,
6774
globalRouting = [],
6875
logServePath,
69-
buildStatusWebSocketPath
76+
buildStatusWebSocketPath,
77+
dashboardServePath
7078
} = options;
7179
this._phasedCommands = new Set(phasedCommands);
7280
this._portParameterLongName = portParameterLongName;
7381
this._globalRoutingRules = globalRouting;
7482
this._logServePath = logServePath;
7583
this._buildStatusWebSocketPath = buildStatusWebSocketPath;
84+
this._dashboardServePath = dashboardServePath;
7685
}
7786

7887
public apply(rushSession: RushSession, rushConfiguration: RushConfiguration): void {
@@ -100,7 +109,8 @@ export class RushServePlugin implements IRushPlugin {
100109
portParameterLongName: this._portParameterLongName,
101110
logServePath: this._logServePath,
102111
globalRoutingRules,
103-
buildStatusWebSocketPath: this._buildStatusWebSocketPath
112+
buildStatusWebSocketPath: this._buildStatusWebSocketPath,
113+
dashboardServePath: this._dashboardServePath
104114
});
105115
};
106116

rush-plugins/rush-serve-plugin/src/phasedCommandHandler.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,16 @@ import {
3131
type WebSocketServerUpgrader
3232
} from './tryEnableBuildStatusWebSocketServer';
3333

34+
export function getServerUrls(
35+
hostNames: readonly string[],
36+
port: number,
37+
servePath: string
38+
): string[] {
39+
return hostNames.map((hostName) => `https://${hostName}:${port}${servePath}`);
40+
}
41+
3442
export async function phasedCommandHandler(options: IPhasedCommandHandlerOptions): Promise<void> {
35-
const { rushSession, command, portParameterLongName, globalRoutingRules } = options;
43+
const { rushSession, command, portParameterLongName, globalRoutingRules, dashboardServePath } = options;
3644

3745
const logger: ILogger = rushSession.getLogger(PLUGIN_NAME);
3846

@@ -44,10 +52,15 @@ export async function phasedCommandHandler(options: IPhasedCommandHandlerOptions
4452
function logHost(): void {
4553
if (activePort !== undefined) {
4654
logger.terminal.writeLine(
47-
`Content is being served from:\n ${activeHostNames
48-
.map((hostName) => `https://${hostName}:${activePort}/`)
49-
.join('\n ')}`
55+
`Content is being served from:\n ${getServerUrls(activeHostNames, activePort, '/').join('\n ')}`
5056
);
57+
if (dashboardServePath) {
58+
logger.terminal.writeLine(
59+
`Rush serve dashboard:\n ${getServerUrls(activeHostNames, activePort, dashboardServePath).join(
60+
'\n '
61+
)}`
62+
);
63+
}
5164
}
5265
}
5366

rush-plugins/rush-serve-plugin/src/schemas/rush-serve-plugin-options.schema.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@
3333
"pattern": "^/(?:[a-zA-Z0-9_$-]+(?:/[a-zA-Z0-9_$-]+)*)?$"
3434
},
3535

36+
"dashboardServePath": {
37+
"type": "string",
38+
"description": "The URL path for the Rush serve dashboard. If specified, the plugin logs the full dashboard URL after the server starts.",
39+
"pattern": "^/(?:[a-zA-Z0-9_.$-]+(?:/[a-zA-Z0-9_.$-]+)*)?$"
40+
},
41+
3642
"logServePath": {
3743
"type": "string",
3844
"description": "The URL path at which to host Rush log files. If not specified, log files will not be served.",
Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
22
// See LICENSE in the project root for license information.
33

4+
import { getServerUrls } from '../phasedCommandHandler';
5+
46
describe('Server', () => {
5-
it('Is not implemented yet.', () => {
6-
// Do nothing.
7+
it('formats clickable serve URLs', () => {
8+
expect(getServerUrls(['localhost', 'rushstack.test'], 8091, '/dashboard/dashboard.html')).toEqual([
9+
'https://localhost:8091/dashboard/dashboard.html',
10+
'https://rushstack.test:8091/dashboard/dashboard.html'
11+
]);
712
});
813
});

rush-plugins/rush-serve-plugin/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export interface IPhasedCommandHandlerOptions {
1111
logServePath: string | undefined;
1212
globalRoutingRules: IRoutingRule[];
1313
buildStatusWebSocketPath: string | undefined;
14+
dashboardServePath: string | undefined;
1415
}
1516
export interface IRushProjectServeJson {
1617
routing: IRoutingRuleJson[];

0 commit comments

Comments
 (0)