Skip to content

Commit

Permalink
Merge pull request #4102 from balena-io/aethernet/childwriter-standalone
Browse files Browse the repository at this point in the history
patch: compile child-writer.ts as a standalone cli
  • Loading branch information
flowzone-app[bot] authored Oct 16, 2023
2 parents 09e13e9 + f38bca2 commit 45bb29a
Show file tree
Hide file tree
Showing 31 changed files with 34,144 additions and 37,159 deletions.
2 changes: 1 addition & 1 deletion .github/actions/publish/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ inputs:
default: "[email protected]"
NODE_VERSION:
type: string
default: "16.x"
default: "18.x"
VERBOSE:
type: string
default: "true"
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pids

# Generated files
/generated
/binaries

# Dependency directory
# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
Expand Down
6 changes: 6 additions & 0 deletions entitlements.mac.plist
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,11 @@
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.get-task-allow</key>
<true/>
<key>com.apple.security.cs.disable-executable-page-protection</key>
<true/>
</dict>
</plist>
205 changes: 36 additions & 169 deletions lib/gui/app/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,30 @@

import * as electron from 'electron';
import * as remote from '@electron/remote';
import * as sdk from 'etcher-sdk';
import * as _ from 'lodash';
import { debounce, capitalize, Dictionary, values } from 'lodash';
import outdent from 'outdent';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { v4 as uuidV4 } from 'uuid';

import * as packageJSON from '../../../package.json';
import { DrivelistDrive, isSourceDrive } from '../../shared/drive-constraints';
import { DrivelistDrive } from '../../shared/drive-constraints';
import * as EXIT_CODES from '../../shared/exit-codes';
import * as messages from '../../shared/messages';
import * as availableDrives from './models/available-drives';
import * as flashState from './models/flash-state';
import { deselectImage, getImage } from './models/selection-state';
import * as settings from './models/settings';
import { Actions, observe, store } from './models/store';
import * as analytics from './modules/analytics';
import { scanner as driveScanner } from './modules/drive-scanner';
import { startApiAndSpawnChild } from './modules/api';
import * as exceptionReporter from './modules/exception-reporter';
import * as osDialog from './os/dialog';
import * as windowProgress from './os/window-progress';
import MainPage from './pages/main/MainPage';
import './css/main.css';
import * as i18next from 'i18next';
import { promises } from 'dns';
import { SourceMetadata } from '../../shared/typings/source-selector';

window.addEventListener(
'unhandledrejection',
Expand Down Expand Up @@ -89,7 +89,7 @@ analytics.logEvent('Application start', {
version: currentVersion,
});

const debouncedLog = _.debounce(console.log, 1000, { maxWait: 1000 });
const debouncedLog = debounce(console.log, 1000, { maxWait: 1000 });

function pluralize(word: string, quantity: number) {
return `${quantity} ${word}${quantity === 1 ? '' : 's'}`;
Expand All @@ -115,7 +115,7 @@ observe(() => {
// might cause some non-sense flashing state logs including
// `undefined` values.
debouncedLog(outdent({ newline: ' ' })`
${_.capitalize(currentFlashState.type)}
${capitalize(currentFlashState.type)}
${active},
${currentFlashState.percentage}%
at
Expand All @@ -128,173 +128,40 @@ observe(() => {
`);
});

/**
* @summary The radix used by USB ID numbers
*/
const USB_ID_RADIX = 16;

/**
* @summary The expected length of a USB ID number
*/
const USB_ID_LENGTH = 4;

/**
* @summary Convert a USB id (e.g. product/vendor) to a string
*
* @example
* console.log(usbIdToString(2652))
* > '0x0a5c'
*/
function usbIdToString(id: number): string {
return `0x${_.padStart(id.toString(USB_ID_RADIX), USB_ID_LENGTH, '0')}`;
}

/**
* @summary Product ID of BCM2708
*/
const USB_PRODUCT_ID_BCM2708_BOOT = 0x2763;

/**
* @summary Product ID of BCM2710
*/
const USB_PRODUCT_ID_BCM2710_BOOT = 0x2764;

/**
* @summary Compute module descriptions
*/
const COMPUTE_MODULE_DESCRIPTIONS: _.Dictionary<string> = {
[USB_PRODUCT_ID_BCM2708_BOOT]: 'Compute Module 1',
[USB_PRODUCT_ID_BCM2710_BOOT]: 'Compute Module 3',
};

async function driveIsAllowed(drive: {
devicePath: string;
device: string;
raw: string;
}) {
const driveBlacklist = (await settings.get('driveBlacklist')) || [];
return !(
driveBlacklist.includes(drive.devicePath) ||
driveBlacklist.includes(drive.device) ||
driveBlacklist.includes(drive.raw)
);
}

type Drive =
| sdk.sourceDestination.BlockDevice
| sdk.sourceDestination.UsbbootDrive
| sdk.sourceDestination.DriverlessDevice;

function prepareDrive(drive: Drive) {
if (drive instanceof sdk.sourceDestination.BlockDevice) {
// @ts-ignore (BlockDevice.drive is private)
return drive.drive;
} else if (drive instanceof sdk.sourceDestination.UsbbootDrive) {
// This is a workaround etcher expecting a device string and a size
// @ts-ignore
drive.device = drive.usbDevice.portId;
drive.size = null;
// @ts-ignore
drive.progress = 0;
drive.disabled = true;
drive.on('progress', (progress) => {
updateDriveProgress(drive, progress);
});
return drive;
} else if (drive instanceof sdk.sourceDestination.DriverlessDevice) {
const description =
COMPUTE_MODULE_DESCRIPTIONS[
drive.deviceDescriptor.idProduct.toString()
] || 'Compute Module';
return {
device: `${usbIdToString(
drive.deviceDescriptor.idVendor,
)}:${usbIdToString(drive.deviceDescriptor.idProduct)}`,
displayName: 'Missing drivers',
description,
mountpoints: [],
isReadOnly: false,
isSystem: false,
disabled: true,
icon: 'warning',
size: null,
link: 'https://www.raspberrypi.com/documentation/computers/compute-module.html#flashing-the-compute-module-emmc',
linkCTA: 'Install',
linkTitle: 'Install missing drivers',
linkMessage: outdent`
Would you like to download the necessary drivers from the Raspberry Pi Foundation?
This will open your browser.
Once opened, download and run the installer from the "Windows Installer" section to install the drivers
`,
};
}
}

function setDrives(drives: _.Dictionary<DrivelistDrive>) {
availableDrives.setDrives(_.values(drives));
}

function getDrives() {
return _.keyBy(availableDrives.getDrives(), 'device');
}

async function addDrive(drive: Drive) {
const preparedDrive = prepareDrive(drive);
if (!(await driveIsAllowed(preparedDrive))) {
return;
}
const drives = getDrives();
drives[preparedDrive.device] = preparedDrive;
setDrives(drives);
}

function removeDrive(drive: Drive) {
if (
drive instanceof sdk.sourceDestination.BlockDevice &&
// @ts-ignore BlockDevice.drive is private
isSourceDrive(drive.drive, getImage())
) {
// Deselect the image if it was on the drive that was removed.
// This will also deselect the image if the drive mountpoints change.
deselectImage();
}
const preparedDrive = prepareDrive(drive);
const drives = getDrives();
delete drives[preparedDrive.device];
setDrives(drives);
}

function updateDriveProgress(
drive: sdk.sourceDestination.UsbbootDrive,
progress: number,
) {
const drives = getDrives();
// @ts-ignore
const driveInMap = drives[drive.device];
if (driveInMap) {
// @ts-ignore
drives[drive.device] = { ...driveInMap, progress };
setDrives(drives);
function setDrives(drives: Dictionary<DrivelistDrive>) {
// prevent setting drives while flashing otherwise we might lose some while we unmount them
if (!flashState.isFlashing()) {
availableDrives.setDrives(values(drives));
}
}

driveScanner.on('attach', addDrive);
driveScanner.on('detach', removeDrive);

driveScanner.on('error', (error) => {
// Stop the drive scanning loop in case of errors,
// otherwise we risk presenting the same error over
// and over again to the user, while also heavily
// spamming our error reporting service.
driveScanner.stop();

return exceptionReporter.report(error);
// Spwaning the child process without privileges to get the drives list
// TODO: clean up this mess of exports
export let requestMetadata: any;

// start the api and spawn the child process
startApiAndSpawnChild({
withPrivileges: false,
}).then(({ emit, registerHandler }) => {
// start scanning
emit('scan');

// make the sourceMetada awaitable to be used on source selection
requestMetadata = async (params: any): Promise<SourceMetadata> => {
emit('sourceMetadata', JSON.stringify(params));

return new Promise((resolve) =>
registerHandler('sourceMetadata', (data: any) => {
resolve(JSON.parse(data));
}),
);
};

registerHandler('drives', (data: any) => {
setDrives(JSON.parse(data));
});
});

driveScanner.start();

let popupExists = false;

analytics.initAnalytics();
Expand Down
5 changes: 3 additions & 2 deletions lib/gui/app/components/flash-results/flash-results.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,9 @@ export function FlashResults({
};
} & FlexProps) {
const [showErrorsInfo, setShowErrorsInfo] = React.useState(false);
const allFailed = !skip && results.devices.successful === 0;
const someFailed = results.devices.failed !== 0 || errors.length !== 0;

const allFailed = !skip && results?.devices?.successful === 0;
const someFailed = results?.devices?.failed !== 0 || errors?.length !== 0;
const effectiveSpeed = bytesToMegabytes(getEffectiveSpeed(results)).toFixed(
1,
);
Expand Down
Loading

0 comments on commit 45bb29a

Please sign in to comment.