diff --git a/build/azure-pipelines/cli/cli-darwin-sign.yml b/build/azure-pipelines/cli/cli-darwin-sign.yml index b4cfdc8f10f..925d8435dae 100644 --- a/build/azure-pipelines/cli/cli-darwin-sign.yml +++ b/build/azure-pipelines/cli/cli-darwin-sign.yml @@ -26,10 +26,10 @@ steps: artifact: ${{ target }} path: $(Build.ArtifactStagingDirectory)/pkg/${{ target }} - - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll darwin-sign $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(Build.ArtifactStagingDirectory)/pkg "*.zip" + - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll sign-darwin $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(Build.ArtifactStagingDirectory)/pkg "*.zip" displayName: Codesign - - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll darwin-notarize $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(Build.ArtifactStagingDirectory)/pkg "*.zip" + - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll notarize-darwin $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(Build.ArtifactStagingDirectory)/pkg "*.zip" displayName: Notarize - ${{ each target in parameters.VSCODE_CLI_ARTIFACTS }}: diff --git a/build/azure-pipelines/cli/cli-win32-sign.yml b/build/azure-pipelines/cli/cli-win32-sign.yml index 2880eafb85d..10d305b92b3 100644 --- a/build/azure-pipelines/cli/cli-win32-sign.yml +++ b/build/azure-pipelines/cli/cli-win32-sign.yml @@ -42,8 +42,8 @@ steps: echo "##vso[task.setvariable variable=EsrpCliDllPath]$EsrpCliDllPath" displayName: Find ESRP CLI - - powershell: node build\azure-pipelines\common\sign $env:EsrpCliDllPath windows $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(Build.ArtifactStagingDirectory)/sign "*.exe" - displayName: Codesign executable + - powershell: node build\azure-pipelines\common\sign $env:EsrpCliDllPath sign-windows $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(Build.ArtifactStagingDirectory)/sign "*.exe" + displayName: Codesign - ${{ each target in parameters.VSCODE_CLI_ARTIFACTS }}: - powershell: | diff --git a/build/azure-pipelines/common/createAsset.js b/build/azure-pipelines/common/createAsset.js deleted file mode 100644 index 5128f607b6a..00000000000 --- a/build/azure-pipelines/common/createAsset.js +++ /dev/null @@ -1,243 +0,0 @@ -"use strict"; -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -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': { - switch (type) { - case 'archive': - return `win32-${arch}-archive`; - case 'setup': - return `win32-${arch}`; - case 'user-setup': - return `win32-${arch}-user`; - default: - throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); - } - } - case 'server': - if (arch === 'arm64') { - throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); - } - return `server-win32-${arch}`; - case 'web': - if (arch === 'arm64') { - throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); - } - return `server-win32-${arch}-web`; - case 'cli': - return `cli-win32-${arch}`; - 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`; - case 'cli': - return `cli-alpine-${arch}`; - 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}`; - case 'cli': - return `cli-linux-${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': - if (arch === 'x64') { - return 'server-darwin'; - } - return `server-darwin-${arch}`; - case 'web': - if (arch === 'x64') { - return 'server-darwin-web'; - } - return `server-darwin-${arch}-web`; - case 'cli': - return `cli-darwin-${arch}`; - 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 blobOptions = { - blobHTTPHeaders: { - blobContentType: mime.lookup(filePath), - blobContentDisposition: `attachment; filename="${fileName}"`, - blobCacheControl: 'max-age=31536000, public' - } - }; - const uploadPromises = []; - uploadPromises.push((async () => { - console.log(`Checking for blob in Azure...`); - if (await (0, retry_1.retry)(() => blobClient.exists())) { - throw new Error(`Blob ${quality}, ${blobName} already exists, not publishing again.`); - } - else { - await (0, retry_1.retry)(async (attempt) => { - console.log(`Uploading blobs to Azure storage (attempt ${attempt})...`); - await blobClient.uploadFile(filePath, blobOptions); - console.log('Blob successfully uploaded to Azure storage.'); - }); - } - })()); - const shouldUploadToMooncake = /true/i.test(process.env['VSCODE_PUBLISH_TO_MOONCAKE'] ?? 'true'); - if (shouldUploadToMooncake) { - 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); - uploadPromises.push((async () => { - console.log(`Checking for blob in Mooncake Azure...`); - if (await (0, retry_1.retry)(() => mooncakeBlobClient.exists())) { - throw new Error(`Mooncake Blob ${quality}, ${blobName} already exists, not publishing again.`); - } - else { - await (0, retry_1.retry)(async (attempt) => { - console.log(`Uploading blobs to Mooncake Azure storage (attempt ${attempt})...`); - await mooncakeBlobClient.uploadFile(filePath, blobOptions); - console.log('Blob successfully uploaded to Mooncake Azure storage.'); - }); - } - })()); - } - const promiseResults = await Promise.allSettled(uploadPromises); - const rejectedPromiseResults = promiseResults.filter(result => result.status === 'rejected'); - if (rejectedPromiseResults.length === 0) { - console.log('All blobs successfully uploaded.'); - } - else if (rejectedPromiseResults[0]?.reason?.message?.includes('already exists')) { - console.warn(rejectedPromiseResults[0].reason.message); - console.log('Some blobs successfully uploaded.'); - } - else { - // eslint-disable-next-line no-throw-literal - throw rejectedPromiseResults[0]?.reason; - } - 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); -}); -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlQXNzZXQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjcmVhdGVBc3NldC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7O0FBRWhHLHlCQUF5QjtBQUV6QixpQ0FBaUM7QUFDakMsc0RBQXdJO0FBQ3hJLDZCQUE2QjtBQUM3QiwwQ0FBNkM7QUFDN0MsOENBQXlEO0FBQ3pELG1DQUFnQztBQWFoQyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO0lBQy9CLE9BQU8sQ0FBQyxLQUFLLENBQUMsMkRBQTJELENBQUMsQ0FBQztJQUMzRSxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDbEIsQ0FBQztBQUVELHdGQUF3RjtBQUN4RixTQUFTLFdBQVcsQ0FBQyxPQUFlLEVBQUUsRUFBVSxFQUFFLElBQVksRUFBRSxJQUFZO0lBQzNFLFFBQVEsRUFBRSxFQUFFLENBQUM7UUFDWixLQUFLLE9BQU87WUFDWCxRQUFRLE9BQU8sRUFBRSxDQUFDO2dCQUNqQixLQUFLLFFBQVEsQ0FBQyxDQUFDLENBQUM7b0JBQ2YsUUFBUSxJQUFJLEVBQUUsQ0FBQzt3QkFDZCxLQUFLLFNBQVM7NEJBQ2IsT0FBTyxTQUFTLElBQUksVUFBVSxDQUFDO3dCQUNoQyxLQUFLLE9BQU87NEJBQ1gsT0FBTyxTQUFTLElBQUksRUFBRSxDQUFDO3dCQUN4QixLQUFLLFlBQVk7NEJBQ2hCLE9BQU8sU0FBUyxJQUFJLE9BQU8sQ0FBQzt3QkFDN0I7NEJBQ0MsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQkFBaUIsT0FBTyxJQUFJLEVBQUUsSUFBSSxJQUFJLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQztvQkFDcEUsQ0FBQztnQkFDRixDQUFDO2dCQUNELEtBQUssUUFBUTtvQkFDWixJQUFJLElBQUksS0FBSyxPQUFPLEVBQUUsQ0FBQzt3QkFDdEIsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQkFB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 \ No newline at end of file diff --git a/build/azure-pipelines/common/createAsset.ts b/build/azure-pipelines/common/createAsset.ts deleted file mode 100644 index ee08d4ae6a5..00000000000 --- a/build/azure-pipelines/common/createAsset.ts +++ /dev/null @@ -1,283 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as fs from 'fs'; -import { Readable } from 'stream'; -import * as crypto from 'crypto'; -import { BlobServiceClient, BlockBlobParallelUploadOptions, StoragePipelineOptions, StorageRetryPolicyType } from '@azure/storage-blob'; -import * as mime from 'mime'; -import { CosmosClient } from '@azure/cosmos'; -import { ClientSecretCredential } from '@azure/identity'; -import { retry } from './retry'; - -interface Asset { - platform: string; - type: string; - url: string; - mooncakeUrl?: string; - hash: string; - sha256hash: string; - size: number; - supportsFastUpdate?: boolean; -} - -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: string, os: string, arch: string, type: string): string { - switch (os) { - case 'win32': - switch (product) { - case 'client': { - switch (type) { - case 'archive': - return `win32-${arch}-archive`; - case 'setup': - return `win32-${arch}`; - case 'user-setup': - return `win32-${arch}-user`; - default: - throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); - } - } - case 'server': - if (arch === 'arm64') { - throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); - } - return `server-win32-${arch}`; - case 'web': - if (arch === 'arm64') { - throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); - } - return `server-win32-${arch}-web`; - case 'cli': - return `cli-win32-${arch}`; - 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`; - case 'cli': - return `cli-alpine-${arch}`; - 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}`; - case 'cli': - return `cli-linux-${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': - if (arch === 'x64') { - return 'server-darwin'; - } - return `server-darwin-${arch}`; - case 'web': - if (arch === 'x64') { - return 'server-darwin-web'; - } - return `server-darwin-${arch}-web`; - case 'cli': - return `cli-darwin-${arch}`; - 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: string) { - switch (type) { - case 'user-setup': - return 'setup'; - case 'deb-package': - case 'rpm-package': - return 'package'; - default: - return type; - } -} - -function hashStream(hashName: string, stream: Readable): Promise { - 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: string): string { - const result = process.env[name]; - - if (typeof result === 'undefined') { - throw new Error('Missing env: ' + name); - } - - return result; -} - -async function main(): Promise { - 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: StoragePipelineOptions = { retryOptions: { retryPolicyType: StorageRetryPolicyType.EXPONENTIAL, maxTries: 6, tryTimeoutInMs: 10 * 60 * 1000 } }; - - const credential = new ClientSecretCredential(process.env['AZURE_TENANT_ID']!, process.env['AZURE_CLIENT_ID']!, process.env['AZURE_CLIENT_SECRET']!); - const blobServiceClient = new BlobServiceClient(`https://vscode.blob.core.windows.net`, credential, storagePipelineOptions); - const containerClient = blobServiceClient.getContainerClient(quality); - const blobClient = containerClient.getBlockBlobClient(blobName); - - const blobOptions: BlockBlobParallelUploadOptions = { - blobHTTPHeaders: { - blobContentType: mime.lookup(filePath), - blobContentDisposition: `attachment; filename="${fileName}"`, - blobCacheControl: 'max-age=31536000, public' - } - }; - - const uploadPromises: Promise[] = []; - - uploadPromises.push((async () => { - console.log(`Checking for blob in Azure...`); - - if (await retry(() => blobClient.exists())) { - throw new Error(`Blob ${quality}, ${blobName} already exists, not publishing again.`); - } else { - await retry(async (attempt) => { - console.log(`Uploading blobs to Azure storage (attempt ${attempt})...`); - await blobClient.uploadFile(filePath, blobOptions); - console.log('Blob successfully uploaded to Azure storage.'); - }); - } - })()); - - const shouldUploadToMooncake = /true/i.test(process.env['VSCODE_PUBLISH_TO_MOONCAKE'] ?? 'true'); - - if (shouldUploadToMooncake) { - const mooncakeCredential = new ClientSecretCredential(process.env['AZURE_MOONCAKE_TENANT_ID']!, process.env['AZURE_MOONCAKE_CLIENT_ID']!, process.env['AZURE_MOONCAKE_CLIENT_SECRET']!); - const mooncakeBlobServiceClient = new BlobServiceClient(`https://vscode.blob.core.chinacloudapi.cn`, mooncakeCredential, storagePipelineOptions); - const mooncakeContainerClient = mooncakeBlobServiceClient.getContainerClient(quality); - const mooncakeBlobClient = mooncakeContainerClient.getBlockBlobClient(blobName); - - uploadPromises.push((async () => { - console.log(`Checking for blob in Mooncake Azure...`); - - if (await retry(() => mooncakeBlobClient.exists())) { - throw new Error(`Mooncake Blob ${quality}, ${blobName} already exists, not publishing again.`); - } else { - await retry(async (attempt) => { - console.log(`Uploading blobs to Mooncake Azure storage (attempt ${attempt})...`); - await mooncakeBlobClient.uploadFile(filePath, blobOptions); - console.log('Blob successfully uploaded to Mooncake Azure storage.'); - }); - } - })()); - } - - const promiseResults = await Promise.allSettled(uploadPromises); - const rejectedPromiseResults = promiseResults.filter(result => result.status === 'rejected') as PromiseRejectedResult[]; - - if (rejectedPromiseResults.length === 0) { - console.log('All blobs successfully uploaded.'); - } else if (rejectedPromiseResults[0]?.reason?.message?.includes('already exists')) { - console.warn(rejectedPromiseResults[0].reason.message); - console.log('Some blobs successfully uploaded.'); - } else { - // eslint-disable-next-line no-throw-literal - throw rejectedPromiseResults[0]?.reason; - } - - 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: 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 CosmosClient({ endpoint: process.env['AZURE_DOCUMENTDB_ENDPOINT']!, aadCredentials: credential }); - const scripts = client.database('builds').container(quality).scripts; - await 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); -}); diff --git a/build/azure-pipelines/common/publish.js b/build/azure-pipelines/common/publish.js new file mode 100644 index 00000000000..33ccbdba908 --- /dev/null +++ b/build/azure-pipelines/common/publish.js @@ -0,0 +1,655 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +const fs = require("fs"); +const path = require("path"); +const node_fetch_1 = require("node-fetch"); +const promises_1 = require("node:stream/promises"); +const yauzl = require("yauzl"); +const crypto = require("crypto"); +const retry_1 = require("./retry"); +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 cp = require("child_process"); +const os = require("os"); +function e(name) { + const result = process.env[name]; + if (typeof result !== 'string') { + throw new Error(`Missing env: ${name}`); + } + return result; +} +class Temp { + _files = []; + tmpNameSync() { + const file = path.join(os.tmpdir(), crypto.randomBytes(20).toString('hex')); + this._files.push(file); + return file; + } + dispose() { + for (const file of this._files) { + try { + fs.unlinkSync(file); + } + catch (err) { + // noop + } + } + } +} +class Sequencer { + current = Promise.resolve(null); + queue(promiseTask) { + return this.current = this.current.then(() => promiseTask(), () => promiseTask()); + } +} +class ProvisionService { + log; + accessToken; + constructor(log, accessToken) { + this.log = log; + this.accessToken = accessToken; + } + async provision(releaseId, fileId, fileName) { + const body = JSON.stringify({ + ReleaseId: releaseId, + PortalName: 'VSCode', + PublisherCode: 'VSCode', + ProvisionedFilesCollection: [{ + PublisherKey: fileId, + IsStaticFriendlyFileName: true, + FriendlyFileName: fileName, + MaxTTL: '1440', + CdnMappings: ['ECN'] + }] + }); + this.log(`Provisioning ${fileName} (releaseId: ${releaseId}, fileId: ${fileId})...`); + const res = await (0, retry_1.retry)(() => this.request('POST', '/api/v2/ProvisionedFiles/CreateProvisionedFiles', { body })); + if (!res.IsSuccess) { + throw new Error(`Failed to submit provisioning request: ${JSON.stringify(res.ErrorDetails)}`); + } + this.log(`Successfully provisioned ${fileName}`); + } + async request(method, url, options) { + const opts = { + method, + body: options?.body, + headers: { + Authorization: `Bearer ${this.accessToken}`, + 'Content-Type': 'application/json' + } + }; + const res = await (0, node_fetch_1.default)(`https://dsprovisionapi.microsoft.com${url}`, opts); + if (!res.ok || res.status < 200 || res.status >= 500) { + throw new Error(`Unexpected status code: ${res.status}`); + } + return await res.json(); + } +} +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'))); + }); +} +class ESRPClient { + log; + tmp; + static Sequencer = new Sequencer(); + authPath; + constructor(log, tmp, tenantId, clientId, authCertSubjectName, requestSigningCertSubjectName) { + this.log = log; + this.tmp = tmp; + this.authPath = this.tmp.tmpNameSync(); + fs.writeFileSync(this.authPath, JSON.stringify({ + Version: '1.0.0', + AuthenticationType: 'AAD_CERT', + TenantId: tenantId, + ClientId: clientId, + AuthCert: { + SubjectName: authCertSubjectName, + StoreLocation: 'LocalMachine', + StoreName: 'My', + SendX5c: 'true' + }, + RequestSigningCert: { + SubjectName: requestSigningCertSubjectName, + StoreLocation: 'LocalMachine', + StoreName: 'My' + } + })); + } + async release(version, filePath) { + const submitReleaseResult = await ESRPClient.Sequencer.queue(async () => { + this.log(`Submitting release for ${version}: ${filePath}`); + return await this.SubmitRelease(version, filePath); + }); + if (submitReleaseResult.submissionResponse.statusCode !== 'pass') { + throw new Error(`Unexpected status code: ${submitReleaseResult.submissionResponse.statusCode}`); + } + const releaseId = submitReleaseResult.submissionResponse.operationId; + this.log(`Successfully submitted release ${releaseId}. Polling for completion...`); + let details; + // Poll every 5 seconds, wait 60 minutes max -> poll 60/5*60=720 times + for (let i = 0; i < 720; i++) { + details = await this.ReleaseDetails(releaseId); + if (details.releaseDetails[0].statusCode === 'pass') { + break; + } + else if (details.releaseDetails[0].statusCode !== 'inprogress') { + throw new Error(`Failed to submit release: ${JSON.stringify(details)}`); + } + await new Promise(c => setTimeout(c, 5000)); + } + if (details.releaseDetails[0].statusCode !== 'pass') { + throw new Error(`Timed out waiting for release ${releaseId}: ${JSON.stringify(details)}`); + } + const fileId = details.releaseDetails[0].fileDetails[0].publisherKey; + this.log('Release completed successfully with fileId: ', fileId); + return { releaseId, fileId }; + } + async SubmitRelease(version, filePath) { + const policyPath = this.tmp.tmpNameSync(); + fs.writeFileSync(policyPath, JSON.stringify({ + Version: '1.0.0', + Audience: 'InternalLimited', + Intent: 'distribution', + ContentType: 'InstallPackage' + })); + const inputPath = this.tmp.tmpNameSync(); + const size = fs.statSync(filePath).size; + const istream = fs.createReadStream(filePath); + const sha256 = await hashStream('sha256', istream); + fs.writeFileSync(inputPath, JSON.stringify({ + Version: '1.0.0', + ReleaseInfo: { + ReleaseMetadata: { + Title: 'VS Code', + Properties: { + ReleaseContentType: 'InstallPackage' + }, + MinimumNumberOfApprovers: 1 + }, + ProductInfo: { + Name: 'VS Code', + Version: version, + Description: path.basename(filePath, path.extname(filePath)), + }, + Owners: [ + { + Owner: { + UserPrincipalName: 'jomo@microsoft.com' + } + } + ], + Approvers: [ + { + Approver: { + UserPrincipalName: 'jomo@microsoft.com' + }, + IsAutoApproved: true, + IsMandatory: false + } + ], + AccessPermissions: { + MainPublisher: 'VSCode', + ChannelDownloadEntityDetails: { + Consumer: ['VSCode'] + } + }, + CreatedBy: { + UserPrincipalName: 'jomo@microsoft.com' + } + }, + ReleaseBatches: [ + { + ReleaseRequestFiles: [ + { + SizeInBytes: size, + SourceHash: sha256, + HashType: 'SHA256', + SourceLocation: path.basename(filePath) + } + ], + SourceLocationType: 'UNC', + SourceRootDirectory: path.dirname(filePath), + DestinationLocationType: 'AzureBlob' + } + ] + })); + const outputPath = this.tmp.tmpNameSync(); + cp.execSync(`ESRPClient SubmitRelease -a ${this.authPath} -p ${policyPath} -i ${inputPath} -o ${outputPath}`, { stdio: 'inherit' }); + const output = fs.readFileSync(outputPath, 'utf8'); + return JSON.parse(output); + } + async ReleaseDetails(releaseId) { + const inputPath = this.tmp.tmpNameSync(); + fs.writeFileSync(inputPath, JSON.stringify({ + Version: '1.0.0', + OperationIds: [releaseId] + })); + const outputPath = this.tmp.tmpNameSync(); + cp.execSync(`ESRPClient ReleaseDetails -a ${this.authPath} -i ${inputPath} -o ${outputPath}`, { stdio: 'inherit' }); + const output = fs.readFileSync(outputPath, 'utf8'); + return JSON.parse(output); + } +} +async function releaseAndProvision(log, releaseTenantId, releaseClientId, releaseAuthCertSubjectName, releaseRequestSigningCertSubjectName, provisionTenantId, provisionAADUsername, provisionAADPassword, version, quality, filePath) { + const fileName = `${quality}/${version}/${path.basename(filePath)}`; + const result = `${e('PRSS_CDN_URL')}/${fileName}`; + const res = await (0, retry_1.retry)(() => (0, node_fetch_1.default)(result)); + if (res.status === 200) { + log(`Already released and provisioned: ${result}`); + return result; + } + const tmp = new Temp(); + process.on('exit', () => tmp.dispose()); + const esrpclient = new ESRPClient(log, tmp, releaseTenantId, releaseClientId, releaseAuthCertSubjectName, releaseRequestSigningCertSubjectName); + const release = await esrpclient.release(version, filePath); + const credential = new identity_1.ClientSecretCredential(provisionTenantId, provisionAADUsername, provisionAADPassword); + const accessToken = await credential.getToken(['https://microsoft.onmicrosoft.com/DS.Provisioning.WebApi/.default']); + const service = new ProvisionService(log, accessToken.token); + await service.provision(release.releaseId, release.fileId, fileName); + return result; +} +class State { + statePath; + set = new Set(); + constructor() { + const pipelineWorkspacePath = e('PIPELINE_WORKSPACE'); + const previousState = fs.readdirSync(pipelineWorkspacePath) + .map(name => /^artifacts_processed_(\d+)$/.exec(name)) + .filter((match) => !!match) + .map(match => ({ name: match[0], attempt: Number(match[1]) })) + .sort((a, b) => b.attempt - a.attempt)[0]; + if (previousState) { + const previousStatePath = path.join(pipelineWorkspacePath, previousState.name, previousState.name + '.txt'); + fs.readFileSync(previousStatePath, 'utf8').split(/\n/).filter(name => !!name).forEach(name => this.set.add(name)); + } + const stageAttempt = e('SYSTEM_STAGEATTEMPT'); + this.statePath = path.join(pipelineWorkspacePath, `artifacts_processed_${stageAttempt}`, `artifacts_processed_${stageAttempt}.txt`); + fs.mkdirSync(path.dirname(this.statePath), { recursive: true }); + fs.writeFileSync(this.statePath, [...this.set.values()].join('\n')); + } + get size() { + return this.set.size; + } + has(name) { + return this.set.has(name); + } + add(name) { + this.set.add(name); + fs.appendFileSync(this.statePath, `${name}\n`); + } + [Symbol.iterator]() { + return this.set[Symbol.iterator](); + } +} +const azdoFetchOptions = { headers: { Authorization: `Bearer ${e('SYSTEM_ACCESSTOKEN')}` }, timeout: 60000 }; +async function requestAZDOAPI(path) { + const res = await (0, node_fetch_1.default)(`${e('BUILDS_API_URL')}${path}?api-version=6.0`, azdoFetchOptions); + if (!res.ok) { + throw new Error(`Unexpected status code: ${res.status}`); + } + return await Promise.race([ + res.json(), + new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 60000)) + ]); +} +async function getPipelineArtifacts() { + const result = await requestAZDOAPI('artifacts'); + return result.value.filter(a => /^vscode_/.test(a.name) && !/sbom$/.test(a.name)); +} +async function getPipelineTimeline() { + return await requestAZDOAPI('timeline'); +} +async function downloadArtifact(artifact, downloadPath) { + const res = await (0, node_fetch_1.default)(artifact.resource.downloadUrl, azdoFetchOptions); + if (!res.ok) { + throw new Error(`Unexpected status code: ${res.status}`); + } + await Promise.race([ + (0, promises_1.pipeline)(res.body, fs.createWriteStream(downloadPath)), + new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 5 * 60 * 1000)) + ]); +} +async function unzip(packagePath, outputPath) { + return new Promise((resolve, reject) => { + yauzl.open(packagePath, { lazyEntries: true }, (err, zipfile) => { + if (err) { + return reject(err); + } + zipfile.on('entry', entry => { + if (/\/$/.test(entry.fileName)) { + zipfile.readEntry(); + } + else { + zipfile.openReadStream(entry, (err, istream) => { + if (err) { + return reject(err); + } + const filePath = path.join(outputPath, entry.fileName); + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + const ostream = fs.createWriteStream(filePath); + ostream.on('finish', () => { + zipfile.close(); + resolve(filePath); + }); + istream?.on('error', err => reject(err)); + istream.pipe(ostream); + }); + } + }); + zipfile.readEntry(); + }); + }); +} +// 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': { + switch (type) { + case 'archive': + return `win32-${arch}-archive`; + case 'setup': + return `win32-${arch}`; + case 'user-setup': + return `win32-${arch}-user`; + default: + throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); + } + } + case 'server': + if (arch === 'arm64') { + throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); + } + return `server-win32-${arch}`; + case 'web': + if (arch === 'arm64') { + throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); + } + return `server-win32-${arch}-web`; + case 'cli': + return `cli-win32-${arch}`; + 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`; + case 'cli': + return `cli-alpine-${arch}`; + 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}`; + case 'cli': + return `cli-linux-${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': + if (arch === 'x64') { + return 'server-darwin'; + } + return `server-darwin-${arch}`; + case 'web': + if (arch === 'x64') { + return 'server-darwin-web'; + } + return `server-darwin-${arch}-web`; + case 'cli': + return `cli-darwin-${arch}`; + 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; + } +} +const azureSequencer = new Sequencer(); +const mooncakeSequencer = new Sequencer(); +async function uploadAssetLegacy(log, quality, commit, filePath) { + const fileName = path.basename(filePath); + 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(e('AZURE_TENANT_ID'), e('AZURE_CLIENT_ID'), e('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 blobOptions = { + blobHTTPHeaders: { + blobContentType: mime.lookup(filePath), + blobContentDisposition: `attachment; filename="${fileName}"`, + blobCacheControl: 'max-age=31536000, public' + } + }; + const uploadPromises = []; + uploadPromises.push((async () => { + log(`Checking for blob in Azure...`); + if (await (0, retry_1.retry)(() => blobClient.exists())) { + throw new Error(`Blob ${quality}, ${blobName} already exists, not publishing again.`); + } + else { + await (0, retry_1.retry)(attempt => azureSequencer.queue(async () => { + log(`Uploading blobs to Azure storage (attempt ${attempt})...`); + await blobClient.uploadFile(filePath, blobOptions); + log('Blob successfully uploaded to Azure storage.'); + })); + } + })()); + const shouldUploadToMooncake = /true/i.test(e('VSCODE_PUBLISH_TO_MOONCAKE')); + if (shouldUploadToMooncake) { + const mooncakeCredential = new identity_1.ClientSecretCredential(e('AZURE_MOONCAKE_TENANT_ID'), e('AZURE_MOONCAKE_CLIENT_ID'), e('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); + uploadPromises.push((async () => { + log(`Checking for blob in Mooncake Azure...`); + if (await (0, retry_1.retry)(() => mooncakeBlobClient.exists())) { + throw new Error(`Mooncake Blob ${quality}, ${blobName} already exists, not publishing again.`); + } + else { + await (0, retry_1.retry)(attempt => mooncakeSequencer.queue(async () => { + log(`Uploading blobs to Mooncake Azure storage (attempt ${attempt})...`); + await mooncakeBlobClient.uploadFile(filePath, blobOptions); + log('Blob successfully uploaded to Mooncake Azure storage.'); + })); + } + })()); + } + const promiseResults = await Promise.allSettled(uploadPromises); + const rejectedPromiseResults = promiseResults.filter(result => result.status === 'rejected'); + if (rejectedPromiseResults.length === 0) { + log('All blobs successfully uploaded.'); + } + else if (rejectedPromiseResults[0]?.reason?.message?.includes('already exists')) { + log(rejectedPromiseResults[0].reason.message); + log('Some blobs successfully uploaded.'); + } + else { + // eslint-disable-next-line no-throw-literal + throw rejectedPromiseResults[0]?.reason; + } + const assetUrl = `${e('AZURE_CDN_URL')}/${quality}/${blobName}`; + const blobPath = new URL(assetUrl).pathname; + const mooncakeUrl = `${e('MOONCAKE_CDN_URL')}${blobPath}`; + return { assetUrl, mooncakeUrl }; +} +const downloadSequencer = new Sequencer(); +const cosmosSequencer = new Sequencer(); +async function processArtifact(artifact) { + const match = /^vscode_(?[^_]+)_(?[^_]+)_(?[^_]+)_(?[^_]+)$/.exec(artifact.name); + if (!match) { + throw new Error(`Invalid artifact name: ${artifact.name}`); + } + const { product, os, arch, unprocessedType } = match.groups; + const log = (...args) => console.log(`[${product} ${os} ${arch} ${unprocessedType}]`, ...args); + const filePath = await (0, retry_1.retry)(async (attempt) => { + const artifactZipPath = path.join(e('AGENT_TEMPDIRECTORY'), `${artifact.name}.zip`); + await downloadSequencer.queue(async () => { + log(`Downloading ${artifact.resource.downloadUrl} (attempt ${attempt})...`); + await downloadArtifact(artifact, artifactZipPath); + }); + log(`Extracting (attempt ${attempt}) ...`); + const filePath = await unzip(artifactZipPath, e('AGENT_TEMPDIRECTORY')); + const artifactSize = fs.statSync(filePath).size; + if (artifactSize !== Number(artifact.resource.properties.artifactsize)) { + throw new Error(`Artifact size mismatch. Expected ${artifact.resource.properties.artifactsize}. Actual ${artifactSize}`); + } + return filePath; + }); + // getPlatform needs the unprocessedType + const quality = e('VSCODE_QUALITY'); + const commit = e('BUILD_SOURCEVERSION'); + const platform = getPlatform(product, os, arch, unprocessedType); + const type = getRealType(unprocessedType); + const size = fs.statSync(filePath).size; + const stream = fs.createReadStream(filePath); + const [sha1hash, sha256hash] = await Promise.all([hashStream('sha1', stream), hashStream('sha256', stream)]); + log(`Publishing (size = ${size}, SHA1 = ${sha1hash}, SHA256 = ${sha256hash})...`); + const [{ assetUrl, mooncakeUrl }, prssUrl] = await Promise.all([ + uploadAssetLegacy(log, quality, commit, filePath), + releaseAndProvision(log, e('RELEASE_TENANT_ID'), e('RELEASE_CLIENT_ID'), e('RELEASE_AUTH_CERT_SUBJECT_NAME'), e('RELEASE_REQUEST_SIGNING_CERT_SUBJECT_NAME'), e('PROVISION_TENANT_ID'), e('PROVISION_AAD_USERNAME'), e('PROVISION_AAD_PASSWORD'), commit, quality, filePath) + ]); + const asset = { platform, type, url: assetUrl, hash: sha1hash, mooncakeUrl, prssUrl, sha256hash, size, supportsFastUpdate: true }; + log('Creating asset...', JSON.stringify(asset)); + await (0, retry_1.retry)(async (attempt) => { + await cosmosSequencer.queue(async () => { + log(`Creating asset in Cosmos DB (attempt ${attempt})...`); + const aadCredentials = new identity_1.ClientSecretCredential(e('AZURE_TENANT_ID'), e('AZURE_CLIENT_ID'), e('AZURE_CLIENT_SECRET')); + const client = new cosmos_1.CosmosClient({ endpoint: e('AZURE_DOCUMENTDB_ENDPOINT'), aadCredentials }); + const scripts = client.database('builds').container(quality).scripts; + await scripts.storedProcedure('createAsset').execute('', [commit, asset, true]); + }); + }); + log('Asset successfully created'); +} +async function main() { + const done = new State(); + const processing = new Set(); + for (const name of done) { + console.log(`\u2705 ${name}`); + } + const stages = new Set(); + if (e('VSCODE_BUILD_STAGE_WINDOWS') === 'True') { + stages.add('Windows'); + } + if (e('VSCODE_BUILD_STAGE_LINUX') === 'True') { + stages.add('Linux'); + } + if (e('VSCODE_BUILD_STAGE_ALPINE') === 'True') { + stages.add('Alpine'); + } + if (e('VSCODE_BUILD_STAGE_MACOS') === 'True') { + stages.add('macOS'); + } + if (e('VSCODE_BUILD_STAGE_WEB') === 'True') { + stages.add('Web'); + } + const operations = []; + while (true) { + const [timeline, artifacts] = await Promise.all([(0, retry_1.retry)(() => getPipelineTimeline()), (0, retry_1.retry)(() => getPipelineArtifacts())]); + const stagesCompleted = new Set(timeline.records.filter(r => r.type === 'Stage' && r.state === 'completed' && stages.has(r.name)).map(r => r.name)); + const stagesInProgress = [...stages].filter(s => !stagesCompleted.has(s)); + if (stagesInProgress.length > 0) { + console.log('Stages in progress:', stagesInProgress.join(', ')); + } + const artifactsInProgress = artifacts.filter(a => processing.has(a.name)); + if (artifactsInProgress.length > 0) { + console.log('Artifacts in progress:', artifactsInProgress.map(a => a.name).join(', ')); + } + if (stagesCompleted.size === stages.size && artifacts.length === done.size + processing.size) { + break; + } + for (const artifact of artifacts) { + if (done.has(artifact.name) || processing.has(artifact.name)) { + continue; + } + console.log(`Found new artifact: ${artifact.name}`); + processing.add(artifact.name); + const operation = processArtifact(artifact).then(() => { + processing.delete(artifact.name); + done.add(artifact.name); + console.log(`\u2705 ${artifact.name}`); + }); + operations.push({ name: artifact.name, operation }); + } + await new Promise(c => setTimeout(c, 10000)); + } + console.log(`Found all ${done.size + processing.size} artifacts, waiting for ${processing.size} artifacts to finish publishing...`); + const artifactsInProgress = operations.filter(o => processing.has(o.name)); + if (artifactsInProgress.length > 0) { + console.log('Artifacts in progress:', artifactsInProgress.map(a => a.name).join(', ')); + } + const results = await Promise.allSettled(operations.map(o => o.operation)); + for (let i = 0; i < operations.length; i++) { + const result = results[i]; + if (result.status === 'rejected') { + console.error(`[${operations[i].name}]`, result.reason); + } + } + if (results.some(r => r.status === 'rejected')) { + throw new Error('Some artifacts failed to publish'); + } + console.log(`All ${done.size} artifacts published!`); +} +if (require.main === module) { + main().then(() => { + process.exit(0); + }, err => { + console.error(err); + process.exit(1); + }); +} +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHVibGlzaC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInB1Ymxpc2gudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Z0dBR2dHOztBQUVoRyx5QkFBeUI7QUFDekIsNkJBQTZCO0FBQzdCLDJDQUFnRDtBQUVoRCxtREFBZ0Q7QUFDaEQsK0JBQStCO0FBQy9CLGlDQUFpQztBQUNqQyxtQ0FBZ0M7QUFDaEMsc0RBQXdJO0FBQ3hJLDZCQUE2QjtBQUM3QiwwQ0FBNkM7QUFDN0MsOENBQXlEO0FBQ3pELG9DQUFvQztBQUNwQyx5QkFBeUI7QUFFekIsU0FBUyxDQUFDLENBQUMsSUFBWTtJQUN0QixNQUFNLE1BQU0sR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBRWpDLElBQUksT0FBTyxNQUFNLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDaEMsTUFBTSxJQUFJLEtBQUssQ0FBQyxnQkFBZ0IsSUFBSSxFQUFFLENBQUMsQ0FBQztJQUN6QyxDQUFDO0lBRUQsT0FBTyxNQUFNLENBQUM7QUFDZixDQUFDO0FBRUQsTUFBTSxJQUFJO0lBQ0QsTUFBTSxHQUFhLEVBQUUsQ0FBQztJQUU5QixXQUFXO1FBQ1YsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLEVBQUUsTUFBTSxDQUFDLFdBQVcsQ0FBQyxFQUFFLENBQUMsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztRQUM1RSxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUN2QixPQUFPLElBQUksQ0FBQztJQUNiLENBQUM7SUFFRCxPQUFPO1FBQ04sS0FBSyxNQUFNLElBQUksSUFBSSxJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDaEMsSUFBSSxDQUFDO2dCQUNKLEVBQUUsQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDckIsQ0FBQztZQUFDLE9BQU8s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 \ No newline at end of file diff --git a/build/azure-pipelines/common/publish.ts b/build/azure-pipelines/common/publish.ts new file mode 100644 index 00000000000..a75cfaa4c72 --- /dev/null +++ b/build/azure-pipelines/common/publish.ts @@ -0,0 +1,869 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as fs from 'fs'; +import * as path from 'path'; +import fetch, { RequestInit } from 'node-fetch'; +import { Readable } from 'stream'; +import { pipeline } from 'node:stream/promises'; +import * as yauzl from 'yauzl'; +import * as crypto from 'crypto'; +import { retry } from './retry'; +import { BlobServiceClient, BlockBlobParallelUploadOptions, StoragePipelineOptions, StorageRetryPolicyType } from '@azure/storage-blob'; +import * as mime from 'mime'; +import { CosmosClient } from '@azure/cosmos'; +import { ClientSecretCredential } from '@azure/identity'; +import * as cp from 'child_process'; +import * as os from 'os'; + +function e(name: string): string { + const result = process.env[name]; + + if (typeof result !== 'string') { + throw new Error(`Missing env: ${name}`); + } + + return result; +} + +class Temp { + private _files: string[] = []; + + tmpNameSync(): string { + const file = path.join(os.tmpdir(), crypto.randomBytes(20).toString('hex')); + this._files.push(file); + return file; + } + + dispose(): void { + for (const file of this._files) { + try { + fs.unlinkSync(file); + } catch (err) { + // noop + } + } + } +} + +class Sequencer { + + private current: Promise = Promise.resolve(null); + + queue(promiseTask: () => Promise): Promise { + return this.current = this.current.then(() => promiseTask(), () => promiseTask()); + } +} + +interface RequestOptions { + readonly body?: string; +} + +interface CreateProvisionedFilesSuccessResponse { + IsSuccess: true; + ErrorDetails: null; +} + +interface CreateProvisionedFilesErrorResponse { + IsSuccess: false; + ErrorDetails: { + Code: string; + Category: string; + Message: string; + CanRetry: boolean; + AdditionalProperties: Record; + }; +} + +type CreateProvisionedFilesResponse = CreateProvisionedFilesSuccessResponse | CreateProvisionedFilesErrorResponse; + +class ProvisionService { + + constructor( + private readonly log: (...args: any[]) => void, + private readonly accessToken: string + ) { } + + async provision(releaseId: string, fileId: string, fileName: string) { + const body = JSON.stringify({ + ReleaseId: releaseId, + PortalName: 'VSCode', + PublisherCode: 'VSCode', + ProvisionedFilesCollection: [{ + PublisherKey: fileId, + IsStaticFriendlyFileName: true, + FriendlyFileName: fileName, + MaxTTL: '1440', + CdnMappings: ['ECN'] + }] + }); + + this.log(`Provisioning ${fileName} (releaseId: ${releaseId}, fileId: ${fileId})...`); + const res = await retry(() => this.request('POST', '/api/v2/ProvisionedFiles/CreateProvisionedFiles', { body })); + + if (!res.IsSuccess) { + throw new Error(`Failed to submit provisioning request: ${JSON.stringify(res.ErrorDetails)}`); + } + + this.log(`Successfully provisioned ${fileName}`); + } + + private async request(method: string, url: string, options?: RequestOptions): Promise { + const opts: RequestInit = { + method, + body: options?.body, + headers: { + Authorization: `Bearer ${this.accessToken}`, + 'Content-Type': 'application/json' + } + }; + + const res = await fetch(`https://dsprovisionapi.microsoft.com${url}`, opts); + + if (!res.ok || res.status < 200 || res.status >= 500) { + throw new Error(`Unexpected status code: ${res.status}`); + } + + return await res.json(); + } +} + +function hashStream(hashName: string, stream: Readable): Promise { + 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'))); + }); +} + +interface Release { + readonly releaseId: string; + readonly fileId: string; +} + +interface SubmitReleaseResult { + submissionResponse: { + operationId: string; + statusCode: string; + }; +} + +interface ReleaseDetailsResult { + releaseDetails: [{ + fileDetails: [{ publisherKey: string }]; + statusCode: 'inprogress' | 'pass'; + }]; +} + +class ESRPClient { + + private static Sequencer = new Sequencer(); + + private readonly authPath: string; + + constructor( + private readonly log: (...args: any[]) => void, + private readonly tmp: Temp, + tenantId: string, + clientId: string, + authCertSubjectName: string, + requestSigningCertSubjectName: string, + ) { + this.authPath = this.tmp.tmpNameSync(); + fs.writeFileSync(this.authPath, JSON.stringify({ + Version: '1.0.0', + AuthenticationType: 'AAD_CERT', + TenantId: tenantId, + ClientId: clientId, + AuthCert: { + SubjectName: authCertSubjectName, + StoreLocation: 'LocalMachine', + StoreName: 'My', + SendX5c: 'true' + }, + RequestSigningCert: { + SubjectName: requestSigningCertSubjectName, + StoreLocation: 'LocalMachine', + StoreName: 'My' + } + })); + } + + async release( + version: string, + filePath: string + ): Promise { + const submitReleaseResult = await ESRPClient.Sequencer.queue(async () => { + this.log(`Submitting release for ${version}: ${filePath}`); + return await this.SubmitRelease(version, filePath); + }); + + if (submitReleaseResult.submissionResponse.statusCode !== 'pass') { + throw new Error(`Unexpected status code: ${submitReleaseResult.submissionResponse.statusCode}`); + } + + const releaseId = submitReleaseResult.submissionResponse.operationId; + this.log(`Successfully submitted release ${releaseId}. Polling for completion...`); + + let details!: ReleaseDetailsResult; + + // Poll every 5 seconds, wait 60 minutes max -> poll 60/5*60=720 times + for (let i = 0; i < 720; i++) { + details = await this.ReleaseDetails(releaseId); + + if (details.releaseDetails[0].statusCode === 'pass') { + break; + } else if (details.releaseDetails[0].statusCode !== 'inprogress') { + throw new Error(`Failed to submit release: ${JSON.stringify(details)}`); + } + + await new Promise(c => setTimeout(c, 5000)); + } + + if (details.releaseDetails[0].statusCode !== 'pass') { + throw new Error(`Timed out waiting for release ${releaseId}: ${JSON.stringify(details)}`); + } + + const fileId = details.releaseDetails[0].fileDetails[0].publisherKey; + this.log('Release completed successfully with fileId: ', fileId); + + return { releaseId, fileId }; + } + + private async SubmitRelease( + version: string, + filePath: string + ): Promise { + const policyPath = this.tmp.tmpNameSync(); + fs.writeFileSync(policyPath, JSON.stringify({ + Version: '1.0.0', + Audience: 'InternalLimited', + Intent: 'distribution', + ContentType: 'InstallPackage' + })); + + const inputPath = this.tmp.tmpNameSync(); + const size = fs.statSync(filePath).size; + const istream = fs.createReadStream(filePath); + const sha256 = await hashStream('sha256', istream); + fs.writeFileSync(inputPath, JSON.stringify({ + Version: '1.0.0', + ReleaseInfo: { + ReleaseMetadata: { + Title: 'VS Code', + Properties: { + ReleaseContentType: 'InstallPackage' + }, + MinimumNumberOfApprovers: 1 + }, + ProductInfo: { + Name: 'VS Code', + Version: version, + Description: path.basename(filePath, path.extname(filePath)), + }, + Owners: [ + { + Owner: { + UserPrincipalName: 'jomo@microsoft.com' + } + } + ], + Approvers: [ + { + Approver: { + UserPrincipalName: 'jomo@microsoft.com' + }, + IsAutoApproved: true, + IsMandatory: false + } + ], + AccessPermissions: { + MainPublisher: 'VSCode', + ChannelDownloadEntityDetails: { + Consumer: ['VSCode'] + } + }, + CreatedBy: { + UserPrincipalName: 'jomo@microsoft.com' + } + }, + ReleaseBatches: [ + { + ReleaseRequestFiles: [ + { + SizeInBytes: size, + SourceHash: sha256, + HashType: 'SHA256', + SourceLocation: path.basename(filePath) + } + ], + SourceLocationType: 'UNC', + SourceRootDirectory: path.dirname(filePath), + DestinationLocationType: 'AzureBlob' + } + ] + })); + + const outputPath = this.tmp.tmpNameSync(); + cp.execSync(`ESRPClient SubmitRelease -a ${this.authPath} -p ${policyPath} -i ${inputPath} -o ${outputPath}`, { stdio: 'inherit' }); + + const output = fs.readFileSync(outputPath, 'utf8'); + return JSON.parse(output) as SubmitReleaseResult; + } + + private async ReleaseDetails( + releaseId: string + ): Promise { + const inputPath = this.tmp.tmpNameSync(); + fs.writeFileSync(inputPath, JSON.stringify({ + Version: '1.0.0', + OperationIds: [releaseId] + })); + + const outputPath = this.tmp.tmpNameSync(); + cp.execSync(`ESRPClient ReleaseDetails -a ${this.authPath} -i ${inputPath} -o ${outputPath}`, { stdio: 'inherit' }); + + const output = fs.readFileSync(outputPath, 'utf8'); + return JSON.parse(output) as ReleaseDetailsResult; + } +} + +async function releaseAndProvision( + log: (...args: any[]) => void, + releaseTenantId: string, + releaseClientId: string, + releaseAuthCertSubjectName: string, + releaseRequestSigningCertSubjectName: string, + provisionTenantId: string, + provisionAADUsername: string, + provisionAADPassword: string, + version: string, + quality: string, + filePath: string +): Promise { + const fileName = `${quality}/${version}/${path.basename(filePath)}`; + const result = `${e('PRSS_CDN_URL')}/${fileName}`; + + const res = await retry(() => fetch(result)); + + if (res.status === 200) { + log(`Already released and provisioned: ${result}`); + return result; + } + + const tmp = new Temp(); + process.on('exit', () => tmp.dispose()); + + const esrpclient = new ESRPClient(log, tmp, releaseTenantId, releaseClientId, releaseAuthCertSubjectName, releaseRequestSigningCertSubjectName); + const release = await esrpclient.release(version, filePath); + + const credential = new ClientSecretCredential(provisionTenantId, provisionAADUsername, provisionAADPassword); + const accessToken = await credential.getToken(['https://microsoft.onmicrosoft.com/DS.Provisioning.WebApi/.default']); + const service = new ProvisionService(log, accessToken.token); + + await service.provision(release.releaseId, release.fileId, fileName); + + return result; +} + +class State { + + private statePath: string; + private set = new Set(); + + constructor() { + const pipelineWorkspacePath = e('PIPELINE_WORKSPACE'); + const previousState = fs.readdirSync(pipelineWorkspacePath) + .map(name => /^artifacts_processed_(\d+)$/.exec(name)) + .filter((match): match is RegExpExecArray => !!match) + .map(match => ({ name: match![0], attempt: Number(match![1]) })) + .sort((a, b) => b.attempt - a.attempt)[0]; + + if (previousState) { + const previousStatePath = path.join(pipelineWorkspacePath, previousState.name, previousState.name + '.txt'); + fs.readFileSync(previousStatePath, 'utf8').split(/\n/).filter(name => !!name).forEach(name => this.set.add(name)); + } + + const stageAttempt = e('SYSTEM_STAGEATTEMPT'); + this.statePath = path.join(pipelineWorkspacePath, `artifacts_processed_${stageAttempt}`, `artifacts_processed_${stageAttempt}.txt`); + fs.mkdirSync(path.dirname(this.statePath), { recursive: true }); + fs.writeFileSync(this.statePath, [...this.set.values()].join('\n')); + } + + get size(): number { + return this.set.size; + } + + has(name: string): boolean { + return this.set.has(name); + } + + add(name: string): void { + this.set.add(name); + fs.appendFileSync(this.statePath, `${name}\n`); + } + + [Symbol.iterator](): IterableIterator { + return this.set[Symbol.iterator](); + } +} + +const azdoFetchOptions = { headers: { Authorization: `Bearer ${e('SYSTEM_ACCESSTOKEN')}` }, timeout: 60_000 }; + +async function requestAZDOAPI(path: string): Promise { + const res = await fetch(`${e('BUILDS_API_URL')}${path}?api-version=6.0`, azdoFetchOptions); + + if (!res.ok) { + throw new Error(`Unexpected status code: ${res.status}`); + } + + return await Promise.race([ + res.json(), + new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 60_000)) + ]); +} + +interface Artifact { + readonly name: string; + readonly resource: { + readonly downloadUrl: string; + readonly properties: { + readonly artifactsize: number; + }; + }; +} + +async function getPipelineArtifacts(): Promise { + const result = await requestAZDOAPI<{ readonly value: Artifact[] }>('artifacts'); + return result.value.filter(a => /^vscode_/.test(a.name) && !/sbom$/.test(a.name)); +} + +interface Timeline { + readonly records: { + readonly name: string; + readonly type: string; + readonly state: string; + }[]; +} + +async function getPipelineTimeline(): Promise { + return await requestAZDOAPI('timeline'); +} + +async function downloadArtifact(artifact: Artifact, downloadPath: string): Promise { + const res = await fetch(artifact.resource.downloadUrl, azdoFetchOptions); + + if (!res.ok) { + throw new Error(`Unexpected status code: ${res.status}`); + } + + await Promise.race([ + pipeline(res.body, fs.createWriteStream(downloadPath)), + new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 5 * 60 * 1000)) + ]); +} + +async function unzip(packagePath: string, outputPath: string): Promise { + return new Promise((resolve, reject) => { + yauzl.open(packagePath, { lazyEntries: true }, (err, zipfile) => { + if (err) { + return reject(err); + } + + zipfile!.on('entry', entry => { + if (/\/$/.test(entry.fileName)) { + zipfile!.readEntry(); + } else { + zipfile!.openReadStream(entry, (err, istream) => { + if (err) { + return reject(err); + } + + const filePath = path.join(outputPath, entry.fileName); + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + + const ostream = fs.createWriteStream(filePath); + ostream.on('finish', () => { + zipfile!.close(); + resolve(filePath); + }); + istream?.on('error', err => reject(err)); + istream!.pipe(ostream); + }); + } + }); + + zipfile!.readEntry(); + }); + }); +} + +interface Asset { + platform: string; + type: string; + url: string; + mooncakeUrl?: string; + prssUrl?: string; + hash: string; + sha256hash: string; + size: number; + supportsFastUpdate?: boolean; +} + +// Contains all of the logic for mapping details to our actual product names in CosmosDB +function getPlatform(product: string, os: string, arch: string, type: string): string { + switch (os) { + case 'win32': + switch (product) { + case 'client': { + switch (type) { + case 'archive': + return `win32-${arch}-archive`; + case 'setup': + return `win32-${arch}`; + case 'user-setup': + return `win32-${arch}-user`; + default: + throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); + } + } + case 'server': + if (arch === 'arm64') { + throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); + } + return `server-win32-${arch}`; + case 'web': + if (arch === 'arm64') { + throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); + } + return `server-win32-${arch}-web`; + case 'cli': + return `cli-win32-${arch}`; + 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`; + case 'cli': + return `cli-alpine-${arch}`; + 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}`; + case 'cli': + return `cli-linux-${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': + if (arch === 'x64') { + return 'server-darwin'; + } + return `server-darwin-${arch}`; + case 'web': + if (arch === 'x64') { + return 'server-darwin-web'; + } + return `server-darwin-${arch}-web`; + case 'cli': + return `cli-darwin-${arch}`; + 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: string) { + switch (type) { + case 'user-setup': + return 'setup'; + case 'deb-package': + case 'rpm-package': + return 'package'; + default: + return type; + } +} + +const azureSequencer = new Sequencer(); +const mooncakeSequencer = new Sequencer(); + +async function uploadAssetLegacy(log: (...args: any[]) => void, quality: string, commit: string, filePath: string): Promise<{ assetUrl: string; mooncakeUrl: string }> { + const fileName = path.basename(filePath); + const blobName = commit + '/' + fileName; + + const storagePipelineOptions: StoragePipelineOptions = { retryOptions: { retryPolicyType: StorageRetryPolicyType.EXPONENTIAL, maxTries: 6, tryTimeoutInMs: 10 * 60 * 1000 } }; + + const credential = new ClientSecretCredential(e('AZURE_TENANT_ID'), e('AZURE_CLIENT_ID'), e('AZURE_CLIENT_SECRET')); + const blobServiceClient = new BlobServiceClient(`https://vscode.blob.core.windows.net`, credential, storagePipelineOptions); + const containerClient = blobServiceClient.getContainerClient(quality); + const blobClient = containerClient.getBlockBlobClient(blobName); + + const blobOptions: BlockBlobParallelUploadOptions = { + blobHTTPHeaders: { + blobContentType: mime.lookup(filePath), + blobContentDisposition: `attachment; filename="${fileName}"`, + blobCacheControl: 'max-age=31536000, public' + } + }; + + const uploadPromises: Promise[] = []; + + uploadPromises.push((async (): Promise => { + log(`Checking for blob in Azure...`); + + if (await retry(() => blobClient.exists())) { + throw new Error(`Blob ${quality}, ${blobName} already exists, not publishing again.`); + } else { + await retry(attempt => azureSequencer.queue(async () => { + log(`Uploading blobs to Azure storage (attempt ${attempt})...`); + await blobClient.uploadFile(filePath, blobOptions); + log('Blob successfully uploaded to Azure storage.'); + })); + } + })()); + + const shouldUploadToMooncake = /true/i.test(e('VSCODE_PUBLISH_TO_MOONCAKE')); + + if (shouldUploadToMooncake) { + const mooncakeCredential = new ClientSecretCredential(e('AZURE_MOONCAKE_TENANT_ID'), e('AZURE_MOONCAKE_CLIENT_ID'), e('AZURE_MOONCAKE_CLIENT_SECRET')); + const mooncakeBlobServiceClient = new BlobServiceClient(`https://vscode.blob.core.chinacloudapi.cn`, mooncakeCredential, storagePipelineOptions); + const mooncakeContainerClient = mooncakeBlobServiceClient.getContainerClient(quality); + const mooncakeBlobClient = mooncakeContainerClient.getBlockBlobClient(blobName); + + uploadPromises.push((async (): Promise => { + log(`Checking for blob in Mooncake Azure...`); + + if (await retry(() => mooncakeBlobClient.exists())) { + throw new Error(`Mooncake Blob ${quality}, ${blobName} already exists, not publishing again.`); + } else { + await retry(attempt => mooncakeSequencer.queue(async () => { + log(`Uploading blobs to Mooncake Azure storage (attempt ${attempt})...`); + await mooncakeBlobClient.uploadFile(filePath, blobOptions); + log('Blob successfully uploaded to Mooncake Azure storage.'); + })); + } + })()); + } + + const promiseResults = await Promise.allSettled(uploadPromises); + const rejectedPromiseResults = promiseResults.filter(result => result.status === 'rejected') as PromiseRejectedResult[]; + + if (rejectedPromiseResults.length === 0) { + log('All blobs successfully uploaded.'); + } else if (rejectedPromiseResults[0]?.reason?.message?.includes('already exists')) { + log(rejectedPromiseResults[0].reason.message); + log('Some blobs successfully uploaded.'); + } else { + // eslint-disable-next-line no-throw-literal + throw rejectedPromiseResults[0]?.reason; + } + + const assetUrl = `${e('AZURE_CDN_URL')}/${quality}/${blobName}`; + const blobPath = new URL(assetUrl).pathname; + const mooncakeUrl = `${e('MOONCAKE_CDN_URL')}${blobPath}`; + + return { assetUrl, mooncakeUrl }; +} + +const downloadSequencer = new Sequencer(); +const cosmosSequencer = new Sequencer(); + +async function processArtifact(artifact: Artifact): Promise { + const match = /^vscode_(?[^_]+)_(?[^_]+)_(?[^_]+)_(?[^_]+)$/.exec(artifact.name); + + if (!match) { + throw new Error(`Invalid artifact name: ${artifact.name}`); + } + + const { product, os, arch, unprocessedType } = match.groups!; + const log = (...args: any[]) => console.log(`[${product} ${os} ${arch} ${unprocessedType}]`, ...args); + + const filePath = await retry(async attempt => { + const artifactZipPath = path.join(e('AGENT_TEMPDIRECTORY'), `${artifact.name}.zip`); + await downloadSequencer.queue(async () => { + log(`Downloading ${artifact.resource.downloadUrl} (attempt ${attempt})...`); + await downloadArtifact(artifact, artifactZipPath); + }); + + log(`Extracting (attempt ${attempt}) ...`); + const filePath = await unzip(artifactZipPath, e('AGENT_TEMPDIRECTORY')); + const artifactSize = fs.statSync(filePath).size; + + if (artifactSize !== Number(artifact.resource.properties.artifactsize)) { + throw new Error(`Artifact size mismatch. Expected ${artifact.resource.properties.artifactsize}. Actual ${artifactSize}`); + } + + return filePath; + }); + + // getPlatform needs the unprocessedType + const quality = e('VSCODE_QUALITY'); + const commit = e('BUILD_SOURCEVERSION'); + const platform = getPlatform(product, os, arch, unprocessedType); + const type = getRealType(unprocessedType); + const size = fs.statSync(filePath).size; + const stream = fs.createReadStream(filePath); + const [sha1hash, sha256hash] = await Promise.all([hashStream('sha1', stream), hashStream('sha256', stream)]); + + log(`Publishing (size = ${size}, SHA1 = ${sha1hash}, SHA256 = ${sha256hash})...`); + + const [{ assetUrl, mooncakeUrl }, prssUrl] = await Promise.all([ + uploadAssetLegacy(log, quality, commit, filePath), + releaseAndProvision( + log, + e('RELEASE_TENANT_ID'), + e('RELEASE_CLIENT_ID'), + e('RELEASE_AUTH_CERT_SUBJECT_NAME'), + e('RELEASE_REQUEST_SIGNING_CERT_SUBJECT_NAME'), + e('PROVISION_TENANT_ID'), + e('PROVISION_AAD_USERNAME'), + e('PROVISION_AAD_PASSWORD'), + commit, + quality, + filePath + ) + ]); + + const asset: Asset = { platform, type, url: assetUrl, hash: sha1hash, mooncakeUrl, prssUrl, sha256hash, size, supportsFastUpdate: true }; + log('Creating asset...', JSON.stringify(asset)); + + await retry(async (attempt) => { + await cosmosSequencer.queue(async () => { + log(`Creating asset in Cosmos DB (attempt ${attempt})...`); + const aadCredentials = new ClientSecretCredential(e('AZURE_TENANT_ID'), e('AZURE_CLIENT_ID'), e('AZURE_CLIENT_SECRET')); + const client = new CosmosClient({ endpoint: e('AZURE_DOCUMENTDB_ENDPOINT'), aadCredentials }); + const scripts = client.database('builds').container(quality).scripts; + await scripts.storedProcedure('createAsset').execute('', [commit, asset, true]); + }); + }); + + log('Asset successfully created'); +} + +async function main() { + const done = new State(); + const processing = new Set(); + + for (const name of done) { + console.log(`\u2705 ${name}`); + } + + const stages = new Set(); + if (e('VSCODE_BUILD_STAGE_WINDOWS') === 'True') { stages.add('Windows'); } + if (e('VSCODE_BUILD_STAGE_LINUX') === 'True') { stages.add('Linux'); } + if (e('VSCODE_BUILD_STAGE_ALPINE') === 'True') { stages.add('Alpine'); } + if (e('VSCODE_BUILD_STAGE_MACOS') === 'True') { stages.add('macOS'); } + if (e('VSCODE_BUILD_STAGE_WEB') === 'True') { stages.add('Web'); } + + const operations: { name: string; operation: Promise }[] = []; + + while (true) { + const [timeline, artifacts] = await Promise.all([retry(() => getPipelineTimeline()), retry(() => getPipelineArtifacts())]); + const stagesCompleted = new Set(timeline.records.filter(r => r.type === 'Stage' && r.state === 'completed' && stages.has(r.name)).map(r => r.name)); + + const stagesInProgress = [...stages].filter(s => !stagesCompleted.has(s)); + + if (stagesInProgress.length > 0) { + console.log('Stages in progress:', stagesInProgress.join(', ')); + } + + const artifactsInProgress = artifacts.filter(a => processing.has(a.name)); + + if (artifactsInProgress.length > 0) { + console.log('Artifacts in progress:', artifactsInProgress.map(a => a.name).join(', ')); + } + + if (stagesCompleted.size === stages.size && artifacts.length === done.size + processing.size) { + break; + } + + for (const artifact of artifacts) { + if (done.has(artifact.name) || processing.has(artifact.name)) { + continue; + } + + console.log(`Found new artifact: ${artifact.name}`); + processing.add(artifact.name); + const operation = processArtifact(artifact).then(() => { + processing.delete(artifact.name); + done.add(artifact.name); + console.log(`\u2705 ${artifact.name}`); + }); + + operations.push({ name: artifact.name, operation }); + } + + await new Promise(c => setTimeout(c, 10_000)); + } + + console.log(`Found all ${done.size + processing.size} artifacts, waiting for ${processing.size} artifacts to finish publishing...`); + + const artifactsInProgress = operations.filter(o => processing.has(o.name)); + + if (artifactsInProgress.length > 0) { + console.log('Artifacts in progress:', artifactsInProgress.map(a => a.name).join(', ')); + } + + const results = await Promise.allSettled(operations.map(o => o.operation)); + + for (let i = 0; i < operations.length; i++) { + const result = results[i]; + + if (result.status === 'rejected') { + console.error(`[${operations[i].name}]`, result.reason); + } + } + + if (results.some(r => r.status === 'rejected')) { + throw new Error('Some artifacts failed to publish'); + } + + console.log(`All ${done.size} artifacts published!`); +} + +if (require.main === module) { + main().then(() => { + process.exit(0); + }, err => { + console.error(err); + process.exit(1); + }); +} diff --git a/build/azure-pipelines/common/retry.js b/build/azure-pipelines/common/retry.js index 843d13539b8..094aa93f458 100644 --- a/build/azure-pipelines/common/retry.js +++ b/build/azure-pipelines/common/retry.js @@ -12,18 +12,17 @@ async function retry(fn) { return await fn(run); } catch (err) { - if (!/ECONNRESET|CredentialUnavailableError|Audience validation failed/i.test(err.message)) { + if (!/fetch failed|timeout|TimeoutError|Timeout Error|RestError|Client network socket disconnected|socket hang up|ECONNRESET|CredentialUnavailableError|endpoints_resolution_error|Audience validation failed|end of central directory record signature not found/i.test(err.message)) { throw err; } lastError = err; - const millis = (Math.random() * 200) + (50 * Math.pow(1.5, run)); - console.log(`Request failed, retrying in ${millis}ms...`); // maximum delay is 10th retry: ~3 seconds + const millis = Math.floor((Math.random() * 200) + (50 * Math.pow(1.5, run))); await new Promise(c => setTimeout(c, millis)); } } - console.log(`Too many retries, aborting.`); + console.error(`Too many retries, aborting.`); throw lastError; } exports.retry = retry; -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmV0cnkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJyZXRyeS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7OztBQUV6RixLQUFLLFVBQVUsS0FBSyxDQUFJLEVBQW1DO0lBQ2pFLElBQUksU0FBNEIsQ0FBQztJQUVqQyxLQUFLLElBQUksR0FBRyxHQUFHLENBQUMsRUFBRSxHQUFHLElBQUksRUFBRSxFQUFFLEdBQUcsRUFBRSxFQUFFLENBQUM7UUFDcEMsSUFBSSxDQUFDO1lBQ0osT0FBTyxNQUFNLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUN0QixDQUFDO1FBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztZQUNkLElBQUksQ0FBQyxtRUFBbUUsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7Z0JBQzVGLE1BQU0sR0FBRyxDQUFDO1lBQ1gsQ0FBQztZQUVELFNBQVMsR0FBRyxHQUFHLENBQUM7WUFDaEIsTUFBTSxNQUFNLEdBQUcsQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLEdBQUcsR0FBRyxDQUFDLEdBQUcsQ0FBQyxFQUFFLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQztZQUNqRSxPQUFPLENBQUMsR0FBRyxDQUFDLCtCQUErQixNQUFNLE9BQU8sQ0FBQyxDQUFDO1lBRTFELDBDQUEwQztZQUMxQyxNQUFNLElBQUksT0FBTyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLENBQUMsRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDO1FBQy9DLENBQUM7SUFDRixDQUFDO0lBRUQsT0FBTyxDQUFDLEdBQUcsQ0FBQyw2QkFBNkIsQ0FBQyxDQUFDO0lBQzNDLE1BQU0sU0FBUyxDQUFDO0FBQ2pCLENBQUM7QUF0QkQsc0JBc0JDIn0= \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmV0cnkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJyZXRyeS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7OztBQUV6RixLQUFLLFVBQVUsS0FBSyxDQUFJLEVBQW1DO0lBQ2pFLElBQUksU0FBNEIsQ0FBQztJQUVqQyxLQUFLLElBQUksR0FBRyxHQUFHLENBQUMsRUFBRSxHQUFHLElBQUksRUFBRSxFQUFFLEdBQUcsRUFBRSxFQUFFLENBQUM7UUFDcEMsSUFBSSxDQUFDO1lBQ0osT0FBTyxNQUFNLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUN0QixDQUFDO1FBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztZQUNkLElBQUksQ0FBQyw4UEFBOFAsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7Z0JBQ3ZSLE1BQU0sR0FBRyxDQUFDO1lBQ1gsQ0FBQztZQUVELFNBQVMsR0FBRyxHQUFHLENBQUM7WUFFaEIsMENBQTBDO1lBQzFDLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLEdBQUcsR0FBRyxDQUFDLEdBQUcsQ0FBQyxFQUFFLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQzdFLE1BQU0sSUFBSSxPQUFPLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7UUFDL0MsQ0FBQztJQUNGLENBQUM7SUFFRCxPQUFPLENBQUMsS0FBSyxDQUFDLDZCQUE2QixDQUFDLENBQUM7SUFDN0MsTUFBTSxTQUFTLENBQUM7QUFDakIsQ0FBQztBQXJCRCxzQkFxQkMifQ== \ No newline at end of file diff --git a/build/azure-pipelines/common/retry.ts b/build/azure-pipelines/common/retry.ts index 9b28b4fd956..0085b78fd7f 100644 --- a/build/azure-pipelines/common/retry.ts +++ b/build/azure-pipelines/common/retry.ts @@ -10,19 +10,18 @@ export async function retry(fn: (attempt: number) => Promise): Promise try { return await fn(run); } catch (err) { - if (!/ECONNRESET|CredentialUnavailableError|Audience validation failed/i.test(err.message)) { + if (!/fetch failed|timeout|TimeoutError|Timeout Error|RestError|Client network socket disconnected|socket hang up|ECONNRESET|CredentialUnavailableError|endpoints_resolution_error|Audience validation failed|end of central directory record signature not found/i.test(err.message)) { throw err; } lastError = err; - const millis = (Math.random() * 200) + (50 * Math.pow(1.5, run)); - console.log(`Request failed, retrying in ${millis}ms...`); // maximum delay is 10th retry: ~3 seconds + const millis = Math.floor((Math.random() * 200) + (50 * Math.pow(1.5, run))); await new Promise(c => setTimeout(c, millis)); } } - console.log(`Too many retries, aborting.`); + console.error(`Too many retries, aborting.`); throw lastError; } diff --git a/build/azure-pipelines/common/sign-win32.js b/build/azure-pipelines/common/sign-win32.js index 8d26aa6d952..9c17eea7d11 100644 --- a/build/azure-pipelines/common/sign-win32.js +++ b/build/azure-pipelines/common/sign-win32.js @@ -8,11 +8,11 @@ const sign_1 = require("./sign"); const path = require("path"); (0, sign_1.main)([ process.env['EsrpCliDllPath'], - 'windows', + 'sign-windows', process.env['ESRPPKI'], process.env['ESRPAADUsername'], process.env['ESRPAADPassword'], path.dirname(process.argv[2]), path.basename(process.argv[2]) ]); -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2lnbi13aW4zMi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInNpZ24td2luMzIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Z0dBR2dHOztBQUVoRyxpQ0FBOEI7QUFDOUIsNkJBQTZCO0FBRTdCLElBQUEsV0FBSSxFQUFDO0lBQ0osT0FBTyxDQUFDLEdBQUcsQ0FBQyxnQkFBZ0IsQ0FBRTtJQUM5QixTQUFTO0lBQ1QsT0FBTyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUU7SUFDdkIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsQ0FBRTtJQUMvQixPQUFPLENBQUMsR0FBRyxDQUFDLGlCQUFpQixDQUFFO0lBQy9CLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUM3QixJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7Q0FDOUIsQ0FBQyxDQUFDIn0= \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2lnbi13aW4zMi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInNpZ24td2luMzIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Z0dBR2dHOztBQUVoRyxpQ0FBOEI7QUFDOUIsNkJBQTZCO0FBRTdCLElBQUEsV0FBSSxFQUFDO0lBQ0osT0FBTyxDQUFDLEdBQUcsQ0FBQyxnQkFBZ0IsQ0FBRTtJQUM5QixjQUFjO0lBQ2QsT0FBTyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUU7SUFDdkIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsQ0FBRTtJQUMvQixPQUFPLENBQUMsR0FBRyxDQUFDLGlCQUFpQixDQUFFO0lBQy9CLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUM3QixJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7Q0FDOUIsQ0FBQyxDQUFDIn0= \ No newline at end of file diff --git a/build/azure-pipelines/common/sign-win32.ts b/build/azure-pipelines/common/sign-win32.ts index d5daa875812..76828b42e1e 100644 --- a/build/azure-pipelines/common/sign-win32.ts +++ b/build/azure-pipelines/common/sign-win32.ts @@ -8,7 +8,7 @@ import * as path from 'path'; main([ process.env['EsrpCliDllPath']!, - 'windows', + 'sign-windows', process.env['ESRPPKI']!, process.env['ESRPAADUsername']!, process.env['ESRPAADPassword']!, diff --git a/build/azure-pipelines/common/sign.js b/build/azure-pipelines/common/sign.js index 10b0eff40bd..b4899e28b8a 100644 --- a/build/azure-pipelines/common/sign.js +++ b/build/azure-pipelines/common/sign.js @@ -4,12 +4,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ Object.defineProperty(exports, "__esModule", { value: true }); -exports.main = void 0; +exports.main = exports.Temp = void 0; const cp = require("child_process"); const fs = require("fs"); +const crypto = require("crypto"); const path = require("path"); const os = require("os"); -const crypto = require("crypto"); class Temp { _files = []; tmpNameSync() { @@ -28,18 +28,82 @@ class Temp { } } } +exports.Temp = Temp; function getParams(type) { switch (type) { - case 'windows': - return '[{"keyCode":"CP-230012","operationSetCode":"SigntoolSign","parameters":[{"parameterName":"OpusName","parameterValue":"VS Code"},{"parameterName":"OpusInfo","parameterValue":"https://code.visualstudio.com/"},{"parameterName":"Append","parameterValue":"/as"},{"parameterName":"FileDigest","parameterValue":"/fd \\"SHA256\\""},{"parameterName":"PageHash","parameterValue":"/NPH"},{"parameterName":"TimeStamp","parameterValue":"/tr \\"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\\" /td sha256"}],"toolName":"sign","toolVersion":"1.0"},{"keyCode":"CP-230012","operationSetCode":"SigntoolVerify","parameters":[{"parameterName":"VerifyAll","parameterValue":"/all"}],"toolName":"sign","toolVersion":"1.0"}]'; - case 'windows-appx': - return '[{"keyCode":"CP-229979","operationSetCode":"SigntoolSign","parameters":[{"parameterName":"OpusName","parameterValue":"VS Code"},{"parameterName":"OpusInfo","parameterValue":"https://code.visualstudio.com/"},{"parameterName":"FileDigest","parameterValue":"/fd \\"SHA256\\""},{"parameterName":"PageHash","parameterValue":"/NPH"},{"parameterName":"TimeStamp","parameterValue":"/tr \\"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\\" /td sha256"}],"toolName":"sign","toolVersion":"1.0"},{"keyCode":"CP-229979","operationSetCode":"SigntoolVerify","parameters":[],"toolName":"sign","toolVersion":"1.0"}]'; - case 'pgp': - return '[{ "keyCode": "CP-450779-Pgp", "operationSetCode": "LinuxSign", "parameters": [], "toolName": "sign", "toolVersion": "1.0" }]'; - case 'darwin-sign': - return '[{"keyCode":"CP-401337-Apple","operationSetCode":"MacAppDeveloperSign","parameters":[{"parameterName":"Hardening","parameterValue":"--options=runtime"}],"toolName":"sign","toolVersion":"1.0"}]'; - case 'darwin-notarize': - return '[{"keyCode":"CP-401337-Apple","operationSetCode":"MacAppNotarize","parameters":[],"toolName":"sign","toolVersion":"1.0"}]'; + case 'sign-windows': + return [ + { + keyCode: 'CP-230012', + operationSetCode: 'SigntoolSign', + parameters: [ + { parameterName: 'OpusName', parameterValue: 'VS Code' }, + { parameterName: 'OpusInfo', parameterValue: 'https://code.visualstudio.com/' }, + { parameterName: 'Append', parameterValue: '/as' }, + { parameterName: 'FileDigest', parameterValue: '/fd "SHA256"' }, + { parameterName: 'PageHash', parameterValue: '/NPH' }, + { parameterName: 'TimeStamp', parameterValue: '/tr "http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer" /td sha256' } + ], + toolName: 'sign', + toolVersion: '1.0' + }, + { + keyCode: 'CP-230012', + operationSetCode: 'SigntoolVerify', + parameters: [ + { parameterName: 'VerifyAll', parameterValue: '/all' } + ], + toolName: 'sign', + toolVersion: '1.0' + } + ]; + case 'sign-windows-appx': + return [ + { + keyCode: 'CP-229979', + operationSetCode: 'SigntoolSign', + parameters: [ + { parameterName: 'OpusName', parameterValue: 'VS Code' }, + { parameterName: 'OpusInfo', parameterValue: 'https://code.visualstudio.com/' }, + { parameterName: 'FileDigest', parameterValue: '/fd "SHA256"' }, + { parameterName: 'PageHash', parameterValue: '/NPH' }, + { parameterName: 'TimeStamp', parameterValue: '/tr "http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer" /td sha256' } + ], + toolName: 'sign', + toolVersion: '1.0' + }, + { + keyCode: 'CP-229979', + operationSetCode: 'SigntoolVerify', + parameters: [], + toolName: 'sign', + toolVersion: '1.0' + } + ]; + case 'sign-pgp': + return [{ + keyCode: 'CP-450779-Pgp', + operationSetCode: 'LinuxSign', + parameters: [], + toolName: 'sign', + toolVersion: '1.0' + }]; + case 'sign-darwin': + return [{ + keyCode: 'CP-401337-Apple', + operationSetCode: 'MacAppDeveloperSign', + parameters: [{ parameterName: 'Hardening', parameterValue: '--options=runtime' }], + toolName: 'sign', + toolVersion: '1.0' + }]; + case 'notarize-darwin': + return [{ + keyCode: 'CP-401337-Apple', + operationSetCode: 'MacAppNotarize', + parameters: [], + toolName: 'sign', + toolVersion: '1.0' + }]; default: throw new Error(`Sign type ${type} not found`); } @@ -50,7 +114,7 @@ function main([esrpCliPath, type, cert, username, password, folderPath, pattern] const patternPath = tmp.tmpNameSync(); fs.writeFileSync(patternPath, pattern); const paramsPath = tmp.tmpNameSync(); - fs.writeFileSync(paramsPath, getParams(type)); + fs.writeFileSync(paramsPath, JSON.stringify(getParams(type))); const keyFile = tmp.tmpNameSync(); const key = crypto.randomBytes(32); const iv = crypto.randomBytes(16); @@ -105,4 +169,4 @@ if (require.main === module) { main(process.argv.slice(2)); process.exit(0); } -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2lnbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInNpZ24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Z0dBR2dHOzs7QUFFaEcsb0NBQW9DO0FBQ3BDLHlCQUF5QjtBQUN6Qiw2QkFBNkI7QUFDN0IseUJBQXlCO0FBQ3pCLGlDQUFpQztBQUVqQyxNQUFNLElBQUk7SUFDRCxNQUFNLEdBQWEsRUFBRSxDQUFDO0lBRTlCLFdBQVc7UUFDVixNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsRUFBRSxNQUFNLENBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO1FBQzVFLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3ZCLE9BQU8sSUFBSSxDQUFDO0lBQ2IsQ0FBQztJQUVELE9BQU87UUFDTixLQUFLLE1BQU0sSUFBSSxJQUFJLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUNoQyxJQUFJLENBQUM7Z0JBQ0osRUFBRSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUNyQixDQUFDO1lBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztnQkFDZCxPQUFPO1lBQ1IsQ0FBQztRQUNGLENBQUM7SUFDRixDQUFDO0NBQ0Q7QUFFRCxTQUFTLFNBQVMsQ0FBQyxJQUFZO0lBQzlCLFFBQVEsSUFBSSxFQUFFLENBQUM7UUFDZCxLQUFLLFNBQVM7WUFDYixPQUFPLHdzQkFBd3NCLENBQUM7UUFDanRCLEtBQUssY0FBYztZQUNsQixPQUFPLGltQkFBaW1CLENBQUM7UUFDMW1CLEtBQUssS0FBSztZQUNULE9BQU8sK0hBQStILENBQUM7UUFDeEksS0FBSyxhQUFhO1lBQ2pCLE9BQU8sa01BQWtNLENBQUM7UUFDM00sS0FBSyxpQkFBaUI7WUFDckIsT0FBTywySEFBMkgsQ0FBQztRQUNwSTtZQUNDLE1B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 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2lnbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInNpZ24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7Z0dBR2dHOzs7QUFFaEcsb0NBQW9DO0FBQ3BDLHlCQUF5QjtBQUN6QixpQ0FBaUM7QUFDakMsNkJBQTZCO0FBQzdCLHlCQUF5QjtBQUV6QixNQUFhLElBQUk7SUFDUixNQUFNLEdBQWEsRUFBRSxDQUFDO0lBRTlCLFdBQVc7UUFDVixNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxNQUFNLEVBQUUsRUFBRSxNQUFNLENBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO1FBQzVFLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3ZCLE9BQU8sSUFBSSxDQUFDO0lBQ2IsQ0FBQztJQUVELE9BQU87UUFDTixLQUFLLE1BQU0sSUFBSSxJQUFJLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUNoQyxJQUFJLENBQUM7Z0JBQ0osRUFBRSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUNyQixDQUFDO1lBQUMsT0FBTyxHQUFHLEVBQUUsQ0FBQztnQkFDZCxPQUFPO1lBQ1IsQ0FBQztRQUNGLENBQUM7SUFDRixDQUFDO0NBQ0Q7QUFsQkQsb0JBa0JDO0FBYUQsU0FBUyxTQUFTLENBQUMsSUFBWTtJQUM5QixRQUFRLElBQUksRUFBRSxDQUFDO1FBQ2QsS0FBSyxjQUFjO1lBQ2xCLE9BQU87Z0JBQ047b0JBQ0MsT0FBTyxFQUFFLFdBQVc7b0JBQ3BCLGdCQUFnQixFQUFFLGNBQWM7b0JBQ2hDLFVBQVUsRUFBRTt3QkFDWCxFQUFFLGFBQWEsRUFBRSxVQUFVLEVBQUUsY0FBYyxFQUFFLFNBQVMsRUFBRTt3QkFDeEQsRUFBRSxhQUFhLEVBQUUsVUFBVSxFQUFFLGNBQWMsRUFBRSxnQ0FBZ0MsRUFBRTt3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 \ No newline at end of file diff --git a/build/azure-pipelines/common/sign.ts b/build/azure-pipelines/common/sign.ts index 494e89b3e12..28fca31205e 100644 --- a/build/azure-pipelines/common/sign.ts +++ b/build/azure-pipelines/common/sign.ts @@ -5,11 +5,11 @@ import * as cp from 'child_process'; import * as fs from 'fs'; +import * as crypto from 'crypto'; import * as path from 'path'; import * as os from 'os'; -import * as crypto from 'crypto'; -class Temp { +export class Temp { private _files: string[] = []; tmpNameSync(): string { @@ -29,18 +29,92 @@ class Temp { } } -function getParams(type: string): string { +interface Params { + readonly keyCode: string; + readonly operationSetCode: string; + readonly parameters: { + readonly parameterName: string; + readonly parameterValue: string; + }[]; + readonly toolName: string; + readonly toolVersion: string; +} + +function getParams(type: string): Params[] { switch (type) { - case 'windows': - return '[{"keyCode":"CP-230012","operationSetCode":"SigntoolSign","parameters":[{"parameterName":"OpusName","parameterValue":"VS Code"},{"parameterName":"OpusInfo","parameterValue":"https://code.visualstudio.com/"},{"parameterName":"Append","parameterValue":"/as"},{"parameterName":"FileDigest","parameterValue":"/fd \\"SHA256\\""},{"parameterName":"PageHash","parameterValue":"/NPH"},{"parameterName":"TimeStamp","parameterValue":"/tr \\"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\\" /td sha256"}],"toolName":"sign","toolVersion":"1.0"},{"keyCode":"CP-230012","operationSetCode":"SigntoolVerify","parameters":[{"parameterName":"VerifyAll","parameterValue":"/all"}],"toolName":"sign","toolVersion":"1.0"}]'; - case 'windows-appx': - return '[{"keyCode":"CP-229979","operationSetCode":"SigntoolSign","parameters":[{"parameterName":"OpusName","parameterValue":"VS Code"},{"parameterName":"OpusInfo","parameterValue":"https://code.visualstudio.com/"},{"parameterName":"FileDigest","parameterValue":"/fd \\"SHA256\\""},{"parameterName":"PageHash","parameterValue":"/NPH"},{"parameterName":"TimeStamp","parameterValue":"/tr \\"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\\" /td sha256"}],"toolName":"sign","toolVersion":"1.0"},{"keyCode":"CP-229979","operationSetCode":"SigntoolVerify","parameters":[],"toolName":"sign","toolVersion":"1.0"}]'; - case 'pgp': - return '[{ "keyCode": "CP-450779-Pgp", "operationSetCode": "LinuxSign", "parameters": [], "toolName": "sign", "toolVersion": "1.0" }]'; - case 'darwin-sign': - return '[{"keyCode":"CP-401337-Apple","operationSetCode":"MacAppDeveloperSign","parameters":[{"parameterName":"Hardening","parameterValue":"--options=runtime"}],"toolName":"sign","toolVersion":"1.0"}]'; - case 'darwin-notarize': - return '[{"keyCode":"CP-401337-Apple","operationSetCode":"MacAppNotarize","parameters":[],"toolName":"sign","toolVersion":"1.0"}]'; + case 'sign-windows': + return [ + { + keyCode: 'CP-230012', + operationSetCode: 'SigntoolSign', + parameters: [ + { parameterName: 'OpusName', parameterValue: 'VS Code' }, + { parameterName: 'OpusInfo', parameterValue: 'https://code.visualstudio.com/' }, + { parameterName: 'Append', parameterValue: '/as' }, + { parameterName: 'FileDigest', parameterValue: '/fd "SHA256"' }, + { parameterName: 'PageHash', parameterValue: '/NPH' }, + { parameterName: 'TimeStamp', parameterValue: '/tr "http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer" /td sha256' } + ], + toolName: 'sign', + toolVersion: '1.0' + }, + { + keyCode: 'CP-230012', + operationSetCode: 'SigntoolVerify', + parameters: [ + { parameterName: 'VerifyAll', parameterValue: '/all' } + ], + toolName: 'sign', + toolVersion: '1.0' + } + ]; + case 'sign-windows-appx': + return [ + { + keyCode: 'CP-229979', + operationSetCode: 'SigntoolSign', + parameters: [ + { parameterName: 'OpusName', parameterValue: 'VS Code' }, + { parameterName: 'OpusInfo', parameterValue: 'https://code.visualstudio.com/' }, + { parameterName: 'FileDigest', parameterValue: '/fd "SHA256"' }, + { parameterName: 'PageHash', parameterValue: '/NPH' }, + { parameterName: 'TimeStamp', parameterValue: '/tr "http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer" /td sha256' } + ], + toolName: 'sign', + toolVersion: '1.0' + }, + { + keyCode: 'CP-229979', + operationSetCode: 'SigntoolVerify', + parameters: [], + toolName: 'sign', + toolVersion: '1.0' + } + ]; + case 'sign-pgp': + return [{ + keyCode: 'CP-450779-Pgp', + operationSetCode: 'LinuxSign', + parameters: [], + toolName: 'sign', + toolVersion: '1.0' + }]; + case 'sign-darwin': + return [{ + keyCode: 'CP-401337-Apple', + operationSetCode: 'MacAppDeveloperSign', + parameters: [{ parameterName: 'Hardening', parameterValue: '--options=runtime' }], + toolName: 'sign', + toolVersion: '1.0' + }]; + case 'notarize-darwin': + return [{ + keyCode: 'CP-401337-Apple', + operationSetCode: 'MacAppNotarize', + parameters: [], + toolName: 'sign', + toolVersion: '1.0' + }]; default: throw new Error(`Sign type ${type} not found`); } @@ -54,7 +128,7 @@ export function main([esrpCliPath, type, cert, username, password, folderPath, p fs.writeFileSync(patternPath, pattern); const paramsPath = tmp.tmpNameSync(); - fs.writeFileSync(paramsPath, getParams(type)); + fs.writeFileSync(paramsPath, JSON.stringify(getParams(type))); const keyFile = tmp.tmpNameSync(); const key = crypto.randomBytes(32); diff --git a/build/azure-pipelines/darwin/product-build-darwin-sign.yml b/build/azure-pipelines/darwin/product-build-darwin-sign.yml index 59f4821c1f1..3f31ac7bd35 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-sign.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-sign.yml @@ -24,10 +24,10 @@ steps: artifact: unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive displayName: Download $(VSCODE_ARCH) artifact - - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll darwin-sign $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(Pipeline.Workspace)/unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive VSCode-darwin-$(VSCODE_ARCH).zip + - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll sign-darwin $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(Pipeline.Workspace)/unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive VSCode-darwin-$(VSCODE_ARCH).zip displayName: Codesign - - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll darwin-notarize $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(Pipeline.Workspace)/unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive VSCode-darwin-$(VSCODE_ARCH).zip + - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll notarize-darwin $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(Pipeline.Workspace)/unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive VSCode-darwin-$(VSCODE_ARCH).zip displayName: Notarize - script: unzip $(Pipeline.Workspace)/unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive/VSCode-darwin-$(VSCODE_ARCH).zip -d $(Agent.BuildDirectory)/VSCode-darwin-$(VSCODE_ARCH) diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index a8d434b135a..c714b9a6c5e 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -313,10 +313,10 @@ steps: continueOnError: true displayName: Download ESRPClient - - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll pgp $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) .build/linux/deb '*.deb' + - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll sign-pgp $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) .build/linux/deb '*.deb' displayName: Codesign deb - - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll pgp $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) .build/linux/rpm '*.rpm' + - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll sign-pgp $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) .build/linux/rpm '*.rpm' displayName: Codesign rpm - script: echo "##vso[task.setvariable variable=ARTIFACT_PREFIX]attempt$(System.JobAttempt)_" diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index 1b4c2b1bb41..5e7f567f0c8 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -126,6 +126,14 @@ variables: value: ${{ eq(parameters.VSCODE_STEP_ON_IT, true) }} - name: VSCODE_BUILD_MACOS_UNIVERSAL value: ${{ and(eq(parameters.VSCODE_BUILD_MACOS, true), eq(parameters.VSCODE_BUILD_MACOS_ARM64, true), eq(parameters.VSCODE_BUILD_MACOS_UNIVERSAL, true)) }} + - name: PRSS_CDN_URL + value: https://vscode.download.prss.microsoft.com/dbazure/download + - name: PRSS_RELEASE_TENANT_ID + value: 975f013f-7f24-47e8-a7d3-abc4752bf346 + - name: PRSS_RELEASE_CLIENT_ID + value: c24324f7-e65f-4c45-8702-ed2d4c35df99 + - name: PRSS_PROVISION_TENANT_ID + value: 72f988bf-86f1-41af-91ab-2d7cd011db47 - name: AZURE_CDN_URL value: https://az764295.vo.msecnd.net - name: AZURE_DOCUMENTDB_ENDPOINT @@ -627,7 +635,7 @@ stages: - stage: Publish dependsOn: - Compile - pool: 1es-ubuntu-20.04-x64 + pool: 1es-windows-2019-x64 variables: - name: BUILDS_API_URL value: $(System.CollectionUri)$(System.TeamProject)/_apis/build/builds/$(Build.BuildId)/ diff --git a/build/azure-pipelines/product-publish.ps1 b/build/azure-pipelines/product-publish.ps1 deleted file mode 100644 index fa4bf1aa31e..00000000000 --- a/build/azure-pipelines/product-publish.ps1 +++ /dev/null @@ -1,130 +0,0 @@ -. build/azure-pipelines/win32/exec.ps1 -$ErrorActionPreference = 'Stop' -$ProgressPreference = 'SilentlyContinue' -$ARTIFACT_PROCESSED_WILDCARD_PATH = "$env:PIPELINE_WORKSPACE/artifacts_processed_*/artifacts_processed_*" -$ARTIFACT_PROCESSED_FILE_PATH = "$env:PIPELINE_WORKSPACE/artifacts_processed_$env:SYSTEM_STAGEATTEMPT/artifacts_processed_$env:SYSTEM_STAGEATTEMPT.txt" - -function Get-PipelineArtifact { - param($Name = '*') - try { - $res = Invoke-RestMethod "$($env:BUILDS_API_URL)artifacts?api-version=6.0" -Headers @{ - Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" - } -MaximumRetryCount 5 -RetryIntervalSec 1 - - if (!$res) { - return - } - - $res.value | Where-Object { $_.name -Like $Name -and $_.name -NotLike "*sbom" } - } catch { - Write-Warning $_ - } -} - -# This set will keep track of which artifacts have already been processed -$set = [System.Collections.Generic.HashSet[string]]::new() - -if (Test-Path $ARTIFACT_PROCESSED_WILDCARD_PATH) { - # Grab the latest artifact_processed text file and load all assets already processed from that. - # This means that the latest artifact_processed_*.txt file has all of the contents of the previous ones. - # Note: The kusto-like syntax only works in PS7+ and only in scripts, not at the REPL. - Get-ChildItem $ARTIFACT_PROCESSED_WILDCARD_PATH - # Sort by file name length first and then Name to make sure we sort numerically. Ex. 12 comes after 9. - | Sort-Object { $_.Name.Length },Name -Bottom 1 - | Get-Content - | ForEach-Object { - $set.Add($_) | Out-Null - Write-Host "Already processed artifact: $_" - } -} - -# Create the artifact file that will be used for this run -New-Item -Path $ARTIFACT_PROCESSED_FILE_PATH -Force | Out-Null - -# Determine which stages we need to watch -$stages = @( - if ($env:VSCODE_BUILD_STAGE_WINDOWS -eq 'True') { 'Windows' } - if ($env:VSCODE_BUILD_STAGE_LINUX -eq 'True') { 'Linux' } - if ($env:VSCODE_BUILD_STAGE_ALPINE -eq 'True') { 'Alpine' } - if ($env:VSCODE_BUILD_STAGE_MACOS -eq 'True') { 'macOS' } - if ($env:VSCODE_BUILD_STAGE_WEB -eq 'True') { 'Web' } -) - -do { - Start-Sleep -Seconds 10 - - $artifacts = Get-PipelineArtifact -Name 'vscode_*' - if (!$artifacts) { - continue - } - - $artifacts | ForEach-Object { - $artifactName = $_.name - - if($set.Add($artifactName)) { - Write-Host "Processing artifact: '$artifactName. Downloading from: $($_.resource.downloadUrl)" - - $extractPath = "$env:AGENT_TEMPDIRECTORY/$artifactName.zip" - try { - Invoke-RestMethod $_.resource.downloadUrl -OutFile $extractPath -Headers @{ - Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" - } -MaximumRetryCount 5 -RetryIntervalSec 1 -TimeoutSec 300 | Out-Null - - Write-Host "Extracting artifact: '$extractPath'" - - Expand-Archive -Path $extractPath -DestinationPath $env:AGENT_TEMPDIRECTORY | Out-Null - } catch { - Write-Warning $_ - $set.Remove($artifactName) | Out-Null - continue - } - - $null,$product,$os,$arch,$type = $artifactName -split '_' - $asset = Get-ChildItem -rec "$env:AGENT_TEMPDIRECTORY/$artifactName" - - if ($asset.Size -ne $_.resource.properties.artifactsize) { - Write-Warning "Artifact size mismatch for '$artifactName'. Expected: $($_.resource.properties.artifactsize). Actual: $($asset.Size)" - $set.Remove($artifactName) | Out-Null - continue - } - - Write-Host "Processing artifact with the following values:" - # turning in into an object just to log nicely - @{ - product = $product - os = $os - arch = $arch - type = $type - asset = $asset.Name - } | Format-Table - - exec { node build/azure-pipelines/common/createAsset.js $product $os $arch $type $asset.Name $asset.FullName } - } - - # Mark the artifact as processed. Make sure to keep the previously - # processed artifacts in the file as well, not just from this run. - $artifactName >> $ARTIFACT_PROCESSED_FILE_PATH - } - - # Get the timeline and see if it says the other stage completed - try { - $timeline = Invoke-RestMethod "$($env:BUILDS_API_URL)timeline?api-version=6.0" -Headers @{ - Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" - } -MaximumRetryCount 5 -RetryIntervalSec 1 - } catch { - Write-Warning $_ - continue - } - - foreach ($stage in $stages) { - $otherStageFinished = $timeline.records | Where-Object { $_.name -eq $stage -and $_.type -eq 'stage' -and $_.state -eq 'completed' } - if (!$otherStageFinished) { - break - } - } - - $artifacts = Get-PipelineArtifact -Name 'vscode_*' - $artifactsStillToProcess = $artifacts.Count -ne $set.Count -} while (!$otherStageFinished -or $artifactsStillToProcess) - -Write-Host "Processed $($set.Count) artifacts." diff --git a/build/azure-pipelines/product-publish.yml b/build/azure-pipelines/product-publish.yml index df6ac09de76..16730948182 100644 --- a/build/azure-pipelines/product-publish.yml +++ b/build/azure-pipelines/product-publish.yml @@ -5,17 +5,30 @@ steps: versionFilePath: .nvmrc nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download + - task: SFP.build-tasks.esrpclient-tools-task.EsrpClientTool@2 + displayName: "Use EsrpClient" + - task: AzureKeyVault@1 displayName: "Azure Key Vault: Get Secrets" inputs: azureSubscription: "vscode-builds-subscription" KeyVaultName: vscode-build-secrets - SecretsFilter: "github-distro-mixin-password" + SecretsFilter: "github-distro-mixin-password,esrp-aad-username,esrp-aad-password" + + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode-build-packages + SecretsFilter: "vscode-esrp,c24324f7-e65f-4c45-8702-ed2d4c35df99" # allow-any-unicode-next-line - pwsh: Write-Host "##vso[build.addbuildtag]🚀" displayName: Add build tag + - pwsh: node build/npm/setupBuildYarnrc + displayName: Prepare build dependencies + - pwsh: yarn workingDirectory: build displayName: Install build dependencies @@ -65,7 +78,25 @@ steps: AZURE_CLIENT_SECRET: "$(AZURE_CLIENT_SECRET)" displayName: Create build if it hasn't been created before - - pwsh: build/azure-pipelines/product-publish.ps1 + - pwsh: | + $ErrorActionPreference = "Stop" + $CertCollection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection + $AuthCertBytes = [System.Convert]::FromBase64String("$(vscode-esrp)") + $CertCollection.Import($AuthCertBytes, $null, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable -bxor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet) + $RequestSigningCertIndex = $CertCollection.Count + $RequestSigningCertBytes = [System.Convert]::FromBase64String("$(c24324f7-e65f-4c45-8702-ed2d4c35df99)") + $CertCollection.Import($RequestSigningCertBytes, $null, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable -bxor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet) + $CertStore = New-Object System.Security.Cryptography.X509Certificates.X509Store("My","LocalMachine") + $CertStore.Open("ReadWrite") + $CertStore.AddRange($CertCollection) + $CertStore.Close() + $AuthCertSubjectName = $CertCollection[0].Subject + $RequestSigningCertSubjectName = $CertCollection[$RequestSigningCertIndex].Subject + Write-Host "##vso[task.setvariable variable=RELEASE_AUTH_CERT_SUBJECT_NAME]$AuthCertSubjectName" + Write-Host "##vso[task.setvariable variable=RELEASE_REQUEST_SIGNING_CERT_SUBJECT_NAME]$RequestSigningCertSubjectName" + displayName: Import certificates + + - pwsh: node build/azure-pipelines/common/publish.js env: GITHUB_TOKEN: "$(github-distro-mixin-password)" AZURE_TENANT_ID: "$(AZURE_TENANT_ID)" @@ -75,7 +106,15 @@ steps: AZURE_MOONCAKE_CLIENT_ID: "$(AZURE_MOONCAKE_CLIENT_ID)" AZURE_MOONCAKE_CLIENT_SECRET: "$(AZURE_MOONCAKE_CLIENT_SECRET)" SYSTEM_ACCESSTOKEN: $(System.AccessToken) + RELEASE_TENANT_ID: "$(PRSS_RELEASE_TENANT_ID)" + RELEASE_CLIENT_ID: "$(PRSS_RELEASE_CLIENT_ID)" + RELEASE_AUTH_CERT_SUBJECT_NAME: "$(RELEASE_AUTH_CERT_SUBJECT_NAME)" + RELEASE_REQUEST_SIGNING_CERT_SUBJECT_NAME: "$(RELEASE_REQUEST_SIGNING_CERT_SUBJECT_NAME)" + PROVISION_TENANT_ID: "$(PRSS_PROVISION_TENANT_ID)" + PROVISION_AAD_USERNAME: "$(esrp-aad-username)" + PROVISION_AAD_PASSWORD: "$(esrp-aad-password)" displayName: Process artifacts + retryCountOnTaskFailure: 3 - publish: $(Pipeline.Workspace)/artifacts_processed_$(System.StageAttempt)/artifacts_processed_$(System.StageAttempt).txt artifact: artifacts_processed_$(System.StageAttempt) @@ -108,6 +147,8 @@ steps: if($didStageFail) { $failedStages += $stage + Write-Host "'$stage' failed!" + Write-Host $didStageFail } else { Write-Host "'$stage' did not fail." } diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index d5d62fa031f..8727c5b5a2b 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -242,11 +242,11 @@ steps: echo "##vso[task.setvariable variable=EsrpCliDllPath]$EsrpCliDllPath" displayName: Find ESRP CLI - - powershell: node build\azure-pipelines\common\sign $env:EsrpCliDllPath windows $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(CodeSigningFolderPath) '*.dll,*.exe,*.node' + - powershell: node build\azure-pipelines\common\sign $env:EsrpCliDllPath sign-windows $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(CodeSigningFolderPath) '*.dll,*.exe,*.node' displayName: Codesign executables and shared libraries - ${{ if eq(parameters.VSCODE_QUALITY, 'insider') }}: - - powershell: node build\azure-pipelines\common\sign $env:EsrpCliDllPath windows-appx $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(CodeSigningFolderPath) '*.appx' + - powershell: node build\azure-pipelines\common\sign $env:EsrpCliDllPath sign-windows-appx $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(CodeSigningFolderPath) '*.appx' displayName: Codesign context menu appx package - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: diff --git a/build/package.json b/build/package.json index ca6b448b393..bd674b74196 100644 --- a/build/package.json +++ b/build/package.json @@ -3,9 +3,9 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@azure/cosmos": "^3.17.3", - "@azure/identity": "^3.1.3", - "@azure/storage-blob": "^12.13.0", + "@azure/cosmos": "^3", + "@azure/identity": "^3.4.1", + "@azure/storage-blob": "^12.17.0", "@electron/get": "^1.12.4", "@types/ansi-colors": "^3.2.0", "@types/byline": "^4.2.32", @@ -56,7 +56,8 @@ "through2": "^4.0.2", "tmp": "^0.2.1", "vscode-universal-bundler": "^0.0.2", - "workerpool": "^6.4.0" + "workerpool": "^6.4.0", + "yauzl": "^2.10.0" }, "scripts": { "compile": "../node_modules/.bin/tsc -p tsconfig.build.json", diff --git a/build/yarn.lock b/build/yarn.lock index f3dd7803415..a5b6b864188 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -22,6 +22,15 @@ "@azure/abort-controller" "^1.0.0" tslib "^2.2.0" +"@azure/core-auth@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.5.0.tgz#a41848c5c31cb3b7c84c409885267d55a2c92e44" + integrity sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw== + dependencies: + "@azure/abort-controller" "^1.0.0" + "@azure/core-util" "^1.1.0" + tslib "^2.2.0" + "@azure/core-client@^1.4.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@azure/core-client/-/core-client-1.5.0.tgz#7aabb87d20e08db3683a117191c844bc19adb74e" @@ -117,7 +126,15 @@ dependencies: tslib "^2.2.0" -"@azure/core-util@^1.0.0", "@azure/core-util@^1.1.1": +"@azure/core-util@^1.1.0", "@azure/core-util@^1.6.1": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.6.1.tgz#fea221c4fa43c26543bccf799beb30c1c7878f5a" + integrity sha512-h5taHeySlsV9qxuK64KZxy4iln1BtMYlNt5jbuEFN3UFSAd1EwKg/Gjl5a6tZ/W8t6li3xPnutOx7zbDyXnPmQ== + dependencies: + "@azure/abort-controller" "^1.0.0" + tslib "^2.2.0" + +"@azure/core-util@^1.1.1": version "1.2.0" resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.2.0.tgz#3499deba1fc36dda6f1912b791809b6f15d4a392" integrity sha512-ffGIw+Qs8bNKNLxz5UPkz4/VBM/EZY07mPve1ZYFqYUdPwFqRj0RPk0U7LZMOfT7GCck9YjuT1Rfp1PApNl1ng== @@ -125,7 +142,7 @@ "@azure/abort-controller" "^1.0.0" tslib "^2.2.0" -"@azure/cosmos@^3.17.3": +"@azure/cosmos@^3": version "3.17.3" resolved "https://registry.yarnpkg.com/@azure/cosmos/-/cosmos-3.17.3.tgz#380398496af8ef3473ae0a9ad8cdbab32d91eb08" integrity sha512-wBglkQ6Irjv5Vo2iw8fd6eYj60WYRSSg4/0DBkeOP6BwQ4RA91znsOHd6s3qG6UAbNgYuzC9Nnq07vlFFZkHEw== @@ -144,27 +161,25 @@ universal-user-agent "^6.0.0" uuid "^8.3.0" -"@azure/identity@^3.1.3": - version "3.1.3" - resolved "https://registry.yarnpkg.com/@azure/identity/-/identity-3.1.3.tgz#667a635b305d9d519e5c91cea5ba3390d0d2c198" - integrity sha512-y0jFjSfHsVPwXSwi3KaSPtOZtJZqhiqAhWUXfFYBUd/+twUBovZRXspBwLrF5rJe0r5NyvmScpQjL+TYDTQVvw== +"@azure/identity@^3.4.1": + version "3.4.1" + resolved "https://registry.yarnpkg.com/@azure/identity/-/identity-3.4.1.tgz#18ba48b7421c818ef8116e8eec3c03ec1a62649a" + integrity sha512-oQ/r5MBdfZTMIUcY5Ch8G7Vv9aIXDkEYyU4Dfqjim4MQN+LY2uiQ57P1JDopMLeHCsZxM4yy8lEdne3tM9Xhzg== dependencies: "@azure/abort-controller" "^1.0.0" - "@azure/core-auth" "^1.3.0" + "@azure/core-auth" "^1.5.0" "@azure/core-client" "^1.4.0" "@azure/core-rest-pipeline" "^1.1.0" "@azure/core-tracing" "^1.0.0" - "@azure/core-util" "^1.0.0" + "@azure/core-util" "^1.6.1" "@azure/logger" "^1.0.0" - "@azure/msal-browser" "^2.32.2" - "@azure/msal-common" "^9.0.2" - "@azure/msal-node" "^1.14.6" + "@azure/msal-browser" "^3.5.0" + "@azure/msal-node" "^2.5.1" events "^3.0.0" jws "^4.0.0" open "^8.0.0" stoppable "^1.1.0" tslib "^2.2.0" - uuid "^8.3.0" "@azure/logger@^1.0.0": version "1.0.1" @@ -173,36 +188,31 @@ dependencies: tslib "^2.0.0" -"@azure/msal-browser@^2.32.2": - version "2.35.0" - resolved "https://registry.yarnpkg.com/@azure/msal-browser/-/msal-browser-2.35.0.tgz#39b553f5da140d5d16bf90e0d92f1bcc6f0d61d3" - integrity sha512-L+gSBbJfU3H81Bnj+VIVjO7jRpt2Ex+4i2YVOPE50ykfQ5W9mtBFMRCHb1K+8FzTeyQH/KkQv6bC+MdaU+3LEw== +"@azure/msal-browser@^3.5.0": + version "3.5.0" + resolved "https://registry.yarnpkg.com/@azure/msal-browser/-/msal-browser-3.5.0.tgz#eb64c931c78c2b75c70807f618e1284bbb183380" + integrity sha512-2NtMuel4CI3UEelCPKkNRXgKzpWEX48fvxIvPz7s0/sTcCaI08r05IOkH2GkXW+czUOtuY6+oGafJCpumnjRLg== dependencies: - "@azure/msal-common" "^12.0.0" + "@azure/msal-common" "14.4.0" -"@azure/msal-common@^12.0.0": - version "12.0.0" - resolved "https://registry.yarnpkg.com/@azure/msal-common/-/msal-common-12.0.0.tgz#bcb41fd31657a34c4218ec38332de76ec6bf03e6" - integrity sha512-SvQl4JWy1yZnxyq0xng/urf103wz68UJG0K9Dq2NM2to7ePA+R1hMisKnXELJvZrEGYANGbh/Hc0T9piGqOteQ== +"@azure/msal-common@14.4.0": + version "14.4.0" + resolved "https://registry.yarnpkg.com/@azure/msal-common/-/msal-common-14.4.0.tgz#f938c1d96bb73d65baab985c96faaa273c97cfd5" + integrity sha512-ffCymScQuMKVj+YVfwNI52A5Tu+uiZO2eTf+c+3TXxdAssks4nokJhtr+uOOMxH0zDi6d1OjFKFKeXODK0YLSg== -"@azure/msal-common@^9.0.2": - version "9.1.1" - resolved "https://registry.yarnpkg.com/@azure/msal-common/-/msal-common-9.1.1.tgz#906d27905c956fe91bd8f31855fc624359098d83" - integrity sha512-we9xR8lvu47fF0h+J8KyXoRy9+G/fPzm3QEa2TrdR3jaVS3LKAyE2qyMuUkNdbVkvzl8Zr9f7l+IUSP22HeqXw== - -"@azure/msal-node@^1.14.6": - version "1.17.0" - resolved "https://registry.yarnpkg.com/@azure/msal-node/-/msal-node-1.17.0.tgz#fa7bba155719a7e26ac6e8d4941dd56e807e458a" - integrity sha512-aOKykKxDc+Kf5vcdOUPdKlJ96YAIyrHyl4W8RyfMqw0iApDckOuhejNwlZr6/M7U40wo1Wj4PwxRVx7d8OFBFg== +"@azure/msal-node@^2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@azure/msal-node/-/msal-node-2.5.1.tgz#d180a1ba5fdc611a318a8f018a2db3453e2e2898" + integrity sha512-PsPRISqCG253HQk1cAS7eJW7NWTbnBGpG+vcGGz5z4JYRdnM2EIXlj1aBpXCdozenEPtXEVvHn2ELleW1w82nQ== dependencies: - "@azure/msal-common" "^12.0.0" + "@azure/msal-common" "14.4.0" jsonwebtoken "^9.0.0" uuid "^8.3.0" -"@azure/storage-blob@^12.13.0": - version "12.13.0" - resolved "https://registry.yarnpkg.com/@azure/storage-blob/-/storage-blob-12.13.0.tgz#9209cbb5c2cd463fb967a0f2ae144ace20879160" - integrity sha512-t3Q2lvBMJucgTjQcP5+hvEJMAsJSk0qmAnjDLie2td017IiduZbbC9BOcFfmwzR6y6cJdZOuewLCNFmEx9IrXA== +"@azure/storage-blob@^12.17.0": + version "12.17.0" + resolved "https://registry.yarnpkg.com/@azure/storage-blob/-/storage-blob-12.17.0.tgz#04aad7f59cb08dbbe5b1b672a9f5b6256c8c9006" + integrity sha512-sM4vpsCpcCApagRW5UIjQNlNylo02my2opgp0Emi8x888hZUvJ3dN69Oq20cEGXkMUWnoCrBaB0zyS3yeB87sQ== dependencies: "@azure/abort-controller" "^1.0.0" "@azure/core-http" "^3.0.0"