/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); const fs = require("fs"); const crypto = require("crypto"); const storage_blob_1 = require("@azure/storage-blob"); const mime = require("mime"); const cosmos_1 = require("@azure/cosmos"); const identity_1 = require("@azure/identity"); const retry_1 = require("./retry"); if (process.argv.length !== 8) { console.error('Usage: node createAsset.js PRODUCT OS ARCH TYPE NAME FILE'); process.exit(-1); } // Contains all of the logic for mapping details to our actual product names in CosmosDB function getPlatform(product, os, arch, type) { switch (os) { case 'win32': switch (product) { case 'client': { const asset = arch === 'ia32' ? 'win32' : `win32-${arch}`; switch (type) { case 'archive': return `${asset}-archive`; case 'setup': return asset; case 'user-setup': return `${asset}-user`; default: throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); } } case 'server': if (arch === 'arm64') { throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); } return arch === 'ia32' ? 'server-win32' : `server-win32-${arch}`; case 'web': if (arch === 'arm64') { throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); } return arch === 'ia32' ? 'server-win32-web' : `server-win32-${arch}-web`; default: throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); } case 'alpine': switch (product) { case 'server': return `server-alpine-${arch}`; case 'web': return `server-alpine-${arch}-web`; default: throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); } case 'linux': switch (type) { case 'snap': return `linux-snap-${arch}`; case 'archive-unsigned': switch (product) { case 'client': return `linux-${arch}`; case 'server': return `server-linux-${arch}`; case 'web': return arch === 'standalone' ? 'web-standalone' : `server-linux-${arch}-web`; default: throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); } case 'deb-package': return `linux-deb-${arch}`; case 'rpm-package': return `linux-rpm-${arch}`; default: throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); } case 'darwin': switch (product) { case 'client': if (arch === 'x64') { return 'darwin'; } return `darwin-${arch}`; case 'server': return 'server-darwin'; case 'web': if (arch !== 'x64') { throw new Error(`What should the platform be?: ${product} ${os} ${arch} ${type}`); } return 'server-darwin-web'; default: throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); } default: throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); } } // Contains all of the logic for mapping types to our actual types in CosmosDB function getRealType(type) { switch (type) { case 'user-setup': return 'setup'; case 'deb-package': case 'rpm-package': return 'package'; default: return type; } } function hashStream(hashName, stream) { return new Promise((c, e) => { const shasum = crypto.createHash(hashName); stream .on('data', shasum.update.bind(shasum)) .on('error', e) .on('close', () => c(shasum.digest('hex'))); }); } function getEnv(name) { const result = process.env[name]; if (typeof result === 'undefined') { throw new Error('Missing env: ' + name); } return result; } async function main() { const [, , product, os, arch, unprocessedType, fileName, filePath] = process.argv; // getPlatform needs the unprocessedType const platform = getPlatform(product, os, arch, unprocessedType); const type = getRealType(unprocessedType); const quality = getEnv('VSCODE_QUALITY'); const commit = getEnv('BUILD_SOURCEVERSION'); console.log('Creating asset...'); const stat = await new Promise((c, e) => fs.stat(filePath, (err, stat) => err ? e(err) : c(stat))); const size = stat.size; console.log('Size:', size); const stream = fs.createReadStream(filePath); const [sha1hash, sha256hash] = await Promise.all([hashStream('sha1', stream), hashStream('sha256', stream)]); console.log('SHA1:', sha1hash); console.log('SHA256:', sha256hash); const blobName = commit + '/' + fileName; const storagePipelineOptions = { retryOptions: { retryPolicyType: storage_blob_1.StorageRetryPolicyType.EXPONENTIAL, maxTries: 6, tryTimeoutInMs: 10 * 60 * 1000 } }; const credential = new identity_1.ClientSecretCredential(process.env['AZURE_TENANT_ID'], process.env['AZURE_CLIENT_ID'], process.env['AZURE_CLIENT_SECRET']); const blobServiceClient = new storage_blob_1.BlobServiceClient(`https://vscode.blob.core.windows.net`, credential, storagePipelineOptions); const containerClient = blobServiceClient.getContainerClient(quality); const blobClient = containerClient.getBlockBlobClient(blobName); const blobExists = await blobClient.exists(); if (blobExists) { console.log(`Blob ${quality}, ${blobName} already exists, not publishing again.`); return; } const mooncakeCredential = new identity_1.ClientSecretCredential(process.env['AZURE_MOONCAKE_TENANT_ID'], process.env['AZURE_MOONCAKE_CLIENT_ID'], process.env['AZURE_MOONCAKE_CLIENT_SECRET']); const mooncakeBlobServiceClient = new storage_blob_1.BlobServiceClient(`https://vscode.blob.core.chinacloudapi.cn`, mooncakeCredential, storagePipelineOptions); const mooncakeContainerClient = mooncakeBlobServiceClient.getContainerClient(quality); const mooncakeBlobClient = mooncakeContainerClient.getBlockBlobClient(blobName); console.log('Uploading blobs to Azure storage and Mooncake Azure storage...'); const blobOptions = { blobHTTPHeaders: { blobContentType: mime.lookup(filePath), blobContentDisposition: `attachment; filename="${fileName}"`, blobCacheControl: 'max-age=31536000, public' } }; await (0, retry_1.retry)(() => Promise.all([ blobClient.uploadFile(filePath, blobOptions), mooncakeBlobClient.uploadFile(filePath, blobOptions) ])); console.log('Blobs successfully uploaded.'); const assetUrl = `${process.env['AZURE_CDN_URL']}/${quality}/${blobName}`; const blobPath = new URL(assetUrl).pathname; const mooncakeUrl = `${process.env['MOONCAKE_CDN_URL']}${blobPath}`; const asset = { platform, type, url: assetUrl, hash: sha1hash, mooncakeUrl, sha256hash, size }; // Remove this if we ever need to rollback fast updates for windows if (/win32/.test(platform)) { asset.supportsFastUpdate = true; } console.log('Asset:', JSON.stringify(asset, null, ' ')); const client = new cosmos_1.CosmosClient({ endpoint: process.env['AZURE_DOCUMENTDB_ENDPOINT'], aadCredentials: credential }); const scripts = client.database('builds').container(quality).scripts; await (0, retry_1.retry)(() => scripts.storedProcedure('createAsset').execute('', [commit, asset, true])); console.log(` Done ✔️`); } main().then(() => { console.log('Asset successfully created'); process.exit(0); }, err => { console.error(err); process.exit(1); });