Skip to content

Commit

Permalink
add deploy error
Browse files Browse the repository at this point in the history
  • Loading branch information
nicoecheza committed Dec 13, 2024
1 parent 9d788fc commit 5e2551f
Show file tree
Hide file tree
Showing 12 changed files with 180 additions and 80 deletions.
8 changes: 0 additions & 8 deletions packages/renderer/src/components/Button/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,5 @@
}

.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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import {
checkDeploymentStatus,
fetchDeploymentStatus,
deriveOverallStatus,
cleanPendingsFromDeploymentStatus,
isDeployFinishing,
} from './utils';
import {
type File,
Expand Down Expand Up @@ -196,9 +198,12 @@ export function Deploy(props: Props) {
}
}, [jumpInUrl]);

const handleDeployFinish = useCallback((status: Status, error?: string) => {
setDeployStatus(status);
setError(error ?? null);
const handleDeploySuccess = useCallback(() => {
setDeployStatus('complete');
}, []);

const handleDeployRetry = useCallback(() => {
props.onBack && props.onBack();
}, []);

return (
Expand Down Expand Up @@ -240,6 +245,7 @@ export function Deploy(props: Props) {
</label>
<span className="buttons">
<Button
color="secondary"
variant="outlined"
size="medium"
onClick={handleBack}
Expand Down Expand Up @@ -321,8 +327,9 @@ export function Deploy(props: Props) {
<Deploying
info={info}
url={jumpInUrl}
onFinish={handleDeployFinish}
onSuccess={handleDeploySuccess}
onClick={handleJumpIn}
onRetry={handleDeployRetry}
/>
)}
{deployStatus === 'complete' && (
Expand Down Expand Up @@ -393,11 +400,12 @@ function Idle({ files, error, onClick }: IdleProps) {
type DeployingProps = {
info: Info;
url: string | null;
onFinish: (status: Status, error?: string) => void;
onSuccess: () => void;
onClick: () => void;
onRetry: () => void;
};

function Deploying({ info, url, onFinish, onClick }: DeployingProps) {
function Deploying({ info, url, onSuccess, onClick, onRetry }: DeployingProps) {
const { wallet } = useAuth();
const [deployState, setDeployState] = useState<DeploymentStatus>(getInitialDeploymentStatus());

Expand All @@ -406,41 +414,37 @@ function Deploying({ info, url, onFinish, onClick }: DeployingProps) {
const identity = localStorageGetIdentity(wallet);
if (!identity) throw new Error(`No identity found for wallet ${wallet}`);
return fetchDeploymentStatus(info.rootCID, identity);
}, [info]);
}, [wallet, info]);

const onReportIssue = useCallback(() => {
void misc.openExternal('https://decentraland.canny.io');
}, []);

useEffect(
() => {
let isCancelled = false;

const handleUpdate = (status: DeploymentStatus) => {
// if catalyst deploy fails, we abort the deployment completely
if (status.catalyst === 'failed') {
isCancelled = true;
return onFinish(
'failed',
t('modal.publish_project.deploy.deploying.errors.catalyst_deploy'),
);
if (!isCancelled) {
if (deriveOverallStatus(status) === 'failed') {
isCancelled = true;
setDeployState(cleanPendingsFromDeploymentStatus(status));
} else {
setDeployState(status);
}
}
if (!isCancelled) setDeployState(status);
};

const handleSuccess = (status: DeploymentStatus) => {
if (!isCancelled) onFinish(deriveOverallStatus(status));
const handleSuccess = () => {
if (!isCancelled) onSuccess();
};

const handleFailure = (error: any) => {
if (!isCancelled) {
// if we know the error, we can translate it
// info: if we know the error, we can translate it
if (isDeploymentError(error, 'MAX_RETRIES')) {
return onFinish(
'failed',
t('modal.publish_project.deploy.deploying.errors.max_retries', {
error: error.message,
}),
);
setDeployState(cleanPendingsFromDeploymentStatus(error.status));
}

onFinish('failed', error.message);
}
};

Expand All @@ -465,48 +469,79 @@ function Deploying({ info, url, onFinish, onClick }: DeployingProps) {
[] /* no deps, want this to run ONLY on mount */,
);

const text = useMemo(() => t('modal.publish_project.deploy.deploying.step.loading'), []);
const is = useCallback((status: Status, status2: Status) => status === status2, []);
const getStepDescription = useCallback((status: Status) => {
switch (status) {
case 'pending':
return t('modal.publish_project.deploy.deploying.step.loading');
case 'failed':
return t('modal.publish_project.deploy.deploying.step.failed');
default:
return undefined;
}
}, []);

const steps: Step[] = useMemo(() => {
const { catalyst, assetBundle, lods } = deployState;

return [
{
bulletText: '1',
name: t('modal.publish_project.deploy.deploying.step.catalyst'),
text: is('pending', catalyst) ? text : undefined,
description: getStepDescription(catalyst),
state: catalyst,
},
{
bulletText: '2',
name: t('modal.publish_project.deploy.deploying.step.asset_bundle'),
text: is('pending', assetBundle) ? text : undefined,
description: getStepDescription(assetBundle),
state: assetBundle,
},
{
bulletText: '3',
name: t('modal.publish_project.deploy.deploying.step.lods'),
text: is('pending', lods) ? text : undefined,
description: getStepDescription(lods),
state: lods,
},
];
}, [deployState]);
}, [deployState, getStepDescription]);

const lastStep = steps[steps.length - 1];
const isFinishing = useMemo(() => isDeployFinishing(deployState), [deployState]);
const overallStatus = useMemo(() => deriveOverallStatus(deployState), [deployState]);
const title = useMemo(() => {
if (overallStatus === 'failed') return t('modal.publish_project.deploy.deploying.failed');
if (isFinishing) return t('modal.publish_project.deploy.deploying.finishing');
return t('modal.publish_project.deploy.deploying.publish');
}, [overallStatus, isFinishing]);

return (
<div className="Deploying">
<div className="title">
<Loader />
<Typography variant="h5">
{lastStep.state === 'pending'
? t('modal.publish_project.deploy.deploying.finishing')
: t('modal.publish_project.deploy.deploying.publish')}
</Typography>
<div className="header">
<div className="title">
{overallStatus === 'failed' ? <div className="Warning" /> : <Loader />}
<Typography variant="h5">{title}</Typography>
</div>
{overallStatus === 'failed' && (
<span>{t('modal.publish_project.deploy.deploying.try_again')}</span>
)}
</div>
<ConnectedSteps steps={steps} />
{lastStep.state === 'pending' ? (
{overallStatus === 'failed' ? (
<div className="actions">
<Button
size="large"
variant="outlined"
color="secondary"
onClick={onReportIssue}
>
{t('modal.publish_project.deploy.deploying.actions.report_issue')}
</Button>
<Button
size="large"
onClick={onRetry}
>
{t('modal.publish_project.deploy.deploying.actions.retry')}
</Button>
</div>
) : isFinishing ? (
<>
<div className="jump">
<JumpUrl
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,19 +229,31 @@
padding: 10px;
}

.Deploy .scene .Deploying .title {
.Deploy .scene .Deploying .header {
color: var(--dcl-silver);
display: flex;
align-items: center;
justify-content: flex-start;
text-transform: uppercase;
margin-bottom: 20px;
flex-direction: column;
}

.Deploy .scene .Deploying .header .title {
display: flex;
align-items: center;
text-transform: uppercase;
}

.Deploy .scene .Deploying .title .Loader {
.Deploy .scene .Deploying .header .Loader {
width: inherit;
margin-right: 12px;
}

.Deploy .scene .Deploying .header .Warning {
background-size: contain;
width: 40px;
margin-right: 12px;
}

.Deploy .scene .Deploying .ConnectedSteps {
margin-bottom: 30px;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,17 @@ export type AssetBundleRegistryResponse = {

export type Error = 'MAX_RETRIES' | 'FETCH';

export class DeploymentError extends ErrorBase<Error> {}
export class DeploymentError extends ErrorBase<Error> {
constructor(
public name: Error,
public message: string = '',
public status: DeploymentStatus,
public cause?: any,
) {
super(name, message, cause);
this.status = status;
}
}

export const isDeploymentError = (error: unknown, type: Error): error is DeploymentError =>
error instanceof DeploymentError && error.name === type;
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,26 @@ export function validateStatus(status: string): Status {
* @returns An overall `Status`.
*/
export function deriveOverallStatus(statuses: Record<string, string>): Status {
const _statuses: Set<Status> = new Set(Object.values(statuses) as Status[]);
if (_statuses.has('pending')) return 'pending';
if (_statuses.has('complete')) return 'complete';
return 'failed';
const uniqueStatuses = new Set(Object.values(statuses) as Status[]);
return STATUS_VALUES.find(status => uniqueStatuses.has(status)) ?? 'idle';
}

/**
* Cleans up a `DeploymentStatus` object by resetting any 'pending' statuses to 'idle'.
*
* This function ensures that any deployment step stuck in a 'pending' state is treated
* as 'idle' to indicate that it hasn't started or needs to be retried.
*
* @param status - The `DeploymentStatus` object containing the current statuses of deployment steps.
* @returns A new `DeploymentStatus` object where all 'pending' statuses are replaced with 'idle'.
*/
export function cleanPendingsFromDeploymentStatus(status: DeploymentStatus): DeploymentStatus {
return Object.fromEntries(
Object.entries(status).map(([step, currentStatus]) => [
step,
currentStatus === 'pending' ? 'idle' : currentStatus,
]),
) as DeploymentStatus;
}

/**
Expand All @@ -96,8 +112,6 @@ export async function fetchDeploymentStatus(

const json = (await response.json()) as AssetBundleRegistryResponse;

console.log('json: ', json);

return {
catalyst: validateStatus(json.catalyst),
assetBundle: deriveOverallStatus(json.assetBundles),
Expand Down Expand Up @@ -169,8 +183,27 @@ export async function checkDeploymentStatus(
const maxRetriesError = new DeploymentError(
'MAX_RETRIES',
'Max retries reached. Deployment failed.',
currentStatus,
error,
);
console.error(maxRetriesError);
throw maxRetriesError;
}

/**
* Checks if the deployment is nearing completion based on a given percentage threshold.
*
* This function evaluates the `DeploymentStatus` object to determine whether the proportion
* of steps with a 'complete' status meets or exceeds the specified threshold (default: 60%).
*
* @param status - The `DeploymentStatus` object containing the current statuses of deployment steps.
* @param percentage - The completion threshold as a decimal (e.g., `0.6` for 60%). Defaults to 0.6.
* @returns `true` if the proportion of completed steps is greater than or equal to the threshold; otherwise, `false`.
*/
export function isDeployFinishing(status: DeploymentStatus, percentage: number = 0.6): boolean {
const statuses = Object.values(status);
const total = statuses.length;
if (total === 0) return false;
const completedCount = statuses.filter(value => value === 'complete').length;
return completedCount / total >= percentage;
}
11 changes: 0 additions & 11 deletions packages/renderer/src/components/Navbar/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,6 @@
border-bottom: 2px solid white;
}

.Navbar .feedback {
border-radius: 6px;
padding: 8px;
text-transform: uppercase;
border: 1px solid var(--text);
font-size: 12px;
font-weight: 600;
text-align: center;
cursor: pointer;
}

.Navbar .actions {
display: flex;
align-items: center;
Expand Down
4 changes: 2 additions & 2 deletions packages/renderer/src/components/Step/component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { Step } from './types';

import './styles.css';

export function Step({ bulletText, name, text, state = 'idle' }: Step) {
export function Step({ bulletText, name, description, state = 'idle' }: Step) {
const bullet = useMemo(() => {
if (state === 'complete') return <CheckIcon />;
if (state === 'failed') return <CloseIcon />;
Expand All @@ -19,7 +19,7 @@ export function Step({ bulletText, name, text, state = 'idle' }: Step) {
<div className="bullet">{bullet}</div>
<div className="body">
<h4>{name}</h4>
<span>{text}</span>
<span>{description}</span>
</div>
</div>
);
Expand Down
Loading

0 comments on commit 5e2551f

Please sign in to comment.