balena-cli
Version:
The official balena Command Line Interface
111 lines • 5.35 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getStream = exports.getImageWritableStream = exports.getImage = exports.resolveVersion = exports.isImageCached = exports.getImagePath = exports.getFileCreatedDate = void 0;
const lazy_1 = require("./lazy");
const BALENAOS_VERSION_REGEX = /v?\d+\.\d+\.\d+(\.rev\d+)?((\-|\+).+)?/;
const validateVersion = (version) => {
if (!BALENAOS_VERSION_REGEX.test(version)) {
throw new Error('Invalid version number');
}
};
const getFileCreatedDate = async (filePath) => {
const { promises: fs } = await Promise.resolve().then(() => require('fs'));
const { ctime } = await fs.stat(filePath);
return ctime;
};
exports.getFileCreatedDate = getFileCreatedDate;
const getImagePath = async (deviceType, version, type) => {
if (typeof version === 'string') {
validateVersion(version);
}
const balena = (0, lazy_1.getBalenaSdk)();
const [cacheDirectory, deviceTypeInfo] = await Promise.all([
balena.settings.get('cacheDirectory'),
balena.models.config.getDeviceTypeManifestBySlug(deviceType),
]);
const extension = deviceTypeInfo.yocto.fstype === 'zip' ? 'zip' : 'img';
const path = await Promise.resolve().then(() => require('path'));
return path.join(cacheDirectory, `${deviceType}-v${version}${type != null ? `-${type}` : ''}.${extension}`);
};
exports.getImagePath = getImagePath;
const isImageCached = async (deviceType, version, type) => {
const imagePath = await (0, exports.getImagePath)(deviceType, version, type);
try {
const createdDate = await (0, exports.getFileCreatedDate)(imagePath);
return createdDate != null;
}
catch (_a) {
return false;
}
};
exports.isImageCached = isImageCached;
const resolveVersion = async (deviceType, versionOrRange) => {
const balena = (0, lazy_1.getBalenaSdk)();
const { getOsType } = await Promise.resolve().then(() => require('./os'));
const version = await balena.models.os.getMaxSatisfyingVersion(deviceType, versionOrRange, getOsType(versionOrRange));
if (!version) {
const { OSVersionNotFoundError } = await Promise.resolve().then(() => require('../errors'));
throw new OSVersionNotFoundError(`Version ${versionOrRange} is not available for the device type ${deviceType}`);
}
return version;
};
exports.resolveVersion = resolveVersion;
const getImage = async (deviceType, version, type) => {
var _a;
const imagePath = await (0, exports.getImagePath)(deviceType, version, type);
const fs = await Promise.resolve().then(() => require('fs'));
const stream = fs.createReadStream(imagePath);
const { getType } = await Promise.resolve().then(() => require('mime'));
stream.mime = (_a = getType(imagePath)) !== null && _a !== void 0 ? _a : 'application/octet-stream';
return stream;
};
exports.getImage = getImage;
const getImageWritableStream = async (deviceType, version, type) => {
const imagePath = await (0, exports.getImagePath)(deviceType, version, type);
const path = await Promise.resolve().then(() => require('path'));
const { promises: fs, createWriteStream } = await Promise.resolve().then(() => require('node:fs'));
await fs.mkdir(path.dirname(imagePath), { recursive: true });
const inProgressPath = imagePath + '.inprogress';
const stream = createWriteStream(inProgressPath);
stream.persistCache = () => fs.rename(inProgressPath, imagePath);
stream.removeCache = () => fs.unlink(inProgressPath);
return stream;
};
exports.getImageWritableStream = getImageWritableStream;
const imageTypeToSdkParam = {
'installation-media': 'flasher',
'disk-image': 'raw',
};
const doDownload = async ({ type, ...restOptions }) => {
const balena = (0, lazy_1.getBalenaSdk)();
const imageStream = await balena.models.os.download({
...restOptions,
imageType: type ? imageTypeToSdkParam[type] : undefined,
});
const { PassThrough } = await Promise.resolve().then(() => require('stream'));
const pass = new PassThrough();
imageStream.pipe(pass);
const cacheStream = await (0, exports.getImageWritableStream)(restOptions.deviceType, restOptions.version, type);
pass.pipe(cacheStream, { end: false });
pass.on('end', cacheStream.persistCache);
const pass2 = new PassThrough();
pass2.mime = imageStream.mime;
imageStream.on('progress', (state) => pass2.emit('progress', state));
imageStream.on('error', async (err) => {
await cacheStream.removeCache();
pass2.emit('error', err);
});
return pass.pipe(pass2);
};
const getStream = async (deviceType, versionOrRange, options = {}) => {
versionOrRange !== null && versionOrRange !== void 0 ? versionOrRange : (versionOrRange = 'latest');
const version = await (0, exports.resolveVersion)(deviceType, versionOrRange);
const existsInCache = await (0, exports.isImageCached)(deviceType, version, options.type);
const $stream = existsInCache
? await (0, exports.getImage)(deviceType, version, options.type)
: await doDownload({ ...options, deviceType, version });
setImmediate(() => $stream.emit('balena-image-manager:resolved-version', version));
return $stream;
};
exports.getStream = getStream;
//# sourceMappingURL=image-manager.js.map