Skip to content

Commit

Permalink
Convert to TypeScript with es6 module notation
Browse files Browse the repository at this point in the history
Change-type: major
  • Loading branch information
thgreasi committed Dec 18, 2024
1 parent 4c0cf8a commit 3576d2c
Show file tree
Hide file tree
Showing 9 changed files with 1,078 additions and 562 deletions.
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ Documentation


* [init](#module_init)
* [.configure(image, manifest, config, [options])](#module_init.configure) ⇒ <code>Promise.&lt;EventEmitter&gt;</code>
* [.initialize(image, manifest, options)](#module_init.initialize) ⇒ <code>Promise.&lt;EventEmitter&gt;</code>
* [~configure(image, manifest, config, [options])](#module_init..configure) ⇒ <code>Promise.&lt;EventEmitter&gt;</code>
* [~initialize(image, manifest, options)](#module_init..initialize) ⇒ <code>Promise.&lt;EventEmitter&gt;</code>

<a name="module_init.configure"></a>
<a name="module_init..configure"></a>

### init.configure(image, manifest, config, [options]) ⇒ <code>Promise.&lt;EventEmitter&gt;</code>
### init~configure(image, manifest, config, [options]) ⇒ <code>Promise.&lt;EventEmitter&gt;</code>
This function injects `config.json` and network settings into the image.

**Kind**: static method of [<code>init</code>](#module_init)
**Kind**: inner method of [<code>init</code>](#module_init)
**Summary**: Configure an image with an application
**Returns**: <code>Promise.&lt;EventEmitter&gt;</code> - configuration event emitter
**Access**: public
Expand Down Expand Up @@ -65,10 +65,10 @@ init.configure('my/rpi.img', manifest, config).then (configuration) ->
configuration.on 'end', ->
console.log('Configuration finished')
```
<a name="module_init.initialize"></a>
<a name="module_init..initialize"></a>

### init.initialize(image, manifest, options) ⇒ <code>Promise.&lt;EventEmitter&gt;</code>
**Kind**: static method of [<code>init</code>](#module_init)
### init~initialize(image, manifest, options) ⇒ <code>Promise.&lt;EventEmitter&gt;</code>
**Kind**: inner method of [<code>init</code>](#module_init)
**Summary**: Initialize an image
**Returns**: <code>Promise.&lt;EventEmitter&gt;</code> - initialization event emitter
**Access**: public
Expand Down
112 changes: 112 additions & 0 deletions src/device-type-json.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/* types for the /device-types/v1 endppoints */

export interface DeviceTypeJson {
slug: string;
name: string;
aliases: string[];

arch: string;
state?: string;
community?: boolean;
private?: boolean;

isDependent?: boolean;
imageDownloadAlerts?: DeviceTypeDownloadAlert[];
/** @deprecated Use the balena.models.deviceType.getInstructions() */
instructions?: string[] | DeviceTypeInstructions;
gettingStartedLink?: string | DeviceTypeGettingStartedLink;
stateInstructions?: { [key: string]: string[] };
options?: DeviceTypeOptions[];
/** Only 3 discontinued device types have this not defined, one of which is the generic */
initialization?: {
options?: DeviceInitializationOptions[];
operations: Array<{
command: string;
}>;
};
/** @deprecated Use the DeviceType.contract.data.led */
supportsBlink?: boolean;
yocto: {
fstype?: string;
deployArtifact: string;
machine?: string;
image?: string;
version?: string;
deployFlasherArtifact?: string;
deployRawArtifact?: string;
compressed?: boolean;
archive?: boolean;
};
/** Only generic and edge are missing this property, which are long deprecated */
configuration?: {
config: DeviceTypeConfigurationConfig;
};
/** Holds the latest balenaOS version */
buildId?: string;
/** @deprecated Use the logo field from the models.deviceType.get() method. */
logoUrl?: string;
}

export type DeviceTypeJsonWithConfiguration = DeviceTypeJson & {
configuration: object;
};

export interface DeviceTypeConfigurationConfig {
/** Only the intel-edison to not have this defined */
partition?:
| number
| {
primary?: number;
logical?: number;
};
/** eg "/config.json" */
path: string;
/** I only found this in the intel-edison eg "my/rpi.img" */
image?: string;
}

export interface DeviceTypeDownloadAlert {
type: string;
message: string;
}

export interface DeviceTypeInstructions {
linux: string[];
osx: string[];
windows: string[];
}

export interface DeviceTypeGettingStartedLink {
linux: string;
osx: string;
windows: string;
[key: string]: string;
}

export interface DeviceTypeOptions {
options: DeviceTypeOptionsGroup[];
collapsed: boolean;
isCollapsible: boolean;
isGroup: boolean;
message: string;
name: string;
}

export interface DeviceInitializationOptions {
message: string;
type: string;
name: string;
}

export interface DeviceTypeOptionsGroup {
default: number | string;
message: string;
name: string;
type: string;
min?: number;
max?: number;
hidden?: boolean;
when?: Record<string, number | string | boolean>;
choices?: string[] | number[];
choicesLabels?: Record<string, string>;
}
173 changes: 141 additions & 32 deletions src/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,75 @@ limitations under the License.
* @module init
*/

const _ = require('lodash');
const Promise = require('bluebird');
const operations = require('resin-device-operations');
const resinSemver = require('balena-semver');
const utils = require('./utils');
const network = require('./network');
import _ from 'lodash';
import Promise from 'bluebird';
import * as operations from 'resin-device-operations';
import * as balenaSemver from 'balena-semver';
import * as utils from './utils';
import * as network from './network';
import type { DeviceTypeJson } from './device-type-json';

exports.getImageManifest = utils.getImageManifest;
exports.getImageOsVersion = utils.getImageOsVersion;
export { getImageManifest, getImageOsVersion } from './utils';

export interface OperationState {
operation:
| CopyOperation
| ReplaceOperation
| RunScriptOperation
| BurnOperation;
percentage: number;
}

export interface Operation {
command: string;
}

export interface CopyOperation extends Operation {
command: 'copy';
from: { path: string };
to: { path: string };
}

export interface ReplaceOperation extends Operation {
command: 'replace';
copy: string;
replace: string;
file: {
path: string;
};
}

export interface RunScriptOperation extends Operation {
command: 'run-script';
script: string;
arguments?: string[];
}

export interface BurnOperation extends Operation {
command: 'burn';
image?: string;
}

export interface BurnProgress {
type: 'write' | 'check';
percentage: number;
transferred: number;
length: number;
remaining: number;
eta: number;
runtime: number;
delta: number;
speed: number;
}

export interface InitializeEmitter {
on(event: 'stdout' | 'stderr', callback: (msg: string) => void): void;
on(event: 'state', callback: (state: OperationState) => void): void;
on(event: 'burn', callback: (state: BurnProgress) => void): void;
on(event: 'end', callback: () => void): void;
on(event: 'close', callback: () => void): void;
on(event: 'error', callback: (error: Error) => void): void;
}

/**
* @summary Configure an image with an application
Expand Down Expand Up @@ -59,34 +119,72 @@ exports.getImageOsVersion = utils.getImageOsVersion;
* configuration.on 'end', ->
* console.log('Configuration finished')
*/
exports.configure = function(image, manifest, config, options) {
if (options == null) { options = {}; }
return Promise.try(function() {
export function configure(
image: string,
manifest: DeviceTypeJson,
config: Record<string, any>,
options: object = {},
): Promise<InitializeEmitter> {
return Promise.try(function () {
// We only know how to find /etc/os-release on specific types of OS image. In future, we'd like to be able
// to do this for any image, but for now we'll just treat others as unknowable (which means below we'll
// configure the network to work for _either_ OS version.
if (((manifest.yocto != null ? manifest.yocto.image : undefined) === 'resin-image') && _.includes(['resinos-img', 'resin-sdcard'], manifest.yocto != null ? manifest.yocto.fstype : undefined)) {
if (
manifest.yocto?.image === 'resin-image' &&
_.includes(['resinos-img', 'resin-sdcard'], manifest.yocto?.fstype)
) {
return utils.getImageOsVersion(image, manifest);
}}).then(function(osVersion) {
const {
configuration
} = manifest;

const majorVersion = resinSemver.major(osVersion);

const configPathDefinition = utils.convertFilePathDefinition(configuration.config);
return utils.writeConfigJSON(image, config, configPathDefinition)
.then(function() {
// Configure for OS2 if it is OS2, or if we're just not sure
if ((majorVersion == null) || (majorVersion === 2)) {
return network.configureOS2Network(image, manifest, options);
}}).then(function() {
// Configure for OS1 if it is OS1, or if we're just not sure
if ((majorVersion == null) || (majorVersion === 1)) {
return network.configureOS1Network(image, manifest, options);
}}).then(() => operations.execute(image, configuration.operations, options));
}
}).then(function (osVersion) {
if (manifest.configuration == null) {
throw new Error(
'Unsupported device type: Manifest missing configuration parameters',
);
}

const { configuration } = manifest;
// TS should be able to detect this on its own and we shoulnd't have to do it manually
const manifestWithConfiguration = manifest as typeof manifest & {
configuration: object;
};

const majorVersion = balenaSemver.major(osVersion);

const configPathDefinition = utils.convertFilePathDefinition(
configuration.config,
);
return utils
.writeConfigJSON(image, config, configPathDefinition)
.then(function () {
// Configure for OS2 if it is OS2, or if we're just not sure
if (majorVersion == null || majorVersion === 2) {
return network.configureOS2Network(
image,
manifestWithConfiguration,
options,
);
}
})
.then(function () {
// Configure for OS1 if it is OS1, or if we're just not sure
if (majorVersion == null || majorVersion === 1) {
return network.configureOS1Network(
image,
manifestWithConfiguration,
options,
);
}
})
.then(() =>
operations.execute(
image,
// @ts-expect-error TODO: Check whether this should be `manifest.initialization.operations` ?
configuration.operations,
options,
),
);
});
};
}

/**
* @summary Initialize an image
Expand Down Expand Up @@ -118,4 +216,15 @@ exports.configure = function(image, manifest, config, options) {
* configuration.on 'end', ->
* console.log('Configuration finished')
*/
exports.initialize = (image, manifest, options) => operations.execute(image, manifest.initialization.operations, options);
export function initialize(
image: string,
manifest: DeviceTypeJson,
options: object,
): Promise<InitializeEmitter> {
if (manifest.initialization == null) {
throw new Error(
'Unsupported device type: Manifest missing initialization parameters',
);
}
return operations.execute(image, manifest.initialization.operations, options);
}
Loading

0 comments on commit 3576d2c

Please sign in to comment.