@microsoft/kiota
Version:
npm package exposing Kiota CLI functionality to TypeScript
160 lines • 6.81 kB
JavaScript
import AdmZip from 'adm-zip';
import { createHash } from 'crypto';
import * as https from 'https';
import * as fs from 'fs';
import * as path from 'path';
import { getKiotaConfig } from './config';
import runtimeJson from './runtime.json';
const kiotaInstallStatusKey = "kiotaInstallStatus";
const installDelayInMs = 30000; // 30 seconds
const state = {};
let kiotaPath;
const binariesRootDirectory = '.kiotabin';
const baseDownloadUrl = "https://github.com/microsoft/kiota/releases/download";
const windowsPlatform = 'win';
const osxPlatform = 'osx';
const linuxPlatform = 'linux';
async function runIfNotLocked(action) {
const installStartTimeStamp = state[kiotaInstallStatusKey];
const currentTimeStamp = new Date().getTime();
if (!installStartTimeStamp || (currentTimeStamp - installStartTimeStamp) > installDelayInMs) {
//locking the context to prevent multiple downloads across multiple instances
//overriding after 30 seconds to prevent stale locks
state[kiotaInstallStatusKey] = currentTimeStamp;
try {
await action();
}
finally {
state[kiotaInstallStatusKey] = undefined;
}
}
}
export async function ensureKiotaIsPresent() {
const installPath = getKiotaPathInternal(false);
if (installPath) {
const runtimeDependencies = getRuntimeDependenciesPackages();
const currentPlatform = getCurrentPlatform();
await ensureKiotaIsPresentInPath(installPath, runtimeDependencies, currentPlatform);
}
}
export async function ensureKiotaIsPresentInPath(installPath, runtimeDependencies, currentPlatform) {
if (installPath) {
if (!fs.existsSync(installPath) || fs.readdirSync(installPath).length === 0) {
await runIfNotLocked(async () => {
try {
const packageToInstall = runtimeDependencies.find((p) => p.platformId === currentPlatform);
if (!packageToInstall) {
throw new Error("Could not find package to install");
}
fs.mkdirSync(installPath, { recursive: true });
const zipFilePath = `${installPath}.zip`;
// If env variable that points to kiota binary zip exists, use it to copy the file instead of downloading it
const kiotaBinaryZip = process.env.KIOTA_SIDELOADING_BINARY_ZIP_PATH;
if (kiotaBinaryZip && fs.existsSync(kiotaBinaryZip)) {
fs.copyFileSync(kiotaBinaryZip, zipFilePath);
}
else {
const downloadUrl = getDownloadUrl(currentPlatform);
await downloadFileFromUrl(downloadUrl, zipFilePath);
if (!await doesFileHashMatch(zipFilePath, packageToInstall.sha256)) {
throw new Error("Hash validation of the downloaded file mismatch");
}
}
unzipFile(zipFilePath, installPath);
if ((currentPlatform.startsWith(linuxPlatform) || currentPlatform.startsWith(osxPlatform)) && installPath) {
const fileName = getKiotaFileName();
const kiotaFilePath = path.join(installPath, fileName);
makeExecutable(kiotaFilePath);
}
}
catch (error) {
fs.rmdirSync(installPath, { recursive: true });
throw new Error("Kiota download failed. Check the logs for more information.");
}
});
}
}
}
export function getKiotaPath() {
if (!kiotaPath) {
kiotaPath = getKiotaPathInternal();
if (!kiotaPath) {
throw new Error("Could not find kiota");
}
}
return kiotaPath;
}
function makeExecutable(path) {
fs.chmodSync(path, 0o755);
}
function getBaseDir() {
return getKiotaConfig().binaryLocation || path.resolve(__dirname);
}
function getRuntimeVersion() {
return getKiotaConfig().binaryVersion || runtimeJson.kiotaVersion;
}
function getKiotaFileName() {
return process.platform === 'win32' ? 'kiota.exe' : 'kiota';
}
function getKiotaPathInternal(withFileName = true) {
const fileName = getKiotaFileName();
const runtimeDependencies = getRuntimeDependenciesPackages();
const currentPlatform = getCurrentPlatform();
const packageToInstall = runtimeDependencies.find((p) => p.platformId === currentPlatform);
const baseDir = getBaseDir();
const runtimeVersion = getRuntimeVersion();
if (packageToInstall) {
const installPath = path.join(baseDir, binariesRootDirectory);
const directoryPath = path.join(installPath, runtimeVersion, currentPlatform);
if (withFileName) {
return path.join(directoryPath, fileName);
}
return directoryPath;
}
return undefined;
}
function unzipFile(zipFilePath, destinationPath) {
const zip = new AdmZip(zipFilePath);
zip.extractAllTo(destinationPath, true);
}
async function doesFileHashMatch(destinationPath, hashValue) {
const hash = createHash('sha256');
return new Promise((resolve, reject) => {
fs.createReadStream(destinationPath).pipe(hash).on('finish', () => {
const computedValue = hash.digest('hex');
hash.destroy();
resolve(computedValue.toUpperCase() === hashValue.toUpperCase());
});
});
}
function downloadFileFromUrl(url, destinationPath) {
return new Promise((resolve) => {
https.get(url, (response) => {
if (response.statusCode && response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
resolve(downloadFileFromUrl(response.headers.location, destinationPath));
}
else {
const filePath = fs.createWriteStream(destinationPath);
response.pipe(filePath);
filePath.on('finish', () => {
filePath.close();
resolve(undefined);
});
}
});
});
}
function getDownloadUrl(platform) {
return `${baseDownloadUrl}/v${getRuntimeVersion()}/${platform}.zip`;
}
export function getRuntimeDependenciesPackages() {
if (runtimeJson.runtimeDependencies) {
return JSON.parse(JSON.stringify(runtimeJson.runtimeDependencies));
}
throw new Error("No runtime dependencies found");
}
export function getCurrentPlatform() {
const binPathSegmentOS = process.platform === 'win32' ? windowsPlatform : process.platform === 'darwin' ? osxPlatform : linuxPlatform;
return `${binPathSegmentOS}-${process.arch}`;
}
//# sourceMappingURL=install.js.map