Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: deploy from Creator Hub #279

Merged
merged 22 commits into from
Nov 11, 2024
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
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"@vitejs/plugin-react": "^4.3.1",
"classnames": "^2.5.1",
"cross-env": "7.0.3",
"decentraland-ui2": "^0.6.0",
"decentraland-ui2": "^0.6.1",
"electron": "31.1.0",
"electron-builder": "24.13.3",
"eslint": "8.57.0",
Expand Down
61 changes: 36 additions & 25 deletions packages/main/src/modules/bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,17 +186,23 @@ export class StreamError extends ErrorBase<Error> {
}
}

export type StreamType = 'all' | 'stdout' | 'stderr';

export type Child = {
pkg: string;
bin: string;
args: string[];
cwd: string;
process: Electron.UtilityProcess;
on: (pattern: RegExp, handler: (data?: string) => void) => number;
once: (pattern: RegExp, handler: (data?: string) => void) => number;
on: (pattern: RegExp, handler: (data?: string) => void, streamType?: StreamType) => number;
once: (pattern: RegExp, handler: (data?: string) => void, streamType?: StreamType) => number;
off: (index: number) => void;
wait: () => Promise<Buffer>;
waitFor: (resolvePattern: RegExp, rejectPattern?: RegExp) => Promise<string>;
waitFor: (
resolvePattern: RegExp,
rejectPattern?: RegExp,
opts?: { resolve?: StreamType; reject?: StreamType },
) => Promise<string>;
kill: () => Promise<void>;
alive: () => boolean;
};
Expand All @@ -205,6 +211,7 @@ type Matcher = {
pattern: RegExp;
handler: (data: string) => void;
enabled: boolean;
streamType: StreamType;
};

type RunOptions = {
Expand Down Expand Up @@ -245,13 +252,13 @@ export function run(pkg: string, bin: string, options: RunOptions = {}): Child {

const stdout: Uint8Array[] = [];
forked.stdout!.on('data', (data: Buffer) => {
handleData(data, matchers);
handleData(data, matchers, 'stdout');
stdout.push(Uint8Array.from(data));
});

const stderr: Uint8Array[] = [];
forked.stderr!.on('data', (data: Buffer) => {
handleData(data, matchers);
handleData(data, matchers, 'stderr');
stderr.push(Uint8Array.from(data));
});

Expand Down Expand Up @@ -287,30 +294,27 @@ export function run(pkg: string, bin: string, options: RunOptions = {}): Child {
}
});

function handleStream(stream: NodeJS.ReadableStream) {
stream!.on('data', (data: Buffer) => handleData(data, matchers));
}

handleStream(forked.stdout!);
handleStream(forked.stderr!);

const child: Child = {
pkg,
bin,
args,
cwd,
process: forked,
on: (pattern, handler) => {
on: (pattern, handler, streamType = 'all') => {
if (alive) {
return matchers.push({ pattern, handler, enabled: true }) - 1;
return matchers.push({ pattern, handler, enabled: true, streamType }) - 1;
}
throw new Error('Process has been killed');
},
once: (pattern, handler) => {
const index = child.on(pattern, data => {
handler(data);
child.off(index);
});
once: (pattern, handler, streamType) => {
const index = child.on(
pattern,
data => {
handler(data);
child.off(index);
},
streamType,
);
return index;
},
off: index => {
Expand All @@ -319,11 +323,11 @@ export function run(pkg: string, bin: string, options: RunOptions = {}): Child {
}
},
wait: () => promise,
waitFor: (resolvePattern, rejectPattern) =>
waitFor: (resolvePattern, rejectPattern, opts) =>
new Promise((resolve, reject) => {
child.once(resolvePattern, data => resolve(data!));
child.once(resolvePattern, data => resolve(data!), opts?.resolve);
if (rejectPattern) {
child.once(rejectPattern, data => reject(new Error(data)));
child.once(rejectPattern, data => reject(new Error(data)), opts?.reject);
}
}),
kill: async () => {
Expand Down Expand Up @@ -382,14 +386,21 @@ export function run(pkg: string, bin: string, options: RunOptions = {}): Child {
return child;
}

async function handleData(buffer: Buffer, matchers: Matcher[]) {
async function handleData(buffer: Buffer, matchers: Matcher[], type: StreamType) {
const data = buffer.toString('utf8');
log.info(`[UtilityProcess] ${data}`); // pipe data to console
for (const { pattern, handler, enabled } of matchers) {
for (const { pattern, handler, enabled, streamType } of matchers) {
if (!enabled) continue;
if (streamType !== 'all' && streamType !== type) continue;
pattern.lastIndex = 0; // reset regexp
if (pattern.test(data)) {
handler(data);
// remove control characters from data
const text = data.replace(
// eslint-disable-next-line no-control-regex
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
'',
);
handler(text);
}
}
}
Expand Down
7 changes: 6 additions & 1 deletion packages/main/src/modules/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export async function deploy({ path, target, targetContent }: DeployOptions) {
deployServer = run('@dcl/sdk-commands', 'sdk-commands', {
args: [
'deploy',
'--no-browser',
'--port',
port.toString(),
...(target ? ['--target', target] : []),
Expand All @@ -54,7 +55,11 @@ export async function deploy({ path, target, targetContent }: DeployOptions) {
});

// App ready at
await deployServer.waitFor(/app ready at/i);
await deployServer.waitFor(/listening/i, /error:/i, { reject: 'stderr' });

deployServer.waitFor(/close the terminal/gi).then(() => deployServer?.kill());

deployServer.wait().catch(); // handle rejection of main promise to avoid warnings in console

return port;
}
6 changes: 5 additions & 1 deletion packages/main/src/modules/electron.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import path from 'path';
import fs from 'fs/promises';
import { app, BrowserWindow, dialog, type OpenDialogOptions, shell } from 'electron';
import { app, BrowserWindow, clipboard, dialog, type OpenDialogOptions, shell } from 'electron';

export function getHome() {
return app.getPath('home');
Expand Down Expand Up @@ -35,3 +35,7 @@ export async function openExternal(url: string) {
export async function getAppVersion() {
return app.getVersion();
}

export async function copyToClipboard(text: string) {
clipboard.writeText(text);
}
14 changes: 11 additions & 3 deletions packages/main/src/modules/handle.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ipcMain } from 'electron';
import log from 'electron-log';
import type { Ipc } from '/shared/types/ipc';
import type { Ipc, IpcError, IpcResult } from '/shared/types/ipc';

// wrapper for ipcMain.handle with types
export async function handle<T extends keyof Ipc>(
Expand All @@ -14,11 +14,19 @@ export async function handle<T extends keyof Ipc>(
.map((arg, idx) => `args[${idx}]=${JSON.stringify(arg)}`)
.join(' ')}`.trim(),
);
const result = await handler(event, ...(args as Parameters<Ipc[T]>));
const value = await handler(event, ...(args as Parameters<Ipc[T]>));
const result: IpcResult<typeof value> = {
success: true,
value,
};
return result;
} catch (error: any) {
log.error(`[IPC] channel=${channel} error=${error.message}`);
throw error;
const result: IpcError = {
success: false,
error: error.message,
};
return result;
}
});
}
1 change: 1 addition & 0 deletions packages/main/src/modules/ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export function initIpc() {
);
handle('electron.showOpenDialog', (_event, opts) => electron.showOpenDialog(opts));
handle('electron.openExternal', (_event, url) => electron.openExternal(url));
handle('electron.copyToClipboard', (_event, text) => electron.copyToClipboard(text));

// inspector
handle('inspector.start', () => inspector.start());
Expand Down
4 changes: 0 additions & 4 deletions packages/preload/src/modules/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@ import type { DeployOptions } from '/shared/types/ipc';

import { invoke } from './invoke';

export async function getWorkspaceConfigPath(_path: string) {
return invoke('electron.getWorkspaceConfigPath', _path);
}

export async function getVersion() {
return invoke('electron.getAppVersion');
}
Expand Down
11 changes: 9 additions & 2 deletions packages/preload/src/modules/invoke.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { ipcRenderer } from 'electron';
import type { Ipc } from '/shared/types/ipc';
import type { Ipc, IpcError, IpcResult } from '/shared/types/ipc';

// wrapper for ipcRenderer.invoke with types
export async function invoke<T extends keyof Ipc>(
channel: T,
...args: Parameters<Ipc[T]>
): Promise<ReturnType<Ipc[T]>> {
return ipcRenderer.invoke(channel, ...args);
const result = await (ipcRenderer.invoke(channel, ...args) as Promise<
IpcResult<ReturnType<Ipc[T]>> | IpcError
>);
if (result.success) {
return result.value;
} else {
throw new Error(result.error);
}
}
4 changes: 4 additions & 0 deletions packages/preload/src/modules/misc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ export async function openExternal(url: string) {
if (!isUrl(url)) throw new Error('Invalid URL provided');
await invoke('electron.openExternal', url);
}

export async function copyToClipboard(text: string) {
await invoke('electron.copyToClipboard', text);
}
15 changes: 13 additions & 2 deletions packages/preload/src/modules/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { type DependencyState, SortBy, type Project } from '/shared/types/projec
import { PACKAGES_LIST } from '/shared/types/pkg';
import { DEFAULT_DEPENDENCY_UPDATE_STRATEGY } from '/shared/types/settings';
import type { Template, Workspace } from '/shared/types/workspace';
import { FileSystemStorage } from '/shared/types/storage';

import { getConfig, setConfig } from './config';
import { exists, writeFile as deepWriteFile } from './fs';
Expand All @@ -18,7 +19,6 @@ import { getDefaultScenesPath, getScenesPath } from './settings';
import { getScene } from './scene';

import { DEFAULT_THUMBNAIL, NEW_SCENE_NAME, EMPTY_SCENE_TEMPLATE_REPO } from './constants';
import { getWorkspaceConfigPath } from './editor';
import { getProjectId } from './analytics';

/**
Expand Down Expand Up @@ -54,7 +54,7 @@ export async function hasNodeModules(_path: string) {
}

export async function getOldProjectThumbnailPath(_path: string) {
const workspaceConfigPath = await getWorkspaceConfigPath(_path);
const workspaceConfigPath = await getConfigPath(_path);
return path.join(workspaceConfigPath, 'images', 'project-thumbnail.png');
}

Expand Down Expand Up @@ -374,3 +374,14 @@ export async function openFolder(_path: string) {
const error = await shell.openPath(_path);
if (error) throw new Error(error);
}

export async function getConfigPath(_path: string) {
return invoke('electron.getWorkspaceConfigPath', _path);
}

export async function getProjectInfo(_path: string) {
const configPath = await getConfigPath(_path);
const projectInfoPath = path.join(configPath, 'project.json');
const projectInfo = await FileSystemStorage.getOrCreate(projectInfoPath);
return projectInfo;
}
12 changes: 12 additions & 0 deletions packages/renderer/src/components/Button/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,15 @@
.Button.MuiButton-colorInfo:hover {
background-color: var(--light-gray);
}

.Button.MuiButton-outlined {
color: var(--dcl) !important;
background-color: transparent;
border-color: var(--dcl);
}

.Button.MuiButton-outlined:hover {
color: var(--dcl) !important;
background-color: transparent;
border-color: var(--dcl-dark);
}
23 changes: 1 addition & 22 deletions packages/renderer/src/components/EditorPage/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import PublicIcon from '@mui/icons-material/Public';

import { useSelector } from '#store';

import { DEPLOY_URLS } from '/shared/types/deploy';
import { isWorkspaceError } from '/shared/types/workspace';

import { t } from '/@/modules/store/translation/utils';
Expand All @@ -17,7 +16,7 @@ import { useEditor } from '/@/hooks/useEditor';

import EditorPng from '/assets/images/editor.png';

import { PublishProject, type StepValue } from '../Modals/PublishProject';
import { PublishProject } from '../Modals/PublishProject';
import { Button } from '../Button';
import { Header } from '../Header';
import { Row } from '../Row';
Expand All @@ -35,7 +34,6 @@ export function EditorPage() {
saveAndGetThumbnail,
inspectorPort,
openPreview,
publishScene,
openCode,
updateScene,
loadingPreview,
Expand Down Expand Up @@ -89,24 +87,6 @@ export function EditorPage() {
setOpen(undefined);
}, []);

const handleSubmitModal = useCallback(
({ target, value }: StepValue) => {
switch (target) {
case 'worlds':
return publishScene({
targetContent: import.meta.env.VITE_WORLDS_SERVER || DEPLOY_URLS.WORLDS,
});
case 'test':
return publishScene({ target: import.meta.env.VITE_TEST_SERVER || DEPLOY_URLS.TEST });
case 'custom':
return publishScene({ target: value });
default:
return publishScene();
}
},
[isReady],
);

// inspector url
const htmlUrl = `http://localhost:${import.meta.env.VITE_INSPECTOR_PORT || inspectorPort}`;
let binIndexJsUrl = `${htmlUrl}/bin/index.js`;
Expand Down Expand Up @@ -213,7 +193,6 @@ export function EditorPage() {
open={open === 'publish'}
project={project}
onClose={handleCloseModal}
onSubmit={handleSubmitModal}
/>
)}
</>
Expand Down
2 changes: 1 addition & 1 deletion packages/renderer/src/components/Loader/styles.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.Loader {
width: 100%;
height: 100vh;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
Expand Down
Loading
Loading