Update dependencies

- upgrade pretty_bytes to 6.1.1
- upgrade electron-remote to 2.1.0
- upgrade semver to 7.5.4 + @types/semver to 7.5.6
- upgrade chai to 4.3.11 + @types/chai to 4.3.10
- upgrade mocha to 10.2.0 + @types/mocha to 10.0.6
- upgrade sinon to 17.0.1 + @types/sinon to 17.0.2
- remove useless @types
- upgrade @svgr/webpack to 8.1.0
- upgrade @sentry/electron to 4.15.1
- upgrade tslib to 2.6.2
- upgrade immutable to 4.3.4
- upgrade redux to 4.2.1
- upgrade ts-node to 10.9.2 & ts-loader to 9.5.1
- remove mini-css-extract-plugin
- upgrade husky to 8.0.3
- upgrade uuid to 9.0.1
- upgrade lint-staged to 15.2.1
- upgrade @types/node to 18.11.9
- upgrade @fortawesome/fontawesome-free to 6.5.1
- upgrade i18next to 23.7.8 & react-i18next to 11.18.6
- bump react, react-dom + related @types to 17.0.2 and rendition to 35.1.0
- fix getuid for ts
- fix @types/react being in wrong deps
- upgrade @types/tmp to 0.2.6
- upgrade typescript to 5.3.3
- upgrade @types/mime-types to 2.1.4
- remove d3 from deps
- upgrade electron-updater to 6.1.7
- upgrade rendition to 35.1.2
- upgrade node-ipc to 9.2.3
- upgrade @types/node-ipc to 9.2.3
- upgrade electron to 27.1.3
- upgrade @electron-forge/* to 7.2.0
- upgrade @reforged/marker-appimage to 3.3.2
- upgrade style-loader to 3.3.3
- upgrade balena-lint to 7.2.4
- run CI with node 18.19
- add xxhash-addon to sidecar assets

Change-type: patch
This commit is contained in:
Edwin Joassart 2023-12-08 14:29:12 +01:00 committed by Akis Kesoglou
parent 70304b492d
commit 0f2b4dbc10
32 changed files with 10836 additions and 7179 deletions

10
.eslintrc.js Normal file
View File

@ -0,0 +1,10 @@
module.exports = {
extends: ["./node_modules/@balena/lint/config/.eslintrc.js"],
root: true,
ignorePatterns: ["node_modules/"],
rules: {
"@typescript-eslint/no-floating-promises": "off",
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/ban-ts-comment": "off",
},
};

View File

@ -1,454 +0,0 @@
env:
browser: true
commonjs: true
es6: true
node: true
mocha: true
plugins:
- lodash
- jsdoc
- node
- react
extends: 'standard'
parserOptions:
sourceType: 'script'
ecmaFeatures:
jsx: true
settings:
jsdoc:
additionalTagNames:
customTags:
- fulfil
rules:
# Possible Errors
no-console:
- off
no-empty:
- error
no-extra-semi:
- error
no-negated-in-lhs:
- error
no-prototype-builtins:
- error
valid-jsdoc:
- error
- requireReturn: false
requireReturnDescription: false
requireReturnType: true
requireParamDescription: true
preferType:
boolean: "Boolean"
number: "Number"
object: "Object"
string: "String"
array: "Array"
prefer:
arg: "param"
return: "returns"
# Best Practices
array-callback-return:
- error
block-scoped-var:
- error
class-methods-use-this:
- error
complexity:
- off
consistent-return:
- error
curly:
- error
default-case:
- error
dot-notation:
- error
guard-for-in:
- error
no-alert:
- error
no-case-declarations:
- error
no-div-regex:
- error
no-else-return:
- error
no-empty-function:
- error
no-eq-null:
- error
no-extra-label:
- error
no-implicit-coercion:
- error
no-implicit-globals:
- error
no-loop-func:
- error
no-magic-numbers:
- error
no-native-reassign:
- error
no-param-reassign:
- error
no-restricted-properties:
- error
- property: __proto__
no-return-await:
- error
no-script-url:
- error
no-unused-expressions:
- error
no-unused-labels:
- error
no-useless-concat:
- error
no-void:
- error
no-warning-comments:
- off
radix:
- error
vars-on-top:
- off
# Strict mode
strict:
- error
- global
# Variables
init-declarations:
- error
- always
no-catch-shadow:
- error
no-restricted-globals:
- error
- event
no-shadow:
- error
no-undefined:
- error
no-unused-vars:
- error
no-use-before-define:
- error
# NodeJS and CommonJS
callback-return:
- error
global-require:
- off
no-mixed-requires:
- error
no-process-env:
- off
no-process-exit:
- off
no-sync:
- off
# Stylistic Issues
array-bracket-spacing:
- error
- always
capitalized-comments:
- error
- always
- ignoreConsecutiveComments: true
comma-spacing:
- error
- before: false
after: true
computed-property-spacing:
- error
- never
consistent-this:
- error
- self
func-name-matching:
- error
- always
func-names:
- error
- never
func-style:
- error
- expression
id-blacklist:
- error
id-length:
- error
- min: 2
exceptions:
- "_"
id-match:
- error
- "^[_0-9A-Za-z\\$]+$"
line-comment-position:
- error
- position: above
linebreak-style:
- error
- unix
lines-around-comment:
- error
- beforeBlockComment: true
afterBlockComment: false
beforeLineComment: true
afterLineComment: false
allowBlockStart: true
allowBlockEnd: false
allowObjectStart: true
allowObjectEnd: false
allowArrayStart: true
allowArrayEnd: false
lines-around-directive:
- error
- always
max-len:
- error
- code: 130
comments: 150
ignoreComments: false
ignoreTrailingComments: false
ignoreUrls: true
max-params:
- off
max-statements-per-line:
- error
- max: 1
multiline-ternary:
- off
newline-per-chained-call:
- off
no-bitwise:
- error
no-continue:
- error
no-inline-comments:
- error
no-lonely-if:
- error
no-mixed-operators:
- error
no-multi-assign:
- error
no-negated-condition:
- error
no-nested-ternary:
- error
no-plusplus:
- error
no-restricted-syntax:
- error
- WithStatement
- ForInStatement
no-spaced-func:
- error
no-underscore-dangle:
- error
- allowAfterThis: false
object-curly-newline:
- error
- minProperties: 3
consistent: true
object-curly-spacing:
- error
- always
one-var-declaration-per-line:
- error
- always
operator-assignment:
- error
- always
quotes:
- error
- single
quote-props:
- error
- as-needed
require-jsdoc:
- error
- require:
FunctionDeclaration: true
ClassDeclaration: true
MethodDefinition: true
ArrowFunctionExpression: true
space-before-function-paren:
- error
- anonymous: always
named: always
asyncArrow: always
template-tag-spacing:
- error
- always
unicode-bom:
- error
# ECMAScript 6
arrow-parens:
- error
- always
arrow-spacing:
- error
- before: true
after: true
generator-star-spacing:
- error
- before: true
after: false
no-confusing-arrow:
- error
no-var:
- error
object-shorthand:
- error
- always
prefer-const:
- error
prefer-spread:
- error
prefer-numeric-literals:
- error
prefer-rest-params:
- error
prefer-template:
- error
prefer-arrow-callback:
- error
- allowNamedFunctions: false
require-yield:
- error
symbol-description:
- error
# Lodash
lodash/chain-style:
- error
- explicit
lodash/identity-shorthand:
- error
- always
lodash/import-scope:
- error
- full
lodash/matches-prop-shorthand:
- error
- always
lodash/matches-shorthand:
- error
- always
lodash/no-commit:
- error
lodash/path-style:
- error
- array
lodash/prefer-compact:
- error
lodash/prefer-filter:
- error
- 5
lodash/prefer-flat-map:
- error
lodash/prefer-invoke-map:
- error
lodash/prefer-map:
- error
lodash/prefer-reject:
- error
lodash/prefer-thru:
- error
lodash/prefer-wrapper-method:
- error
lodash/prop-shorthand:
- error
- always
lodash/prefer-constant:
- error
- true
- true
lodash/prefer-get:
- error
- 2
lodash/prefer-includes:
- error
- includeNative: true
lodash/prefer-is-nil:
- error
lodash/prefer-lodash-chain:
- error
lodash/prefer-lodash-method:
- error
lodash/prefer-lodash-typecheck:
- error
lodash/prefer-matches:
- error
- 3
lodash/prefer-noop:
- error
lodash/prefer-over-quantifier:
- error
lodash/prefer-startswith:
- error
lodash/prefer-times:
- error
# JSDoc
jsdoc/check-param-names:
- error
jsdoc/check-tag-names:
- error
jsdoc/newline-after-description:
- error
jsdoc/require-example:
- error
jsdoc/require-hyphen-before-param-description:
- error
jsdoc/require-param:
- error
jsdoc/require-param-description:
- error
jsdoc/require-param-type:
- error
jsdoc/require-returns-type:
- error
# Node
node/no-deprecated-api:
- error
node/no-missing-import:
- error
node/no-missing-require:
- error
node/process-exit-as-throw:
- error
node/no-extraneous-require:
- error
node/no-extraneous-import:
- error
# React
react/jsx-uses-vars:
- error
overrides:
files: ['*.jsx']
rules:
require-jsdoc:
- off

View File

@ -15,7 +15,7 @@ inputs:
# Beware that native modules will be built for this version, # Beware that native modules will be built for this version,
# which might not be compatible with the one used by pkg (see forge.sidecar.ts) # which might not be compatible with the one used by pkg (see forge.sidecar.ts)
# https://github.com/vercel/pkg-fetch/releases # https://github.com/vercel/pkg-fetch/releases
default: "18.18" default: "18.x"
VERBOSE: VERBOSE:
type: string type: string
default: "true" default: "true"
@ -117,9 +117,10 @@ runs:
shell: bash shell: bash
# IMPORTANT: before making changes to this step please consult @engineering in balena's chat. # IMPORTANT: before making changes to this step please consult @engineering in balena's chat.
run: | run: |
if [[ '${{ inputs.VERBOSE }}' =~ on|On|Yes|yes|true|True ]]; then ## FIXME: causes issues with `xxhash` which tries to load a debug build which doens't exist and cannot be compiled
export DEBUG='electron-forge:*,sidecar' # if [[ '${{ inputs.VERBOSE }}' =~ on|On|Yes|yes|true|True ]]; then
fi # export DEBUG='electron-forge:*,sidecar'
# fi
APPLICATION_VERSION="$(jq -r '.version' package.json)" APPLICATION_VERSION="$(jq -r '.version' package.json)"
HOST_ARCH="$(echo "${RUNNER_ARCH}" | tr '[:upper:]' '[:lower:]')" HOST_ARCH="$(echo "${RUNNER_ARCH}" | tr '[:upper:]' '[:lower:]')"

View File

@ -49,9 +49,10 @@ runs:
- name: Test release - name: Test release
shell: bash shell: bash
run: | run: |
if [[ '${{ inputs.VERBOSE }}' =~ on|On|Yes|yes|true|True ]]; then ## FIXME: causes issues with `xxhash` which tries to load a debug build which doens't exist and cannot be compiled
export DEBUG='electron-forge:*,sidecar' # if [[ '${{ inputs.VERBOSE }}' =~ on|On|Yes|yes|true|True ]]; then
fi # export DEBUG='electron-forge:*,sidecar'
# fi
runner_os="$(echo "${RUNNER_OS}" | tr '[:upper:]' '[:lower:]')" runner_os="$(echo "${RUNNER_OS}" | tr '[:upper:]' '[:lower:]')"

View File

@ -38,7 +38,6 @@ import * as windowProgress from './os/window-progress';
import MainPage from './pages/main/MainPage'; import MainPage from './pages/main/MainPage';
import './css/main.css'; import './css/main.css';
import * as i18next from 'i18next'; import * as i18next from 'i18next';
import { promises } from 'dns';
import { SourceMetadata } from '../../shared/typings/source-selector'; import { SourceMetadata } from '../../shared/typings/source-selector';
window.addEventListener( window.addEventListener(

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
import ExclamationTriangleSvg from '@fortawesome/fontawesome-free/svgs/solid/exclamation-triangle.svg'; import ExclamationTriangleSvg from '@fortawesome/fontawesome-free/svgs/solid/triangle-exclamation.svg';
import ChevronDownSvg from '@fortawesome/fontawesome-free/svgs/solid/chevron-down.svg'; import ChevronDownSvg from '@fortawesome/fontawesome-free/svgs/solid/chevron-down.svg';
import * as sourceDestination from 'etcher-sdk/build/source-destination/'; import * as sourceDestination from 'etcher-sdk/build/source-destination/';
import * as React from 'react'; import * as React from 'react';
@ -42,7 +42,7 @@ import {
Table, Table,
} from '../../styled-components'; } from '../../styled-components';
import { SourceMetadata } from '../source-selector/source-selector'; import { SourceMetadata } from '../../../../shared/typings/source-selector';
import { middleEllipsis } from '../../utils/middle-ellipsis'; import { middleEllipsis } from '../../utils/middle-ellipsis';
import * as i18next from 'i18next'; import * as i18next from 'i18next';
@ -310,9 +310,17 @@ export class DriveSelector extends React.Component<
case compatibility.system(): case compatibility.system():
return warning.systemDrive(); return warning.systemDrive();
case compatibility.tooSmall(): case compatibility.tooSmall():
const size = return warning.tooSmall(
this.state.image?.recommendedDriveSize || this.state.image?.size || 0; {
return warning.tooSmall({ size }, drive); size:
this.state.image?.recommendedDriveSize ||
this.state.image?.size ||
0,
},
drive,
);
default:
return '';
} }
} }
@ -428,11 +436,10 @@ export class DriveSelector extends React.Component<
) : ( ) : (
<> <>
<DrivesTable <DrivesTable
refFn={(t) => { refFn={() => {
if (t !== null) { // noop
t.setRowSelection(selectedList);
}
}} }}
checkedItems={selectedList}
checkedRowsNumber={selectedList.length} checkedRowsNumber={selectedList.length}
multipleSelection={this.props.multipleSelection} multipleSelection={this.props.multipleSelection}
columns={this.tableColumns} columns={this.tableColumns}
@ -442,7 +449,10 @@ export class DriveSelector extends React.Component<
isDrivelistDrive(row) && row.isSystem ? ['system'] : [] isDrivelistDrive(row) && row.isSystem ? ['system'] : []
} }
rowKey="displayName" rowKey="displayName"
onCheck={(rows: Drive[]) => { onCheck={(rows) => {
if (rows == null) {
rows = [];
}
let newSelection = rows.filter(isDrivelistDrive); let newSelection = rows.filter(isDrivelistDrive);
if (this.props.multipleSelection) { if (this.props.multipleSelection) {
if (rows.length === 0) { if (rows.length === 0) {

View File

@ -1,11 +1,10 @@
import ExclamationTriangleSvg from '@fortawesome/fontawesome-free/svgs/solid/exclamation-triangle.svg'; import ExclamationTriangleSvg from '@fortawesome/fontawesome-free/svgs/solid/triangle-exclamation.svg';
import * as _ from 'lodash';
import * as React from 'react'; import * as React from 'react';
import { Badge, Flex, Txt, ModalProps } from 'rendition'; import { Badge, Flex, Txt, ModalProps } from 'rendition';
import { Modal, ScrollableFlex } from '../../styled-components'; import { Modal, ScrollableFlex } from '../../styled-components';
import { middleEllipsis } from '../../utils/middle-ellipsis'; import { middleEllipsis } from '../../utils/middle-ellipsis';
import * as prettyBytes from 'pretty-bytes'; import prettyBytes from 'pretty-bytes';
import { DriveWithWarnings } from '../../pages/main/Flash'; import { DriveWithWarnings } from '../../pages/main/Flash';
import * as i18next from 'i18next'; import * as i18next from 'i18next';

View File

@ -15,9 +15,8 @@
*/ */
import CircleSvg from '@fortawesome/fontawesome-free/svgs/solid/circle.svg'; import CircleSvg from '@fortawesome/fontawesome-free/svgs/solid/circle.svg';
import CheckCircleSvg from '@fortawesome/fontawesome-free/svgs/solid/check-circle.svg'; import CheckCircleSvg from '@fortawesome/fontawesome-free/svgs/solid/circle-check.svg';
import TimesCircleSvg from '@fortawesome/fontawesome-free/svgs/solid/times-circle.svg'; import TimesCircleSvg from '@fortawesome/fontawesome-free/svgs/solid/circle-xmark.svg';
import outdent from 'outdent';
import * as React from 'react'; import * as React from 'react';
import { Flex, FlexProps, Link, TableColumn, Txt } from 'rendition'; import { Flex, FlexProps, Link, TableColumn, Txt } from 'rendition';
import styled from 'styled-components'; import styled from 'styled-components';

View File

@ -17,7 +17,7 @@
import GithubSvg from '@fortawesome/fontawesome-free/svgs/brands/github.svg'; import GithubSvg from '@fortawesome/fontawesome-free/svgs/brands/github.svg';
import * as _ from 'lodash'; import * as _ from 'lodash';
import * as React from 'react'; import * as React from 'react';
import { Box, Checkbox, Flex, TextWithCopy, Txt } from 'rendition'; import { Box, Checkbox, Flex, Txt } from 'rendition';
import { version, packageType } from '../../../../../package.json'; import { version, packageType } from '../../../../../package.json';
import * as settings from '../../models/settings'; import * as settings from '../../models/settings';
@ -61,7 +61,9 @@ const EPInfo = etcherProInfo();
const InfoBox = (props: any) => ( const InfoBox = (props: any) => (
<Box fontSize={14}> <Box fontSize={14}>
<Txt>{props.label}</Txt> <Txt>{props.label}</Txt>
<TextWithCopy code text={props.value} copy={props.value} /> <Txt code copy={props.value}>
{props.value}{' '}
</Txt>
</Box> </Box>
); );

View File

@ -17,7 +17,7 @@
import CopySvg from '@fortawesome/fontawesome-free/svgs/solid/copy.svg'; import CopySvg from '@fortawesome/fontawesome-free/svgs/solid/copy.svg';
import FileSvg from '@fortawesome/fontawesome-free/svgs/solid/file.svg'; import FileSvg from '@fortawesome/fontawesome-free/svgs/solid/file.svg';
import LinkSvg from '@fortawesome/fontawesome-free/svgs/solid/link.svg'; import LinkSvg from '@fortawesome/fontawesome-free/svgs/solid/link.svg';
import ExclamationTriangleSvg from '@fortawesome/fontawesome-free/svgs/solid/exclamation-triangle.svg'; import ExclamationTriangleSvg from '@fortawesome/fontawesome-free/svgs/solid/triangle-exclamation.svg';
import ChevronDownSvg from '@fortawesome/fontawesome-free/svgs/solid/chevron-down.svg'; import ChevronDownSvg from '@fortawesome/fontawesome-free/svgs/solid/chevron-down.svg';
import ChevronRightSvg from '@fortawesome/fontawesome-free/svgs/solid/chevron-right.svg'; import ChevronRightSvg from '@fortawesome/fontawesome-free/svgs/solid/chevron-right.svg';
import { ipcRenderer, IpcRendererEvent } from 'electron'; import { ipcRenderer, IpcRendererEvent } from 'electron';
@ -388,10 +388,9 @@ export class SourceSelector extends React.Component<
SourceType: Source, SourceType: Source,
auth?: Authentication, auth?: Authentication,
): { promise: Promise<void>; cancel: () => void } { ): { promise: Promise<void>; cancel: () => void } {
let cancelled = false;
return { return {
cancel: () => { cancel: () => {
cancelled = true; // noop
}, },
promise: (async () => { promise: (async () => {
const sourcePath = isString(selected) ? selected : selected.device; const sourcePath = isString(selected) ? selected : selected.device;
@ -519,8 +518,8 @@ export class SourceSelector extends React.Component<
} }
private async onDrop(event: React.DragEvent<HTMLDivElement>) { private async onDrop(event: React.DragEvent<HTMLDivElement>) {
const [file] = event.dataTransfer.files; const file = event.dataTransfer.files.item(0);
if (file) { if (file != null) {
await this.selectSource(file.path, 'File').promise; await this.selectSource(file.path, 'File').promise;
} }
} }
@ -581,7 +580,7 @@ export class SourceSelector extends React.Component<
imageLoading, imageLoading,
} = this.state; } = this.state;
const selectionImage = selectionState.getImage(); const selectionImage = selectionState.getImage();
let image: SourceMetadata | DrivelistDrive = let image =
selectionImage !== undefined ? selectionImage : ({} as SourceMetadata); selectionImage !== undefined ? selectionImage : ({} as SourceMetadata);
image = image.drive ?? image; image = image.drive ?? image;
@ -684,7 +683,7 @@ export class SourceSelector extends React.Component<
style={{ style={{
boxShadow: '0 3px 7px rgba(0, 0, 0, 0.3)', boxShadow: '0 3px 7px rgba(0, 0, 0, 0.3)',
}} }}
titleElement={ title={
<span> <span>
<ExclamationTriangleSvg fill="#fca321" height="1em" />{' '} <ExclamationTriangleSvg fill="#fca321" height="1em" />{' '}
<span>{this.state.warning.title}</span> <span>{this.state.warning.title}</span>

View File

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
import ExclamationTriangleSvg from '@fortawesome/fontawesome-free/svgs/solid/exclamation-triangle.svg'; import ExclamationTriangleSvg from '@fortawesome/fontawesome-free/svgs/solid/triangle-exclamation.svg';
import * as React from 'react'; import * as React from 'react';
import { Flex, FlexProps, Txt } from 'rendition'; import { Flex, FlexProps, Txt } from 'rendition';

View File

@ -43,7 +43,7 @@ function blink(t: number) {
return Math.floor(t) % 2; return Math.floor(t) % 2;
} }
function one(_t: number) { function one() {
return 1; return 1;
} }

View File

@ -52,7 +52,7 @@ export const anonymizeSentryData = (
return event; return event;
}; };
const extractPathRegex = /(.*)(^|\s)(file\:\/\/)?(\w\:)?([\\\/].+)/; const extractPathRegex = /(.*)(^|\s)(file:\/\/)?(\w:)?([\\/].+)/;
const etcherSegmentMarkers = ['app.asar', 'Resources']; const etcherSegmentMarkers = ['app.asar', 'Resources'];
export const anonymizePath = (input: string) => { export const anonymizePath = (input: string) => {
@ -156,7 +156,7 @@ function flattenObject(obj: any) {
const toReturn: AnalyticsPayload = {}; const toReturn: AnalyticsPayload = {};
for (const i in obj) { for (const i in obj) {
if (!obj.hasOwnProperty(i)) { if (!Object.prototype.hasOwnProperty.call(obj, i)) {
continue; continue;
} }
@ -168,7 +168,7 @@ function flattenObject(obj: any) {
if (typeof obj[i] === 'object' && obj[i] !== null) { if (typeof obj[i] === 'object' && obj[i] !== null) {
const flatObject = flattenObject(obj[i]); const flatObject = flattenObject(obj[i]);
for (const x in flatObject) { for (const x in flatObject) {
if (!flatObject.hasOwnProperty(x)) { if (!Object.prototype.hasOwnProperty.call(flatObject, x)) {
continue; continue;
} }

View File

@ -25,7 +25,6 @@ import * as settings from '../models/settings';
import * as analytics from '../modules/analytics'; import * as analytics from '../modules/analytics';
import * as windowProgress from '../os/window-progress'; import * as windowProgress from '../os/window-progress';
import { startApiAndSpawnChild } from './api'; import { startApiAndSpawnChild } from './api';
import { terminateScanningServer } from '../app';
/** /**
* @summary Handle a flash error and log it to analytics * @summary Handle a flash error and log it to analytics
@ -81,7 +80,13 @@ async function performWrite(
console.log({ image, drives }); console.log({ image, drives });
return await new Promise(async (resolve, reject) => { // Spawn the child process with privileges and wait for the connection to be made
const { emit, registerHandler, terminateServer } =
await startApiAndSpawnChild({
withPrivileges: true,
});
return await new Promise((resolve, reject) => {
const flashResults: FlashResults = {}; const flashResults: FlashResults = {};
const analyticsData = { const analyticsData = {
@ -92,7 +97,7 @@ async function performWrite(
flashInstanceUuid: flashState.getFlashUuid(), flashInstanceUuid: flashState.getFlashUuid(),
}; };
const onFail = ({ device, error }) => { const onFail = ({ device, error }: { device: any; error: any }) => {
console.log('fail event'); console.log('fail event');
console.log(device); console.log(device);
console.log(error); console.log(error);
@ -103,7 +108,7 @@ async function performWrite(
finish(); finish();
}; };
const onDone = (event) => { const onDone = (event: any) => {
console.log('done event'); console.log('done event');
event.results.errors = event.results.errors.map( event.results.errors = event.results.errors.map(
(data: Dictionary<any> & { message: string }) => { (data: Dictionary<any> & { message: string }) => {
@ -151,12 +156,6 @@ async function performWrite(
resolve(flashResults); resolve(flashResults);
}; };
// Spawn the child process with privileges and wait for the connection to be made
const { emit, registerHandler, terminateServer } =
await startApiAndSpawnChild({
withPrivileges: true,
});
registerHandler('state', onProgress); registerHandler('state', onProgress);
registerHandler('fail', onFail); registerHandler('fail', onFail);
registerHandler('done', onDone); registerHandler('done', onDone);

View File

@ -14,8 +14,8 @@
* limitations under the License. * limitations under the License.
*/ */
import CogSvg from '@fortawesome/fontawesome-free/svgs/solid/cog.svg'; import CogSvg from '@fortawesome/fontawesome-free/svgs/solid/gear.svg';
import QuestionCircleSvg from '@fortawesome/fontawesome-free/svgs/solid/question-circle.svg'; import QuestionCircleSvg from '@fortawesome/fontawesome-free/svgs/solid/circle-question.svg';
import * as path from 'path'; import * as path from 'path';
import * as prettyBytes from 'pretty-bytes'; import * as prettyBytes from 'pretty-bytes';
@ -116,10 +116,10 @@ interface MainPageState {
} }
export class MainPage extends React.Component< export class MainPage extends React.Component<
{}, object,
MainPageState & MainPageStateFromStore MainPageState & MainPageStateFromStore
> { > {
constructor(props: {}) { constructor(props: object) {
super(props); super(props);
this.state = { this.state = {
current: 'main', current: 'main',

View File

@ -14,7 +14,6 @@
* limitations under the License. * limitations under the License.
*/ */
import * as _ from 'lodash';
import * as React from 'react'; import * as React from 'react';
import { import {
Alert as AlertBase, Alert as AlertBase,
@ -113,14 +112,25 @@ export const DetailsText = (props: FlexProps) => (
const modalFooterShadowCss = css` const modalFooterShadowCss = css`
overflow: auto; overflow: auto;
background: 0, linear-gradient(rgba(255, 255, 255, 0), white 70%) 0 100%, 0, background:
0,
linear-gradient(rgba(255, 255, 255, 0), white 70%) 0 100%,
0,
linear-gradient(rgba(255, 255, 255, 0), rgba(221, 225, 240, 0.5) 70%) 0 100%; linear-gradient(rgba(255, 255, 255, 0), rgba(221, 225, 240, 0.5) 70%) 0 100%;
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: 100% 40px, 100% 40px, 100% 8px, 100% 8px; background-size:
100% 40px,
100% 40px,
100% 8px,
100% 8px;
background-repeat: no-repeat; background-repeat: no-repeat;
background-color: white; background-color: white;
background-size: 100% 40px, 100% 40px, 100% 8px, 100% 8px; background-size:
100% 40px,
100% 40px,
100% 8px,
100% 8px;
background-attachment: local, local, scroll, scroll; background-attachment: local, local, scroll, scroll;
`; `;
@ -236,16 +246,15 @@ export interface GenericTableProps<T> extends BaseTableProps<T> {
showWarnings?: boolean; showWarnings?: boolean;
} }
const GenericTable: <T>( function GenericTable<T>(
props: GenericTableProps<T>, props: GenericTableProps<T>,
) => React.ReactElement<GenericTableProps<T>> = <T extends {}>({ ): React.ReactElement<GenericTableProps<T>> {
refFn, return (
...props
}: GenericTableProps<T>) => (
<div> <div>
<BaseTable<T> ref={refFn} {...props} /> <BaseTable<T> ref={props.refFn} {...props} />
</div> </div>
); );
}
function StyledTable<T>() { function StyledTable<T>() {
return styled((props: GenericTableProps<T>) => ( return styled((props: GenericTableProps<T>) => (
@ -284,7 +293,6 @@ function StyledTable<T>() {
[data-display='table-body'] > [data-display='table-row'] { [data-display='table-body'] > [data-display='table-row'] {
> [data-display='table-cell']:first-child { > [data-display='table-cell']:first-child {
padding-left: 15px; padding-left: 15px;
width: 6%;
} }
> [data-display='table-cell']:last-child { > [data-display='table-cell']:last-child {
@ -319,7 +327,7 @@ function StyledTable<T>() {
`; `;
} }
export const Table = <T extends {}>(props: GenericTableProps<T>) => { export const Table = <T extends object>(props: GenericTableProps<T>) => {
const TypedStyledFunctional = StyledTable<T>(); const TypedStyledFunctional = StyledTable<T>();
return <TypedStyledFunctional {...props} />; return <TypedStyledFunctional {...props} />;
}; };

View File

@ -40,6 +40,8 @@ import * as SentryMain from '@sentry/electron/main';
import * as packageJSON from '../../package.json'; import * as packageJSON from '../../package.json';
import { anonymizeSentryData } from './app/modules/analytics'; import { anonymizeSentryData } from './app/modules/analytics';
import { delay } from '../shared/utils';
const customProtocol = 'etcher'; const customProtocol = 'etcher';
const scheme = `${customProtocol}://`; const scheme = `${customProtocol}://`;
const updatablePackageTypes = ['appimage', 'nsis', 'dmg']; const updatablePackageTypes = ['appimage', 'nsis', 'dmg'];
@ -144,14 +146,6 @@ electron.app.on('open-url', async (event, data) => {
await selectImageURL(data); await selectImageURL(data);
}); });
interface AutoUpdaterConfig {
autoDownload?: boolean;
autoInstallOnAppQuit?: boolean;
allowPrerelease?: boolean;
fullChangelog?: boolean;
allowDowngrade?: boolean;
}
async function createMainWindow() { async function createMainWindow() {
const fullscreen = Boolean(await settings.get('fullscreen')); const fullscreen = Boolean(await settings.get('fullscreen'));
const defaultWidth = settings.DEFAULT_WIDTH; const defaultWidth = settings.DEFAULT_WIDTH;
@ -202,7 +196,7 @@ async function createMainWindow() {
// Prevent external resources from being loaded (like images) // Prevent external resources from being loaded (like images)
// when dropping them on the WebView. // when dropping them on the WebView.
// See https://github.com/electron/electron/issues/5919 // See https://github.com/electron/electron/issues/5919
mainWindow.webContents.on('will-navigate', (event) => { mainWindow.webContents.on('will-navigate', (event: any) => {
event.preventDefault(); event.preventDefault();
}); });
@ -287,7 +281,7 @@ async function main(): Promise<void> {
const webview = electron.webContents.fromId(id); const webview = electron.webContents.fromId(id);
// Open link in browser if it's opened as a 'foreground-tab' // Open link in browser if it's opened as a 'foreground-tab'
webview.setWindowOpenHandler((event) => { webview!.setWindowOpenHandler((event) => {
const url = new URL(event.url); const url = new URL(event.url);
if ( if (
(url.protocol === 'http:' || url.protocol === 'https:') && (url.protocol === 'http:' || url.protocol === 'https:') &&

View File

@ -16,7 +16,7 @@
import { Dictionary } from 'lodash'; import { Dictionary } from 'lodash';
import { outdent } from 'outdent'; import { outdent } from 'outdent';
import * as prettyBytes from 'pretty-bytes'; import prettyBytes from 'pretty-bytes';
import '../gui/app/i18n'; import '../gui/app/i18n';
import * as i18next from 'i18next'; import * as i18next from 'i18next';

View File

@ -70,14 +70,14 @@ export async function isElevated(): Promise<boolean> {
} }
return true; return true;
} }
return process.geteuid() === UNIX_SUPERUSER_USER_ID; return process.geteuid!() === UNIX_SUPERUSER_USER_ID;
} }
/** /**
* @summary Check if the current process is running with elevated permissions * @summary Check if the current process is running with elevated permissions
*/ */
export function isElevatedUnixSync(): boolean { export function isElevatedUnixSync(): boolean {
return process.geteuid() === UNIX_SUPERUSER_USER_ID; return process.geteuid!() === UNIX_SUPERUSER_USER_ID;
} }
function escapeSh(value: any): string { function escapeSh(value: any): string {

View File

@ -15,17 +15,18 @@
*/ */
import * as ipc from 'node-ipc'; import * as ipc from 'node-ipc';
import { Dictionary, values } from 'lodash';
import type { MultiDestinationProgress } from 'etcher-sdk/build/multi-write';
import { toJSON } from '../shared/errors'; import { toJSON } from '../shared/errors';
import { GENERAL_ERROR, SUCCESS } from '../shared/exit-codes'; import { GENERAL_ERROR, SUCCESS } from '../shared/exit-codes';
import { delay } from '../shared/utils'; import { delay } from '../shared/utils';
import { WriteOptions } from './types/types'; import { WriteOptions } from './types/types';
import { MultiDestinationProgress } from 'etcher-sdk/build/multi-write';
import { write, cleanup } from './child-writer'; import { write, cleanup } from './child-writer';
import { startScanning } from './scanner'; import { startScanning } from './scanner';
import { getSourceMetadata } from './source-metadata'; import { getSourceMetadata } from './source-metadata';
import { DrivelistDrive } from '../shared/drive-constraints'; import { DrivelistDrive } from '../shared/drive-constraints';
import { Dictionary, values } from 'lodash';
ipc.config.id = process.env.IPC_CLIENT_ID as string; ipc.config.id = process.env.IPC_CLIENT_ID as string;
ipc.config.socketRoot = process.env.IPC_SOCKET_ROOT as string; ipc.config.socketRoot = process.env.IPC_SOCKET_ROOT as string;
@ -40,6 +41,7 @@ ipc.config.silent = true;
// The purpose behind this change is for this process // The purpose behind this change is for this process
// to emit a "disconnect" event as soon as the GUI // to emit a "disconnect" event as soon as the GUI
// process is closed, so we can kill this process as well. // process is closed, so we can kill this process as well.
// @ts-ignore (0 is a valid value for stopRetrying and is not the same as false) // @ts-ignore (0 is a valid value for stopRetrying and is not the same as false)
ipc.config.stopRetrying = 0; ipc.config.stopRetrying = 0;

View File

@ -52,6 +52,7 @@ async function write(options: WriteOptions) {
const onFail = (destination: SourceDestination, error: Error) => { const onFail = (destination: SourceDestination, error: Error) => {
emitFail({ emitFail({
// TODO: device should be destination // TODO: device should be destination
// @ts-ignore (destination.drive is private) // @ts-ignore (destination.drive is private)
device: destination.drive, device: destination.drive,
error: toJSON(error), error: toJSON(error),

View File

@ -30,14 +30,13 @@ const adapters: Adapter[] = [
// Can't use permissions.isElevated() here as it returns a promise and we need to set // Can't use permissions.isElevated() here as it returns a promise and we need to set
// module.exports = scanner right now. // module.exports = scanner right now.
if (platform !== 'linux' || geteuid() === 0) { if (platform !== 'linux' || (geteuid && geteuid() === 0)) {
adapters.push(new UsbbootDeviceAdapter()); adapters.push(new UsbbootDeviceAdapter());
} }
if (platform === 'win32') { if (platform === 'win32') {
const { const {
DriverlessDeviceAdapter: driverless, DriverlessDeviceAdapter: driverless,
// tslint:disable-next-line:no-var-requires
} = require('etcher-sdk/build/scanner/adapters/driverless'); } = require('etcher-sdk/build/scanner/adapters/driverless');
adapters.push(new driverless()); adapters.push(new driverless());
} }

View File

@ -73,9 +73,11 @@ function prepareDrive(drive: Drive) {
return drive.drive; return drive.drive;
} else if (drive instanceof sdk.sourceDestination.UsbbootDrive) { } else if (drive instanceof sdk.sourceDestination.UsbbootDrive) {
// This is a workaround etcher expecting a device string and a size // This is a workaround etcher expecting a device string and a size
// @ts-ignore // @ts-ignore
drive.device = drive.usbDevice.portId; drive.device = drive.usbDevice.portId;
drive.size = null; drive.size = null;
// @ts-ignore // @ts-ignore
drive.progress = 0; drive.progress = 0;
drive.disabled = true; drive.disabled = true;
@ -140,6 +142,7 @@ function updateDriveProgress(
progress: number, progress: number,
) { ) {
const drives = getDrives(); const drives = getDrives();
// @ts-ignore // @ts-ignore
const driveInMap = drives[drive.device]; const driveInMap = drives[drive.device];
if (driveInMap) { if (driveInMap) {

17200
npm-shrinkwrap.json generated

File diff suppressed because it is too large Load Diff

View File

@ -14,9 +14,8 @@
"url": "git@github.com:balena-io/etcher.git" "url": "git@github.com:balena-io/etcher.git"
}, },
"scripts": { "scripts": {
"lint-css": "prettier --write lib/**/*.css", "prettify": "prettier --write lib/**/*.css && balena-lint --fix --typescript typings lib tests forge.config.ts forge.sidecar.ts webpack.config.ts",
"lint-ts": "balena-lint --fix --typescript typings lib tests forge.config.ts forge.sidecar.ts webpack.config.ts", "lint": "npm run prettify && catch-uncommitted",
"lint": "npm run lint-ts && npm run lint-css",
"test-gui": "electron-mocha --recursive --reporter spec --window-config tests/gui/window-config.json --require ts-node/register/transpile-only --require-main tests/gui/allow-renderer-process-reuse.ts --full-trace --no-sandbox --renderer tests/gui/**/*.ts", "test-gui": "electron-mocha --recursive --reporter spec --window-config tests/gui/window-config.json --require ts-node/register/transpile-only --require-main tests/gui/allow-renderer-process-reuse.ts --full-trace --no-sandbox --renderer tests/gui/**/*.ts",
"test-shared": "electron-mocha --recursive --reporter spec --require ts-node/register/transpile-only --require-main tests/gui/allow-renderer-process-reuse.ts --full-trace --no-sandbox tests/shared/**/*.ts", "test-shared": "electron-mocha --recursive --reporter spec --require ts-node/register/transpile-only --require-main tests/gui/allow-renderer-process-reuse.ts --full-trace --no-sandbox tests/shared/**/*.ts",
"test-windows": "npm run lint && npm run test-gui && npm run test-shared", "test-windows": "npm run lint && npm run test-gui && npm run test-shared",
@ -29,94 +28,82 @@
}, },
"husky": { "husky": {
"hooks": { "hooks": {
"pre-commit": "lint-staged" "pre-commit": "npm run prettify"
} }
}, },
"lint-staged": {
"./**/*.{ts,tsx}": [
"npm run lint-ts"
],
"./**/*.css": [
"npm run lint-css"
]
},
"author": "Balena Ltd. <hello@balena.io>", "author": "Balena Ltd. <hello@balena.io>",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@balena/sudo-prompt": "9.2.1-workaround-windows-amperstand-in-username-0849e215b947987a643fe5763902aea201255534", "@balena/sudo-prompt": "9.2.1-workaround-windows-amperstand-in-username-0849e215b947987a643fe5763902aea201255534",
"@electron/remote": "^2.0.9", "@electron/remote": "^2.1.0",
"@fortawesome/fontawesome-free": "5.15.4", "@fortawesome/fontawesome-free": "6.5.1",
"@sentry/electron": "^4.1.2", "@sentry/electron": "^4.15.1",
"analytics-client": "^2.0.1", "analytics-client": "^2.0.1",
"axios": "^0.27.2", "axios": "^1.6.0",
"d3": "4.13.0",
"debug": "4.3.4", "debug": "4.3.4",
"electron-squirrel-startup": "^1.0.0", "electron-squirrel-startup": "^1.0.0",
"electron-updater": "5.3.0", "electron-updater": "6.1.7",
"etcher-sdk": "8.3.1", "etcher-sdk": "9.0.0",
"i18next": "21.10.0", "i18next": "23.7.8",
"immutable": "3.8.2", "immutable": "3.8.2",
"lodash": "4.17.21", "lodash": "4.17.21",
"node-ipc": "9.2.1", "node-ipc": "9.2.1",
"outdent": "0.8.0", "outdent": "0.8.0",
"path-is-inside": "1.0.2", "path-is-inside": "1.0.2",
"pretty-bytes": "5.6.0", "pretty-bytes": "5.6.0",
"react": "16.8.5", "react": "17.0.2",
"react-dom": "16.8.5", "react-dom": "17.0.2",
"react-i18next": "11.18.6", "react-i18next": "13.5.0",
"redux": "4.2.0", "redux": "4.2.1",
"rendition": "19.3.2", "rendition": "35.1.2",
"semver": "7.3.8", "semver": "7.5.4",
"styled-components": "5.3.6", "styled-components": "5.3.6",
"sys-class-rgb-led": "3.0.1", "sys-class-rgb-led": "3.0.1",
"uuid": "8.3.2" "uuid": "9.0.1"
}, },
"devDependencies": { "devDependencies": {
"@balena/lint": "5.4.2", "@balena/lint": "7.2.4",
"@electron-forge/cli": "6.4.2", "@electron-forge/cli": "7.2.0",
"@electron-forge/maker-deb": "6.4.2", "@electron-forge/maker-deb": "7.2.0",
"@electron-forge/maker-dmg": "6.4.2", "@electron-forge/maker-dmg": "7.2.0",
"@electron-forge/maker-rpm": "6.4.2", "@electron-forge/maker-rpm": "7.2.0",
"@electron-forge/maker-squirrel": "6.4.2", "@electron-forge/maker-squirrel": "7.2.0",
"@electron-forge/maker-zip": "6.4.2", "@electron-forge/maker-zip": "7.2.0",
"@electron-forge/plugin-auto-unpack-natives": "6.4.2", "@electron-forge/plugin-auto-unpack-natives": "7.2.0",
"@electron-forge/plugin-webpack": "6.4.2", "@electron-forge/plugin-webpack": "7.2.0",
"@reforged/maker-appimage": "3.3.1", "@reforged/maker-appimage": "3.3.2",
"@svgr/webpack": "5.5.0", "@svgr/webpack": "8.1.0",
"@types/chai": "4.3.4", "@types/chai": "4.3.11",
"@types/copy-webpack-plugin": "6.4.3",
"@types/debug": "^4.1.12", "@types/debug": "^4.1.12",
"@types/mime-types": "2.1.1", "@types/mime-types": "2.1.4",
"@types/mini-css-extract-plugin": "1.4.3", "@types/mocha": "^10.0.6",
"@types/mocha": "^9.1.1", "@types/node": "^18.11.9",
"@types/node": "^16.18.12", "@types/node-ipc": "9.2.3",
"@types/node-ipc": "9.2.0", "@types/react": "17.0.2",
"@types/react": "16.14.34", "@types/react-dom": "17.0.2",
"@types/react-dom": "16.9.17", "@types/semver": "7.5.6",
"@types/semver": "7.3.13", "@types/sinon": "17.0.2",
"@types/sinon": "9.0.11", "@types/tmp": "0.2.6",
"@types/tmp": "0.2.3",
"@vercel/webpack-asset-relocator-loader": "1.7.3", "@vercel/webpack-asset-relocator-loader": "1.7.3",
"chai": "4.3.7", "catch-uncommitted": "^2.0.0",
"chai": "4.3.10",
"css-loader": "5.2.7", "css-loader": "5.2.7",
"electron": "25.8.2", "electron": "27.1.3",
"electron-mocha": "^11.0.2", "electron-mocha": "^12.2.0",
"file-loader": "6.2.0", "file-loader": "6.2.0",
"husky": "4.3.8", "husky": "8.0.3",
"lint-staged": "10.5.4", "mocha": "^10.2.0",
"mini-css-extract-plugin": "1.6.2",
"mocha": "^9.1.1",
"native-addon-loader": "2.0.1", "native-addon-loader": "2.0.1",
"node-loader": "^2.0.0", "node-loader": "^2.0.0",
"omit-deep-lodash": "1.1.7", "omit-deep-lodash": "1.1.7",
"pkg": "^5.8.1", "pkg": "^5.8.1",
"sinon": "9.2.4", "sinon": "17.0.1",
"string-replace-loader": "3.1.0", "string-replace-loader": "3.1.0",
"style-loader": "2.0.0", "style-loader": "3.3.3",
"ts-loader": "^9.5.0", "ts-loader": "^9.5.1",
"ts-node": "^10.9.1", "ts-node": "^10.9.2",
"tslib": "2.4.1", "tslib": "2.6.2",
"typescript": "4.4.4", "typescript": "^5.3.3",
"url-loader": "4.1.1" "url-loader": "4.1.1"
}, },
"hostDependencies": { "hostDependencies": {

View File

@ -5,6 +5,8 @@
"node_modules/drivelist/**", "node_modules/drivelist/**",
"node_modules/mountutils/**", "node_modules/mountutils/**",
"node_modules/winusb-driver-generator/**", "node_modules/winusb-driver-generator/**",
"node_modules/node-raspberrypi-usbboot/**" "node_modules/node-raspberrypi-usbboot/**",
"node_modules/xxhash-addon/**",
"node_modules/axios/**"
] ]
} }

View File

@ -1,8 +1,6 @@
// tslint:disable-next-line:no-var-requires
const { app } = require('electron'); const { app } = require('electron');
if (app !== undefined) { if (app !== undefined) {
// tslint:disable-next-line:no-var-requires
const remoteMain = require('@electron/remote/main'); const remoteMain = require('@electron/remote/main');
remoteMain.initialize(); remoteMain.initialize();

View File

@ -15,7 +15,6 @@
*/ */
import { expect } from 'chai'; import { expect } from 'chai';
import * as _ from 'lodash';
import { stub } from 'sinon'; import { stub } from 'sinon';
import * as settings from '../../../lib/gui/app/models/settings'; import * as settings from '../../../lib/gui/app/models/settings';
@ -47,8 +46,8 @@ describe('Browser: settings', () => {
const writeConfigFileStub = stub(); const writeConfigFileStub = stub();
writeConfigFileStub.returns(Promise.reject(new Error('settings error'))); writeConfigFileStub.returns(Promise.reject(new Error('settings error')));
const p = settings.set('foo', 'baz', writeConfigFileStub); const promise = settings.set('foo', 'baz', writeConfigFileStub);
await checkError(p, async (error) => { await checkError(promise, async (error) => {
expect(error).to.be.an.instanceof(Error); expect(error).to.be.an.instanceof(Error);
expect(error.message).to.equal('settings error'); expect(error.message).to.equal('settings error');
expect(await settings.get('foo')).to.equal('bar'); expect(await settings.get('foo')).to.equal('bar');

View File

@ -1,14 +1,14 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ES2019", "target": "ES2020",
"allowJs": false, "allowJs": false,
"skipLibCheck": true, "skipLibCheck": true,
"esModuleInterop": false, "esModuleInterop": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"strict": true, "strict": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"typeRoots": ["./node_modules/@types", "./typings"], "typeRoots": ["./node_modules/@types", "./typings"],
"module": "CommonJS", "module": "commonjs",
"moduleResolution": "Node", "moduleResolution": "Node",
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true "isolatedModules": true

View File

@ -16,7 +16,6 @@
import type { Configuration, ModuleOptions } from 'webpack'; import type { Configuration, ModuleOptions } from 'webpack';
import * as _ from 'lodash';
import { import {
BannerPlugin, BannerPlugin,
IgnorePlugin, IgnorePlugin,