UNPKG

balena-cli

Version:

The official balena Command Line Interface

115 lines 5.16 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getStream = exports.getImageWritableStream = exports.getImage = exports.isESR = exports.isImageFresh = 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) => { 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}.${extension}`); }; exports.getImagePath = getImagePath; const isImageFresh = async (deviceType, version) => { const imagePath = await (0, exports.getImagePath)(deviceType, version); let createdDate; try { createdDate = await (0, exports.getFileCreatedDate)(imagePath); } catch (_a) { } if (createdDate == null) { return false; } const balena = (0, lazy_1.getBalenaSdk)(); const lastModifiedDate = await balena.models.os.getLastModified(deviceType, version); return lastModifiedDate < createdDate; }; exports.isImageFresh = isImageFresh; const isESR = (version) => { const match = version.match(/^v?(\d+)\.\d+\.\d+/); const major = parseInt((match && match[1]) || '', 10); return major >= 2018; }; exports.isESR = isESR; const resolveVersion = async (deviceType, versionOrRange) => { const balena = (0, lazy_1.getBalenaSdk)(); const version = await balena.models.os.getMaxSatisfyingVersion(deviceType, versionOrRange, (0, exports.isESR)(versionOrRange) ? 'esr' : 'default'); if (!version) { throw new Error('No such version for the device type'); } return version; }; const getImage = async (deviceType, version) => { var _a; const imagePath = await (0, exports.getImagePath)(deviceType, version); 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) => { const imagePath = await (0, exports.getImagePath)(deviceType, version); const path = await Promise.resolve().then(() => require('path')); const { mkdirp } = await Promise.resolve().then(() => require('mkdirp')); await mkdirp(path.dirname(imagePath)); const inProgressPath = imagePath + '.inprogress'; const { promises, createWriteStream } = await Promise.resolve().then(() => require('fs')); const stream = createWriteStream(inProgressPath); stream.persistCache = () => promises.rename(inProgressPath, imagePath); stream.removeCache = () => promises.unlink(inProgressPath); return stream; }; exports.getImageWritableStream = getImageWritableStream; const doDownload = async (options) => { const balena = (0, lazy_1.getBalenaSdk)(); const imageStream = await balena.models.os.download(options); const { PassThrough } = await Promise.resolve().then(() => require('stream')); const pass = new PassThrough(); imageStream.pipe(pass); const cacheStream = await (0, exports.getImageWritableStream)(options.deviceType, options.version); 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 = {}) => { if (versionOrRange == null) { versionOrRange = 'latest'; } const version = await resolveVersion(deviceType, versionOrRange); const isFresh = await (0, exports.isImageFresh)(deviceType, version); const $stream = isFresh ? await (0, exports.getImage)(deviceType, version) : await doDownload({ ...options, deviceType, version }); setImmediate(() => $stream.emit('balena-image-manager:resolved-version', version)); return $stream; }; exports.getStream = getStream; //# sourceMappingURL=image-manager.js.map