Skip to content

Commit 8c3ce77

Browse files
committed
address feedback
1 parent c91cc49 commit 8c3ce77

File tree

7 files changed

+96
-72
lines changed

7 files changed

+96
-72
lines changed

jest.config.cjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ module.exports = {
5959
'/node_modules/',
6060
'/src/cli.ts',
6161
'/src/command-line-arguments.ts',
62-
'/src/interactive-ui.ts',
62+
'/src/ui.ts',
6363
'/src/dirname.ts',
6464
],
6565

src/main.test.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@ import { buildMockProject } from '../tests/unit/helpers.js';
33
import { main } from './main.js';
44
import * as initialParametersModule from './initial-parameters.js';
55
import * as monorepoWorkflowOperations from './monorepo-workflow-operations.js';
6-
import * as interactiveUi from './interactive-ui.js';
6+
import * as ui from './ui.js';
77

88
jest.mock('./initial-parameters');
99
jest.mock('./monorepo-workflow-operations');
10-
jest.mock('./interactive-ui');
10+
jest.mock('./ui');
1111
jest.mock('./dirname', () => ({
1212
getCurrentDirectoryPath: jest.fn().mockReturnValue('/path/to/somewhere'),
1313
}));
1414

1515
describe('main', () => {
16-
it('executes the monorepo workflow if the project is a monorepo', async () => {
16+
it('executes the CLI monorepo workflow if the project is a monorepo and interactive is false', async () => {
1717
const project = buildMockProject({ isMonorepo: true });
1818
const stdout = fs.createWriteStream('/dev/null');
1919
const stderr = fs.createWriteStream('/dev/null');
@@ -50,7 +50,7 @@ describe('main', () => {
5050
});
5151
});
5252

53-
it('executes the interactive UI workflow if the project is a monorepo', async () => {
53+
it('executes the interactive UI monorepo workflow if the project is a monorepo and interactive is true', async () => {
5454
const project = buildMockProject({ isMonorepo: true });
5555
const stdout = fs.createWriteStream('/dev/null');
5656
const stderr = fs.createWriteStream('/dev/null');
@@ -65,9 +65,7 @@ describe('main', () => {
6565
interactive: true,
6666
port: 3000,
6767
});
68-
const startInteractiveUISpy = jest
69-
.spyOn(interactiveUi, 'startInteractiveUI')
70-
.mockResolvedValue();
68+
const startUISpy = jest.spyOn(ui, 'startUI').mockResolvedValue();
7169

7270
await main({
7371
argv: [],
@@ -76,7 +74,7 @@ describe('main', () => {
7674
stderr,
7775
});
7876

79-
expect(startInteractiveUISpy).toHaveBeenCalledWith({
77+
expect(startUISpy).toHaveBeenCalledWith({
8078
project,
8179
releaseType: 'backport',
8280
defaultBranch: 'main',

src/main.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { WriteStream } from 'fs';
22
import { determineInitialParameters } from './initial-parameters.js';
33
import { followMonorepoWorkflow } from './monorepo-workflow-operations.js';
4-
import { startInteractiveUI } from './interactive-ui.js';
4+
import { startUI } from './ui.js';
55

66
/**
77
* The main function for this tool. Designed to not access `process.argv`,
@@ -42,8 +42,8 @@ export async function main({
4242
);
4343

4444
if (interactive) {
45-
stdout.write(`Starting interactive UI on port ${port}...\n`);
46-
await startInteractiveUI({
45+
stdout.write(`Starting UI on port ${port}...\n`);
46+
await startUI({
4747
project,
4848
releaseType,
4949
defaultBranch,

src/interactive-ui.ts renamed to src/ui.ts

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import { getCurrentDirectoryPath } from './dirname.js';
2828

2929
const UI_BUILD_DIR = join(getCurrentDirectoryPath(), 'ui');
3030

31-
type InteractiveUIOptions = {
31+
type UIOptions = {
3232
project: Project;
3333
releaseType: 'ordinary' | 'backport';
3434
defaultBranch: string;
@@ -38,24 +38,24 @@ type InteractiveUIOptions = {
3838
};
3939

4040
/**
41-
* Starts the interactive UI for the release process.
41+
* Starts the UI for the release process.
4242
*
43-
* @param options - The options for the interactive UI.
43+
* @param options - The options for the UI.
4444
* @param options.project - The project object.
4545
* @param options.releaseType - The type of release.
4646
* @param options.defaultBranch - The default branch name.
4747
* @param options.port - The port number for the server.
4848
* @param options.stdout - The stdout stream.
4949
* @param options.stderr - The stderr stream.
5050
*/
51-
export async function startInteractiveUI({
51+
export async function startUI({
5252
project,
5353
releaseType,
5454
defaultBranch,
5555
port,
5656
stdout,
5757
stderr,
58-
}: InteractiveUIOptions): Promise<void> {
58+
}: UIOptions): Promise<void> {
5959
const { version: newReleaseVersion, firstRun } = await createReleaseBranch({
6060
project,
6161
releaseType,
@@ -69,6 +69,59 @@ export async function startInteractiveUI({
6969
);
7070
}
7171

72+
const app = createApp({
73+
project,
74+
defaultBranch,
75+
stderr,
76+
version: newReleaseVersion,
77+
closeServer: () => {
78+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
79+
server.close();
80+
stdout.write('Release process completed. Server shutdown.\n');
81+
},
82+
});
83+
84+
const server = app.listen(port, async () => {
85+
const url = `http://localhost:${port}`;
86+
stdout.write(`UI server running at ${url}\n`);
87+
});
88+
89+
return new Promise((resolve, reject) => {
90+
server.on('error', (error) => {
91+
stderr.write(`Failed to start server: ${error}\n`);
92+
reject(error);
93+
});
94+
95+
server.on('close', () => {
96+
resolve();
97+
});
98+
});
99+
}
100+
101+
/**
102+
* Creates an Express application for the UI server.
103+
*
104+
* @param options - The options for creating the app.
105+
* @param options.project - The project object.
106+
* @param options.defaultBranch - The default branch name.
107+
* @param options.stderr - The stderr stream.
108+
* @param options.version - The release version.
109+
* @param options.closeServer - The function to close the server.
110+
* @returns The Express application.
111+
*/
112+
function createApp({
113+
project,
114+
defaultBranch,
115+
stderr,
116+
version,
117+
closeServer,
118+
}: {
119+
project: Project;
120+
defaultBranch: string;
121+
stderr: Pick<WriteStream, 'write'>;
122+
version: string;
123+
closeServer: () => void;
124+
}): express.Application {
72125
const app = express();
73126

74127
app.use(express.static(UI_BUILD_DIR));
@@ -223,22 +276,20 @@ export async function startInteractiveUI({
223276
const releasePlan = await planRelease({
224277
project,
225278
releaseSpecificationPackages,
226-
newReleaseVersion,
279+
newReleaseVersion: version,
227280
});
228281
await executeReleasePlan(project, releasePlan, stderr);
229282
await fixConstraints(project.directoryPath);
230283
await updateYarnLockfile(project.directoryPath);
231284
await deduplicateDependencies(project.directoryPath);
232285
await commitAllChanges(
233286
project.directoryPath,
234-
`Update Release ${newReleaseVersion}`,
287+
`Update Release ${version}`,
235288
);
236289

237290
res.json({ status: 'success' });
238-
// eslint-disable-next-line @typescript-eslint/no-use-before-define
239-
server.close(() => {
240-
stdout.write('Release process completed. Server shutdown.\n');
241-
});
291+
292+
closeServer();
242293
} catch (error) {
243294
stderr.write(`Release error: ${error}\n`);
244295
res.status(400).send('Invalid request');
@@ -250,14 +301,5 @@ export async function startInteractiveUI({
250301
res.sendFile(join(UI_BUILD_DIR, 'index.html'));
251302
});
252303

253-
const server = app.listen(port, () => {
254-
stdout.write(`Interactive UI server running at http://localhost:${port}\n`);
255-
});
256-
257-
return new Promise((_, reject) => {
258-
server.on('error', (error) => {
259-
stderr.write(`Failed to start server: ${error}\n`);
260-
reject(error);
261-
});
262-
});
304+
return app;
263305
}

src/ui/App.tsx

Lines changed: 20 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { PackageItem } from './PackageItem.js';
66
import { Package, ReleaseType } from './types.js';
77

88
type SubmitButtonProps = {
9-
isSubmitting: boolean;
109
selections: Record<string, string>;
1110
packageDependencyErrors: Record<
1211
string,
@@ -16,13 +15,11 @@ type SubmitButtonProps = {
1615
};
1716

1817
function SubmitButton({
19-
isSubmitting,
2018
selections,
2119
packageDependencyErrors,
2220
onSubmit,
2321
}: SubmitButtonProps) {
2422
const isDisabled =
25-
isSubmitting ||
2623
Object.keys(selections).length === 0 ||
2724
Object.keys(packageDependencyErrors).length > 0 ||
2825
Object.values(selections).every((value) => value === 'intentionally-skip');
@@ -37,9 +34,7 @@ function SubmitButton({
3734
: 'bg-blue-500 hover:bg-blue-600'
3835
} text-white`}
3936
>
40-
{isSubmitting
41-
? 'Processing Release (This may take a few minutes)...'
42-
: 'Submit Release Selections'}
37+
Create Release Branch
4338
</button>
4439
);
4540
}
@@ -75,7 +70,7 @@ function App() {
7570
fetch('/api/packages')
7671
.then((res) => {
7772
if (!res.ok) {
78-
throw new Error('Failed to fetch packages');
73+
throw new Error(`Received ${res.status}`);
7974
}
8075
return res.json();
8176
})
@@ -172,19 +167,18 @@ function App() {
172167
packageName: string,
173168
value: ReleaseType | '',
174169
): void => {
175-
setSelections((prev) => {
176-
if (value === '') {
177-
const { [packageName]: _, ...rest } = prev;
178-
const { [packageName]: __, ...remainingErrors } =
179-
packageDependencyErrors;
180-
setPackageDependencyErrors(remainingErrors);
181-
return rest;
182-
}
183-
return {
184-
...prev,
170+
if (value === '') {
171+
const { [packageName]: _, ...rest } = selections;
172+
setSelections(rest);
173+
174+
const { [packageName]: __, ...remainingErrors } = packageDependencyErrors;
175+
setPackageDependencyErrors(remainingErrors);
176+
} else {
177+
setSelections({
178+
...selections,
185179
[packageName]: value,
186-
};
187-
});
180+
});
181+
}
188182
};
189183

190184
const handleSubmit = async (): Promise<void> => {
@@ -279,10 +273,10 @@ function App() {
279273
return (
280274
<div className="fixed inset-0 bg-white bg-opacity-90 flex items-center justify-center z-50">
281275
<div className="text-center p-8">
282-
<h2 className="text-2xl font-bold mb-4">Processing Release</h2>
276+
<h2 className="text-2xl font-bold mb-4">Create Release Branch</h2>
283277
<p className="mb-6">
284-
Please wait while we process your release. This may take a few
285-
minutes...
278+
Please wait while we create the release branch with your selections.
279+
This may take a few minutes...
286280
</p>
287281
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500 mx-auto"></div>
288282
</div>
@@ -294,24 +288,18 @@ function App() {
294288
return (
295289
<div className="fixed inset-0 bg-white flex items-center justify-center">
296290
<div className="text-center p-8">
297-
<h2 className="text-2xl font-bold mb-4">Release Successful!</h2>
291+
<h2 className="text-2xl font-bold mb-4">
292+
Release Branch Successfully Created!
293+
</h2>
298294
<p className="mb-6">You can now close this window.</p>
299-
<button
300-
onClick={() => window.close()}
301-
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
302-
>
303-
Close Window
304-
</button>
305295
</div>
306296
</div>
307297
);
308298
}
309299

310300
return (
311301
<div className="container mx-auto px-4 py-8">
312-
<h1 className="text-3xl font-bold mb-6">
313-
Create Release Branch Interactive UI
314-
</h1>
302+
<h1 className="text-3xl font-bold mb-6">Create New Core Release</h1>
315303

316304
{selectedPackages.size > 0 && (
317305
<div className="mb-4 p-4 bg-gray-100 rounded">
@@ -353,7 +341,6 @@ function App() {
353341

354342
{packages.length > 0 && (
355343
<SubmitButton
356-
isSubmitting={isSubmitting}
357344
selections={selections}
358345
packageDependencyErrors={packageDependencyErrors}
359346
onSubmit={handleSubmit}

src/ui/PackageItem.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export function PackageItem({
4747
id={`package-${pkg.name}`}
4848
className={`border p-4 rounded-lg ${
4949
selections[pkg.name] && selections[pkg.name] !== 'intentionally-skip'
50-
? 'border-gray-500'
50+
? 'border-2'
5151
: 'border-gray-200'
5252
} ${
5353
packageDependencyErrors[pkg.name] &&

src/ui/index.html

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<head>
44
<meta charset="UTF-8" />
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6-
<title>Create Release Branch Interactive UI</title>
6+
<title>Create New Core Release</title>
77
<link
88
rel="icon"
99
href="https://metamask.github.io/test-dapp/metamask-fox.svg"
@@ -20,10 +20,7 @@
2020
}
2121
}
2222
</script>
23-
<link
24-
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css"
25-
rel="stylesheet"
26-
/>
23+
<script src="https://unpkg.com/@tailwindcss/browser@4"></script>
2724
</head>
2825
<body>
2926
<div id="root"></div>

0 commit comments

Comments
 (0)