Skip to content

Commit a0c5f28

Browse files
[cueweb/docs] Add "About CueWeb" dialog and version sourcing from VERSION.in (#2442)
## Related Issues ## Summarize your change. [cueweb] Add "About CueWeb" dialog and version sourcing from VERSION.in About dialog (Help -> About CueWeb): - [x] New about-dialog.tsx modal showing version + build SHA, active Cuebot facility, masked REST gateway URL, Apache-2.0 license link, and OpenCue/ASWF credits, plus a "Copy diagnostics" button (copies the fields as JSON). - [x] Opened via a CustomEvent from a new Help-group command in the menu registry; mounted once at the layout level. - [x] Build SHA comes from NEXT_PUBLIC_GIT_SHA (next.config env whitelist, Dockerfile ARG/ENV, docker-compose build arg, .env.example); CI injects the short SHA. Version sourcing: - [x] next.config.js resolves NEXT_PUBLIC_APP_VERSION from cueweb/OVERRIDE_CUEWEB_VERSION.in: the value "VERSION.in" (default) tracks the repo-root VERSION.in (OpenCue's shared version, 1.25); any other value pins CueWeb's version explicitly. A NEXT_PUBLIC_APP_VERSION build-arg still wins; package.json is the last fallback. - [x] The Docker build reads the repo-root VERSION.in via a `project_root` named build context (docker-compose additional_contexts + COPY --from=project_root), keeping the cueweb build context small. [cueweb] Document the "About CueWeb" dialog Document the Help -> About CueWeb dialog and the version/SHA build inputs it shows, and add the screenshots. - [x] other-guides + user-guide (user-facing): add About CueWeb to the Help menu list and describe the dialog (version + build SHA, active Cuebot facility, masked REST gateway URL, Apache-2.0 license, credits, Copy diagnostics), with the Help-menu and About-dialog screenshots. - [x] reference: add the About CueWeb entry to the Help dropdown; add a NEXT_PUBLIC_GIT_SHA env row and update NEXT_PUBLIC_APP_VERSION to reflect resolution from OVERRIDE_CUEWEB_VERSION.in / VERSION.in; rewrite the status-bar Version resolution order. - [x] developer-guide: add about-dialog.tsx to the component tree, note the About command in useMenuRegistry, and add an AboutDialog entry (open event, masked gateway, copy diagnostics, version/SHA resolution incl. the project_root named build context). - [x] deploying-cueweb: document the OVERRIDE_CUEWEB_VERSION.in / VERSION.in version sourcing and the NEXT_PUBLIC_GIT_SHA build arg. ## LLM usage disclosure Parts of this solution's implementation were developed with assistance from Claude Opus.
1 parent 00e9925 commit a0c5f28

22 files changed

Lines changed: 411 additions & 29 deletions

.github/workflows/testing-pipeline.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,9 @@ jobs:
411411
- name: Build CueWeb Docker image
412412
working-directory: cueweb
413413
run: |
414-
docker buildx build . -f Dockerfile -t opencue/cueweb:test --load
414+
docker buildx build . -f Dockerfile -t opencue/cueweb:test --load \
415+
--build-context project_root=.. \
416+
--build-arg NEXT_PUBLIC_GIT_SHA=$(git rev-parse --short HEAD)
415417
echo "CueWeb Docker image built successfully"
416418
417419
build_rest_gateway:

cueweb/.env.example

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
NEXT_PUBLIC_OPENCUE_ENDPOINT=http://your-rest-gateway-url.com
22

3+
# Short Git SHA shown in the "About CueWeb" dialog (Help -> About CueWeb).
4+
# Build-time only; CI injects it, e.g.
5+
# docker build --build-arg NEXT_PUBLIC_GIT_SHA=$(git rev-parse --short HEAD) ...
6+
# Defaults to empty (the dialog shows "unknown").
7+
# NEXT_PUBLIC_GIT_SHA=
8+
39
# Cuebot Facility switching (CueGUI "Cuebot Facility" menu parity).
410
# NEXT_PUBLIC_CUEBOT_FACILITIES lists the facilities shown in the header menu
511
# (comma-separated). Defaults to "local,dev,cloud,external" when unset.
@@ -14,6 +20,7 @@ NEXT_PUBLIC_OPENCUE_ENDPOINT=http://your-rest-gateway-url.com
1420
# CUEBOT_DEV_JWT_SECRET=dev-gateway-jwt-secret
1521
# CUEBOT_CLOUD_REST_GATEWAY_URL=https://cloud-rest-gateway.example.com
1622
# CUEBOT_CLOUD_JWT_SECRET=cloud-gateway-jwt-secret
23+
1724
# Optional allow-list for the Stuck Frames "Last Line" reader
1825
# (/api/stuck-frames/lastline), as a colon-separated list of absolute path
1926
# prefixes. When set, only .rqlog files under one of these roots are read.

cueweb/Dockerfile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ RUN npm ci \
2828
--fetch-timeout=600000
2929

3030
COPY *.json /opt/cueweb/
31+
COPY OVERRIDE_CUEWEB_VERSION.in /opt/cueweb/
32+
# Repo-root VERSION.in (OpenCue's shared version) read by next.config.js when
33+
# OVERRIDE_CUEWEB_VERSION.in is set to the "VERSION.in" sentinel. It lives
34+
# outside this build context, so it is pulled from the `project_root` named
35+
# build context (defined in docker-compose.yml; for a standalone build pass
36+
# `--build-context project_root=<repo-root>`).
37+
COPY --from=project_root VERSION.in /opt/VERSION.in
3138
COPY next.config.js /opt/cueweb/
3239
COPY postcss.config.js /opt/cueweb/
3340
COPY tailwind.config.js /opt/cueweb/
@@ -113,5 +120,10 @@ ENV NEXT_PUBLIC_AUTH_PROVIDER=${NEXT_PUBLIC_AUTH_PROVIDER}
113120
ARG NEXT_PUBLIC_APP_VERSION=
114121
ENV NEXT_PUBLIC_APP_VERSION=${NEXT_PUBLIC_APP_VERSION}
115122

123+
# Build SHA shown in the About CueWeb dialog. CI injects the short Git SHA via
124+
# --build-arg NEXT_PUBLIC_GIT_SHA=$(git rev-parse --short HEAD). Empty -> "unknown".
125+
ARG NEXT_PUBLIC_GIT_SHA=
126+
ENV NEXT_PUBLIC_GIT_SHA=${NEXT_PUBLIC_GIT_SHA}
127+
116128
RUN npm run build
117129
CMD ["npm", "run", "start"]

cueweb/OVERRIDE_CUEWEB_VERSION.in

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# OVERRIDE_CUEWEB_VERSION.in
2+
#
3+
# Controls the version string CueWeb displays (bottom status bar + the
4+
# "About CueWeb" dialog). The value is resolved at build time in
5+
# next.config.js and exposed to the client as NEXT_PUBLIC_APP_VERSION.
6+
#
7+
# How to use:
8+
# - Leave the value line below as exactly "VERSION.in" (the default) to track
9+
# the OpenCue version from the repo-root VERSION.in -- the shared source of
10+
# truth also read by cuebot, cuegui and the docs. This keeps CueWeb's
11+
# version in lock-step with the rest of OpenCue.
12+
# - Replace it with an explicit version (e.g. "0.1.0") to pin/override
13+
# CueWeb's version independently of VERSION.in.
14+
#
15+
# Precedence: a NEXT_PUBLIC_APP_VERSION env / --build-arg always wins over this
16+
# file (CI injects the generated OpenCue version or a Git SHA). If neither this
17+
# file nor VERSION.in resolves, CueWeb falls back to package.json's "version".
18+
#
19+
# Lines starting with "#" and blank lines are ignored; the first remaining line
20+
# is the value.
21+
VERSION.in

cueweb/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ CueWeb replicates the core functionality of CueGUI (Cuetopia and Cuecommander) i
4343
- **Cuetopia** → Monitor Jobs.
4444
- **CueCommander** → Allocations, Limits, Monitor Cue, Monitor Hosts, Redirect, Services, Shows, Stuck Frame, Subscription Graphs, Subscriptions. Unimplemented routes 404 gracefully until the corresponding pages land.
4545
- **Other****Attributes** (toggles the docked Attributes panel, see below), **Show Shortcuts** (opens the same overlay as pressing `?`), **Notify on Shortcut** (toggles the per-shortcut toast).
46-
- **Help** → search box that searches *every* menu command in CueWeb (CueGUI parity) plus links to the Online User Guide, Make a Suggestion, and Report a Bug (URLs overridable via `NEXT_PUBLIC_DOCS_URL` / `NEXT_PUBLIC_SUGGESTIONS_URL` / `NEXT_PUBLIC_BUGS_URL`).
46+
- **Help** → search box that searches *every* menu command in CueWeb (CueGUI parity), an **About CueWeb** dialog showing the build version, Build SHA, active Cuebot facility, masked REST gateway URL, Apache 2.0 license link, OpenCue/ASWF credits, and a **Copy diagnostics** button, plus links to the Online User Guide, Make a Suggestion, and Report a Bug (URLs overridable via `NEXT_PUBLIC_DOCS_URL` / `NEXT_PUBLIC_SUGGESTIONS_URL` / `NEXT_PUBLIC_BUGS_URL`).
4747
- Theme toggle on the right.
4848
- An always-visible **Sign out** button on the right. With an active session, `signOut()` clears it and redirects to `/login`; without a session it just navigates to `/login`. The `/login` page itself handles both auth configurations — empty `NEXT_PUBLIC_AUTH_PROVIDER` renders the **CueWeb Home** button, while a populated value renders the provider buttons.
4949
- When the user is signed in, the right-side cluster also shows the session's name or email next to the Sign out button.
@@ -68,7 +68,7 @@ CueWeb replicates the core functionality of CueGUI (Cuetopia and Cuecommander) i
6868
- 24-pixel-tall bar fixed to the bottom of the viewport with three metrics, each with a tooltip:
6969
1. **Gateway**: dot + `Online`/`Offline` + round-trip latency in milliseconds. Polled every 10 seconds via `/api/health` (a JWT-signed reachability probe against `/show.ShowInterface/GetActiveShows` with a 5-second timeout). When the gateway is unreachable, the bar's surface turns red so the failure is visible at a glance.
7070
2. **Last refresh**: live relative timestamp ("just now", "12s ago", "3m ago", ...). Updated whenever the jobs table fires a `cueweb:jobs-refreshed` CustomEvent (every 5 seconds while the table is mounted). Re-renders once per second so the relative time stays accurate without waiting for the next event.
71-
3. **Version**: `v<NEXT_PUBLIC_APP_VERSION>`. Resolved at build time - falls back to the `version` field in `package.json` when the env var is unset, and can be overridden in CI by passing `--build-arg NEXT_PUBLIC_APP_VERSION=<sha>` to `docker build`.
71+
3. **Version**: `v<NEXT_PUBLIC_APP_VERSION>` (also shown in **Help → About CueWeb** alongside the Build SHA). Resolved once at build time, first hit wins: (1) the `NEXT_PUBLIC_APP_VERSION` env / build-arg (CI injects the OpenCue version or a SHA); (2) `OVERRIDE_CUEWEB_VERSION.in` - its default value `VERSION.in` tracks the repo-root `VERSION.in` (OpenCue's shared version, also read by Cuebot / CueGUI), while any other value is used verbatim as a CueWeb-specific override; (3) the `version` field in `package.json` as a last-resort fallback. The **Build SHA** comes from the `NEXT_PUBLIC_GIT_SHA` build-arg and shows `unknown` when unset.
7272
- Hidden on `/login*`; matches the chrome's translucent surface so it integrates with both light and dark themes.
7373
- **Mobile-friendly UI:**
7474
- Every authenticated route works on phone-sized viewports. The Jobs page stacks its filter / toolbar / table vertically on small screens instead of forcing a wide layout, and each data table can be swiped horizontally to reach off-screen columns.

cueweb/app/layout.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import "./globals.css";
1919
import { ThemeProvider } from "@/components/ui/theme-provider";
2020
import { JobSubscriptionPoller } from "@/app/providers/job-subscription-poller";
2121
import { AppSessionProvider } from "@/app/providers/session-provider";
22+
import { AboutDialog } from "@/components/ui/about-dialog";
2223
import { AppHeader } from "@/components/ui/app-header";
2324
import { AppSidebar } from "@/components/ui/app-sidebar";
2425
import { AttributesPanel } from "@/components/ui/attributes-panel";
@@ -52,6 +53,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
5253
</div>
5354
<AttributesPanel />
5455
<StatusBar />
56+
<AboutDialog />
5557
<KeyboardShortcuts />
5658
<MobileNavSheet />
5759
<ToastHost />

cueweb/app/utils/use_menu_registry.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { NAV_MENUS } from "@/app/utils/menus";
2424
import { useAttributesPanel } from "@/app/utils/use_attributes_panel";
2525
import { useCuebotFacility } from "@/app/utils/use_cuebot_facility";
2626
import { useDisableJobInteraction } from "@/app/utils/use_disable_job_interaction";
27+
import { CUEWEB_OPEN_ABOUT_EVENT } from "@/components/ui/about-dialog";
2728

2829
/**
2930
* A flat, searchable list of every menu command in CueWeb - used by the
@@ -107,6 +108,19 @@ export function useMenuRegistry(): MenuCommand[] {
107108
});
108109
}
109110

111+
// Help -> About CueWeb (opens the About dialog, not an external link).
112+
cmds.push({
113+
id: "help.about",
114+
group: "Help",
115+
label: "About CueWeb",
116+
hint: "dialog",
117+
run: () => {
118+
if (typeof window !== "undefined") {
119+
window.dispatchEvent(new CustomEvent(CUEWEB_OPEN_ABOUT_EVENT));
120+
}
121+
},
122+
});
123+
110124
return cmds;
111125
}, [router, toggleJobInteraction, facilities, setFacility, toggleAttributes]);
112126
}
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
/*
2+
* Copyright Contributors to the OpenCue Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
"use client";
18+
19+
import * as React from "react";
20+
import { Copy, ExternalLink } from "lucide-react";
21+
22+
import {
23+
Dialog,
24+
DialogContent,
25+
DialogDescription,
26+
DialogFooter,
27+
DialogHeader,
28+
DialogTitle,
29+
} from "@/components/ui/dialog";
30+
import { Button } from "@/components/ui/button";
31+
import { useCuebotFacility } from "@/app/utils/use_cuebot_facility";
32+
import { toastSuccess, toastWarning } from "@/app/utils/notify_utils";
33+
34+
/**
35+
* "About CueWeb" dialog - CueGUI parity (Help -> About in
36+
* `cuegui/cuegui/MainWindow.py`). Opened from the Help menu via the
37+
* `CUEWEB_OPEN_ABOUT_EVENT` CustomEvent and mounted once at the layout level.
38+
*
39+
* Shows the build version + Git SHA (build-time env), the active Cuebot
40+
* facility, the REST gateway URL (masked so it can be pasted into a bug report
41+
* without leaking the full internal host), the license, and credits. A
42+
* "Copy diagnostics" button copies all fields as JSON.
43+
*/
44+
45+
export const CUEWEB_OPEN_ABOUT_EVENT = "cueweb:open-about";
46+
47+
const LICENSE_URL =
48+
"https://github.com/AcademySoftwareFoundation/OpenCue/blob/master/LICENSE";
49+
const OPENCUE_URL = "https://www.opencue.io/";
50+
51+
/**
52+
* Mask a gateway URL for display: keep the scheme + port and the first/last
53+
* couple of host characters, replacing the middle with asterisks. Drops any
54+
* path / query / userinfo. Falls back to a coarse mask for non-URL strings.
55+
*/
56+
function maskGatewayUrl(raw: string | undefined): string {
57+
const value = (raw ?? "").trim();
58+
if (!value) return "(not configured)";
59+
try {
60+
const u = new URL(value);
61+
const host = u.hostname;
62+
const masked =
63+
host.length <= 4
64+
? "*".repeat(host.length || 4)
65+
: `${host.slice(0, 2)}${"*".repeat(Math.max(3, host.length - 4))}${host.slice(-2)}`;
66+
const port = u.port ? `:${u.port}` : "";
67+
return `${u.protocol}//${masked}${port}`;
68+
} catch {
69+
return value.length <= 6 ? "******" : `${value.slice(0, 3)}***${value.slice(-3)}`;
70+
}
71+
}
72+
73+
function Field({ label, value, mono = false }: { label: string; value: React.ReactNode; mono?: boolean }) {
74+
return (
75+
<div className="grid grid-cols-[8rem_1fr] items-start gap-2 py-1 text-sm">
76+
<dt className="text-muted-foreground">{label}</dt>
77+
<dd className={mono ? "break-all font-mono text-xs" : "break-words"}>{value}</dd>
78+
</div>
79+
);
80+
}
81+
82+
export function AboutDialog() {
83+
const [open, setOpen] = React.useState(false);
84+
const { facility } = useCuebotFacility();
85+
86+
React.useEffect(() => {
87+
const handler = () => setOpen(true);
88+
window.addEventListener(CUEWEB_OPEN_ABOUT_EVENT, handler);
89+
return () => window.removeEventListener(CUEWEB_OPEN_ABOUT_EVENT, handler);
90+
}, []);
91+
92+
const version =
93+
process.env.NEXT_PUBLIC_APP_VERSION && process.env.NEXT_PUBLIC_APP_VERSION.length > 0
94+
? process.env.NEXT_PUBLIC_APP_VERSION
95+
: "dev";
96+
const gitSha =
97+
process.env.NEXT_PUBLIC_GIT_SHA && process.env.NEXT_PUBLIC_GIT_SHA.length > 0
98+
? process.env.NEXT_PUBLIC_GIT_SHA
99+
: "unknown";
100+
const maskedGateway = maskGatewayUrl(process.env.NEXT_PUBLIC_OPENCUE_ENDPOINT);
101+
102+
async function copyDiagnostics() {
103+
const payload = {
104+
product: "CueWeb",
105+
version,
106+
gitSha,
107+
facility,
108+
restGateway: maskedGateway,
109+
userAgent: typeof navigator !== "undefined" ? navigator.userAgent : "",
110+
generatedAt: new Date().toISOString(),
111+
};
112+
const text = JSON.stringify(payload, null, 2);
113+
try {
114+
await navigator.clipboard.writeText(text);
115+
toastSuccess("Diagnostics copied to clipboard.");
116+
} catch {
117+
toastWarning("Could not copy to clipboard.");
118+
}
119+
}
120+
121+
return (
122+
<Dialog open={open} onOpenChange={setOpen}>
123+
<DialogContent className="sm:max-w-lg">
124+
<DialogHeader>
125+
<DialogTitle>About CueWeb</DialogTitle>
126+
<DialogDescription>
127+
The web-based interface for OpenCue.
128+
</DialogDescription>
129+
</DialogHeader>
130+
131+
<dl className="divide-y divide-border">
132+
<Field label="Version" value={version} mono />
133+
<Field label="Build SHA" value={gitSha} mono />
134+
<Field label="Cuebot facility" value={<span className="uppercase">{facility}</span>} />
135+
<Field label="REST gateway" value={maskedGateway} mono />
136+
<Field
137+
label="License"
138+
value={
139+
<a
140+
href={LICENSE_URL}
141+
target="_blank"
142+
rel="noopener noreferrer"
143+
className="inline-flex items-center gap-1 text-primary hover:underline"
144+
>
145+
Apache License 2.0
146+
<ExternalLink className="h-3 w-3" aria-hidden="true" />
147+
</a>
148+
}
149+
/>
150+
<Field
151+
label="Credits"
152+
value={
153+
<span>
154+
OpenCue is an open source project hosted by the{" "}
155+
<a
156+
href="https://www.aswf.io/"
157+
target="_blank"
158+
rel="noopener noreferrer"
159+
className="text-primary hover:underline"
160+
>
161+
Academy Software Foundation
162+
</a>
163+
. Learn more at{" "}
164+
<a
165+
href={OPENCUE_URL}
166+
target="_blank"
167+
rel="noopener noreferrer"
168+
className="text-primary hover:underline"
169+
>
170+
opencue.io
171+
</a>
172+
.
173+
</span>
174+
}
175+
/>
176+
</dl>
177+
178+
<DialogFooter className="gap-2 sm:justify-between">
179+
<Button type="button" variant="outline" size="sm" onClick={copyDiagnostics}>
180+
<Copy className="mr-2 h-4 w-4" aria-hidden="true" />
181+
Copy diagnostics
182+
</Button>
183+
<Button type="button" size="sm" onClick={() => setOpen(false)}>
184+
Close
185+
</Button>
186+
</DialogFooter>
187+
</DialogContent>
188+
</Dialog>
189+
);
190+
}

0 commit comments

Comments
 (0)