Merge pull request #365 from 50Wliu/rethink-azureextensionui-dependency

Remove vscode-azureextensionui
This commit is contained in:
Winston Liu 2021-01-25 21:38:29 -05:00 коммит произвёл GitHub
Родитель fbcd714412 43019c784c
Коммит f7d0079da8
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
19 изменённых файлов: 562 добавлений и 520 удалений

205
package-lock.json сгенерированный
Просмотреть файл

@ -36,6 +36,12 @@
"@types/node": "*"
}
},
"@types/html-to-text": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/@types/html-to-text/-/html-to-text-5.1.2.tgz",
"integrity": "sha512-56PHFwRe18fmZ+UsN6GqUaoKfalOwFzxaBMRA2O5URde3d5mxdH1HvnIV7LSugCiO8JWlRdSL2L3xJXykCTKVg==",
"dev": true
},
"@types/minimatch": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
@ -695,15 +701,6 @@
"ms-rest-azure": "^2.5.5"
}
},
"azure-arm-storage": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/azure-arm-storage/-/azure-arm-storage-3.2.0.tgz",
"integrity": "sha512-jrew8qgqCiJ66QyjAhQis7AXhK6hp5soypvcxio3g/b1mNJr7RsQ6vD5zBRebcvM+QBGzvv7COiXy/xzibyOgA==",
"requires": {
"ms-rest": "^2.2.2",
"ms-rest-azure": "^2.3.3"
}
},
"azure-arm-website": {
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/azure-arm-website/-/azure-arm-website-5.7.0.tgz",
@ -1700,9 +1697,9 @@
}
},
"diagnostic-channel-publishers": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.3.4.tgz",
"integrity": "sha512-SZ1zMfFiEabf4Qx0Og9V1gMsRoqz3O+5ENkVcNOfI+SMJ3QhQsdEoKX99r0zvreagXot2parPxmrwwUM/ja8ug=="
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.3.5.tgz",
"integrity": "sha512-AOIjw4T7Nxl0G2BoBPhkQ6i7T4bUd9+xvdYizwvG7vVAM1dvr+SDrcUudlmzwH0kbEwdR2V1EcnKT0wAeYLQNQ=="
},
"diff": {
"version": "3.5.0",
@ -1739,14 +1736,14 @@
},
"dependencies": {
"domelementtype": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz",
"integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ=="
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.2.tgz",
"integrity": "sha512-wFwTwCVebUrMgGeAwRL/NhZtHAUyT9n9yg4IMDwf10+6iCMxSkVq9MGCVEH+QZWo1nNidy8kNvwmv4zWHDTqvA=="
},
"entities": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.0.2.tgz",
"integrity": "sha512-dmD3AvJQBUjKpcNkoqr+x+IF0SdRtPz9Vk0uTy4yWqga9ibB6s4v++QFWNohjiUGoMlF552ZvNyXDxz5iW0qmw=="
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz",
"integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w=="
}
}
},
@ -2280,16 +2277,6 @@
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
"dev": true
},
"fs-extra": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz",
"integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==",
"requires": {
"graceful-fs": "^4.1.2",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
}
},
"fs-write-stream-atomic": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
@ -2431,7 +2418,8 @@
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw=="
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"dev": true
},
"growl": {
"version": "1.10.5",
@ -2566,14 +2554,14 @@
}
},
"html-to-text": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-4.0.0.tgz",
"integrity": "sha512-QQl5EEd97h6+3crtgBhkEAO6sQnZyDff8DAeJzoSkOc1Dqe1UvTUZER0B+KjBe6fPZqq549l2VUhtracus3ndA==",
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-5.1.1.tgz",
"integrity": "sha512-Bci6bD/JIfZSvG4s0gW/9mMKwBRoe/1RWLxUME/d6WUSZCdY7T60bssf/jFf7EYXRyqU4P5xdClVqiYU0/ypdA==",
"requires": {
"he": "^1.0.0",
"htmlparser2": "^3.9.2",
"lodash": "^4.17.4",
"optimist": "^0.6.1"
"he": "^1.2.0",
"htmlparser2": "^3.10.1",
"lodash": "^4.17.11",
"minimist": "^1.2.0"
}
},
"htmlparser2": {
@ -2830,7 +2818,8 @@
"is-wsl": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
"integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0="
"integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
"dev": true
},
"isarray": {
"version": "1.0.0",
@ -2919,6 +2908,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
"dev": true,
"requires": {
"graceful-fs": "^4.1.6"
}
@ -3255,9 +3245,9 @@
}
},
"minimist": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
"integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8="
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
},
"mississippi": {
"version": "3.0.0",
@ -3697,23 +3687,6 @@
"wrappy": "1"
}
},
"opn": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/opn/-/opn-6.0.0.tgz",
"integrity": "sha512-I9PKfIZC+e4RXZ/qr1RhgyCnGgYX0UEIlXgWnCOVACIvFgaC9rz6Won7xbdhoHrd8IIhV7YEpHjreNUNkqCGkQ==",
"requires": {
"is-wsl": "^1.1.0"
}
},
"optimist": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
"integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
"requires": {
"minimist": "~0.0.1",
"wordwrap": "~0.0.2"
}
},
"os-browserify": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
@ -5684,7 +5657,8 @@
"universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"dev": true
},
"unset-value": {
"version": "1.0.0",
@ -5860,70 +5834,12 @@
}
}
},
"vscode-azureextensionui": {
"version": "0.26.3",
"resolved": "https://registry.npmjs.org/vscode-azureextensionui/-/vscode-azureextensionui-0.26.3.tgz",
"integrity": "sha512-bWWj7S4+PSAFgwoHUcZSnjo0s7qLNAHxn/yY4EbvyzjPff83BgMe6Mkla+h0F7l33Pfc75VZoZKl3v6kNNzwcA==",
"requires": {
"azure-arm-resource": "^3.0.0-preview",
"azure-arm-storage": "^3.1.0",
"fs-extra": "^4.0.3",
"html-to-text": "^4.0.0",
"ms-rest": "^2.2.2",
"ms-rest-azure": "^2.4.4",
"opn": "^6.0.0",
"semver": "^5.6.0",
"vscode-extension-telemetry": "^0.1.0",
"vscode-nls": "^4.0.0"
},
"dependencies": {
"azure-arm-resource": {
"version": "3.0.0-preview",
"resolved": "https://registry.npmjs.org/azure-arm-resource/-/azure-arm-resource-3.0.0-preview.tgz",
"integrity": "sha512-5AxK9Nnk9hRDtyiXkaqvHV2BvII12JpPpTTHL8p9ZKgkwy67mWk+repoe9PnjxwG2Rm1RadutonccynJ+VHVAw==",
"requires": {
"ms-rest": "^2.0.0",
"ms-rest-azure": "^2.0.0"
}
},
"vscode-extension-telemetry": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.6.tgz",
"integrity": "sha512-rbzSg7k4NnsCdF4Lz0gI4jl3JLXR0hnlmfFgsY8CSDYhXgdoIxcre8jw5rjkobY0xhSDhbG7xCjP8zxskySJ/g==",
"requires": {
"applicationinsights": "1.7.4"
}
},
"vscode-nls": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-4.1.2.tgz",
"integrity": "sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw=="
}
}
},
"vscode-extension-telemetry": {
"version": "0.0.18",
"resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.18.tgz",
"integrity": "sha512-Vw3Sr+dZwl+c6PlsUwrTtCOJkgrmvS3OUVDQGcmpXWAgq9xGq6as0K4pUx+aGqTjzLAESmWSrs6HlJm6J6Khcg==",
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.6.tgz",
"integrity": "sha512-rbzSg7k4NnsCdF4Lz0gI4jl3JLXR0hnlmfFgsY8CSDYhXgdoIxcre8jw5rjkobY0xhSDhbG7xCjP8zxskySJ/g==",
"requires": {
"applicationinsights": "1.0.1"
},
"dependencies": {
"applicationinsights": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-1.0.1.tgz",
"integrity": "sha1-U0Rrgw/o1dYZ7uKieLMdPSUDCSc=",
"requires": {
"diagnostic-channel": "0.2.0",
"diagnostic-channel-publishers": "0.2.1",
"zone.js": "0.7.6"
}
},
"diagnostic-channel-publishers": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz",
"integrity": "sha1-ji1geottef6IC1SLxYzGvrKIxPM="
}
"applicationinsights": "1.7.4"
}
},
"vscode-json-languageservice": {
@ -5935,6 +5851,13 @@
"vscode-languageserver-types": "^3.6.1",
"vscode-nls": "^3.2.1",
"vscode-uri": "^1.0.3"
},
"dependencies": {
"vscode-uri": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.8.tgz",
"integrity": "sha512-obtSWTlbJ+a+TFRYGaUumtVwb+InIUVI0Lu0VBUAPmj2cU5JutEXg3xUE0c2J5Tcy7h2DEKVJBFi+Y9ZSFzzPQ=="
}
}
},
"vscode-jsonrpc": {
@ -5968,25 +5891,6 @@
"lru-cache": "^6.0.0"
}
},
"vscode-jsonrpc": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz",
"integrity": "sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg=="
},
"vscode-languageserver-protocol": {
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz",
"integrity": "sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==",
"requires": {
"vscode-jsonrpc": "6.0.0",
"vscode-languageserver-types": "3.16.0"
}
},
"vscode-languageserver-types": {
"version": "3.16.0",
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz",
"integrity": "sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA=="
},
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
@ -6001,6 +5905,13 @@
"requires": {
"vscode-languageserver-protocol": "^3.7.0",
"vscode-uri": "^1.0.1"
},
"dependencies": {
"vscode-uri": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.8.tgz",
"integrity": "sha512-obtSWTlbJ+a+TFRYGaUumtVwb+InIUVI0Lu0VBUAPmj2cU5JutEXg3xUE0c2J5Tcy7h2DEKVJBFi+Y9ZSFzzPQ=="
}
}
},
"vscode-languageserver-protocol": {
@ -6041,9 +5952,9 @@
}
},
"vscode-uri": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.6.tgz",
"integrity": "sha512-sLI2L0uGov3wKVb9EB+vIQBl9tVP90nqRvxSoJ35vI3NjxE8jfsE5DSOhWgSunHSZmKS4OCi2jrtfxK7uyp2ww=="
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.2.tgz",
"integrity": "sha512-jkjy6pjU1fxUvI51P+gCsxg1u2n8LSt0W6KrCNQceaziKzff74GoWmjVG46KieVzybO1sttPQmYfrwSHey7GUA=="
},
"watchpack": {
"version": "1.7.2",
@ -6968,11 +6879,6 @@
"source-map": "~0.6.1"
}
},
"wordwrap": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
"integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="
},
"worker-farm": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
@ -7037,11 +6943,6 @@
"compress-commons": "^2.1.1",
"readable-stream": "^3.4.0"
}
},
"zone.js": {
"version": "0.7.6",
"resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.7.6.tgz",
"integrity": "sha1-+7w50+AmHQmG8boGMG6zrrDSIAk="
}
}
}

Просмотреть файл

@ -38,8 +38,7 @@
],
"activationEvents": [
"onLanguage:azure-pipelines",
"onCommand:azure-pipelines.configure-pipeline",
"onCommand:azure-pipelines.browse-pipeline"
"onCommand:azure-pipelines.configure-pipeline"
],
"main": "./out/extension",
"contributes": {
@ -103,11 +102,6 @@
"command": "azure-pipelines.configure-pipeline",
"title": "Configure Pipeline",
"category": "Azure Pipelines"
},
{
"command": "azure-pipelines.browse-pipeline",
"title": "Browse Pipeline",
"category": "Azure Pipelines"
}
],
"menus": {
@ -117,12 +111,6 @@
"group": "Azure Pipelines",
"when": "explorerResourceIsFolder == true"
}
],
"commandPalette": [
{
"command": "azure-pipelines.browse-pipeline",
"when": "never"
}
]
}
},
@ -135,9 +123,10 @@
},
"devDependencies": {
"@types/fs-extra": "4.0.5",
"@types/html-to-text": "^5.1.2",
"@types/mocha": "5.2.5",
"@types/mustache": "0.8.32",
"@types/node": "^12.0.0",
"@types/node": "^12.12.2",
"@types/q": "1.5.0",
"@types/underscore": "1.8.9",
"@types/vscode": "^1.45.1",
@ -155,6 +144,7 @@
"azure-arm-resource": "7.3.0",
"azure-arm-website": "5.7.0",
"azure-pipelines-language-server": "0.6.0",
"html-to-text": "^5.1.1",
"mustache": "3.0.1",
"q": "1.5.1",
"shelljs": "^0.3.0",
@ -162,11 +152,9 @@
"typed-rest-client": "1.0.7",
"underscore": "1.9.1",
"uuid": "^3.3.2",
"vscode-azureextensionui": "0.26.3",
"vscode-extension-telemetry": "0.0.18",
"vscode-extension-telemetry": "^0.1.6",
"vscode-languageclient": "^7.0.0",
"vscode-nls": "3.2.4",
"vscode-uri": "1.0.6"
"vscode-uri": "^3.0.2"
},
"extensionDependencies": [
"ms-vscode.azure-account"

Просмотреть файл

@ -1,44 +1,26 @@
import * as vscode from 'vscode';
import { registerCommand, IActionContext, createApiProvider, AzureTreeItem } from 'vscode-azureextensionui';
import { AzureExtensionApi, AzureExtensionApiProvider } from 'vscode-azureextensionui/api';
import { browsePipeline } from './browse';
import { configurePipeline } from './configure';
import { Messages } from './resources/messages';
import { AzureAccountExtensionExports, extensionVariables } from './model/models';
import { telemetryHelper } from './helper/telemetryHelper';
import { telemetryHelper } from '../helpers/telemetryHelper';
export async function activateConfigurePipeline(): Promise<AzureExtensionApiProvider> {
export async function activateConfigurePipeline(): Promise<void> {
let azureAccountExtension = vscode.extensions.getExtension("ms-vscode.azure-account");
if (!azureAccountExtension) {
throw new Error(Messages.azureAccountExntesionUnavailable);
}
if (!azureAccountExtension.isActive) {
await azureAccountExtension.activate();
}
extensionVariables.azureAccountExtensionApi = <AzureAccountExtensionExports>azureAccountExtension.exports;
// The command has been defined in the package.json file
// Now provide the implementation of the command with registerCommand
// The commandId parameter must match the command field in package.json
registerCommand('azure-pipelines.configure-pipeline', async (actionContext: IActionContext, node: any) => {
// The code you place here will be executed every time your command is executed
telemetryHelper.initialize(actionContext, 'configure-pipeline');
await configurePipeline(node);
});
vscode.commands.registerCommand('azure-pipelines.configure-pipeline', async () => {
if (!azureAccountExtension.isActive) {
await azureAccountExtension.activate();
}
registerCommand('azure-pipelines.browse-pipeline', async (actionContext: IActionContext, node: AzureTreeItem) => {
// The code you place here will be executed every time your command is executed
telemetryHelper.initialize(actionContext, 'browse-pipeline');
await browsePipeline(node);
telemetryHelper.initialize('configure-pipeline');
await telemetryHelper.callWithTelemetryAndErrorHandling(async () => {
await configurePipeline();
});
});
return createApiProvider([<AzureExtensionApi>
{
configurePipelineApi: configurePipeline,
browsePipeline: browsePipeline,
apiVersion: "0.0.1"
}]);
}

Просмотреть файл

@ -1,61 +0,0 @@
import * as vscode from 'vscode';
import { AzureTreeItem, UserCancelledError } from 'vscode-azureextensionui';
import { AppServiceClient, ScmType } from './clients/azure/appServiceClient';
import { getSubscriptionSession } from './helper/azureSessionHelper';
import { ControlProvider } from './helper/controlProvider';
import { AzureSession, ParsedAzureResourceId, extensionVariables } from './model/models';
import * as constants from './resources/constants';
import { Messages } from './resources/messages';
import { telemetryHelper, Result } from './helper/telemetryHelper';
import { TelemetryKeys } from './resources/telemetryKeys';
export async function browsePipeline(node: AzureTreeItem): Promise<void> {
await telemetryHelper.executeFunctionWithTimeTelemetry(async () => {
try {
if (!!node && !!node.fullId) {
let parsedAzureResourceId: ParsedAzureResourceId = new ParsedAzureResourceId(node.fullId);
let session: AzureSession = getSubscriptionSession(parsedAzureResourceId.subscriptionId);
let appServiceClient = new AppServiceClient(session.credentials, session.tenantId, session.environment.portalUrl, parsedAzureResourceId.subscriptionId);
let siteConfig = await appServiceClient.getAppServiceConfig(node.fullId);
telemetryHelper.setTelemetry(TelemetryKeys.ScmType, siteConfig.scmType);
let controlProvider = new ControlProvider();
if (siteConfig.scmType.toLowerCase() === ScmType.VSTSRM.toLowerCase()) {
let pipelineUrl = await appServiceClient.getAzurePipelineUrl(node.fullId);
vscode.env.openExternal(vscode.Uri.parse(pipelineUrl));
telemetryHelper.setTelemetry(TelemetryKeys.BrowsedExistingPipeline, 'true');
}
else if (siteConfig.scmType === '' || siteConfig.scmType.toLowerCase() === ScmType.NONE.toLowerCase()) {
let result = await controlProvider.showInformationBox(
constants.BrowseNotAvailableConfigurePipeline,
Messages.browseNotAvailableConfigurePipeline,
'Configure Pipeline');
if (result === 'Configure Pipeline') {
vscode.commands.executeCommand('azure-pipelines.configure-pipeline', node);
telemetryHelper.setTelemetry(TelemetryKeys.ClickedConfigurePipeline, 'true');
}
}
else {
let deploymentCenterUrl: string = await appServiceClient.getDeploymentCenterUrl(node.fullId);
await vscode.env.openExternal(vscode.Uri.parse(deploymentCenterUrl));
telemetryHelper.setTelemetry(TelemetryKeys.BrowsedDeploymentCenter, 'true');
}
}
else {
throw new Error(Messages.didNotRecieveAzureResourceNodeToProcess);
}
}
catch (error) {
if (!(error instanceof UserCancelledError)) {
extensionVariables.outputChannel.appendLine(error.message);
vscode.window.showErrorMessage(error.message);
telemetryHelper.setResult(Result.Failed, error);
}
else {
telemetryHelper.setResult(Result.Canceled, error);
}
}
}, TelemetryKeys.CommandExecutionDuration);
}

Просмотреть файл

@ -5,7 +5,7 @@ import { AzureDevOpsBaseUrl, ReservedHostNames } from '../../resources/constants
import { RestClient } from '../restClient';
import { ServiceClientCredentials, UrlBasedRequestPrepareOptions } from 'ms-rest';
import { sleepForMilliSeconds, stringCompareFunction } from "../../helper/commonHelper";
import { telemetryHelper } from '../../helper/telemetryHelper';
import { telemetryHelper } from '../../../helpers/telemetryHelper';
import * as Q from 'q';
import * as util from 'util';

Просмотреть файл

@ -2,7 +2,6 @@ const uuid = require('uuid/v4');
import { AppServiceClient } from './clients/azure/appServiceClient';
import { AzureDevOpsClient } from './clients/devOps/azureDevOpsClient';
import { AzureDevOpsHelper } from './helper/devOps/azureDevOpsHelper';
import { AzureTreeItem, UserCancelledError } from 'vscode-azureextensionui';
import { generateDevOpsProjectName, generateDevOpsOrganizationName } from './helper/commonHelper';
import { GenericResource } from 'azure-arm-resource/lib/resource/models';
import { GraphHelper } from './helper/graphHelper';
@ -11,55 +10,39 @@ import { Messages } from './resources/messages';
import { ServiceConnectionHelper } from './helper/devOps/serviceConnectionHelper';
import { SourceOptions, RepositoryProvider, extensionVariables, WizardInputs, WebAppKind, PipelineTemplate, QuickPickItemWithData, GitRepositoryParameters, GitBranchDetails, TargetResourceType } from './model/models';
import { TracePoints } from './resources/tracePoints';
import { TelemetryKeys } from './resources/telemetryKeys';
import { TelemetryKeys } from '../helpers/telemetryKeys';
import * as constants from './resources/constants';
import * as path from 'path';
import * as templateHelper from './helper/templateHelper';
import * as utils from 'util';
import * as vscode from 'vscode';
import { Result, telemetryHelper } from './helper/telemetryHelper';
import { telemetryHelper } from '../helpers/telemetryHelper';
import { ControlProvider } from './helper/controlProvider';
import { GitHubProvider } from './helper/gitHubHelper';
import { getSubscriptionSession } from './helper/azureSessionHelper';
import {Build} from './model/azureDevOps';
import { Build } from './model/azureDevOps';
import { UserCancelledError } from './helper/userCancelledError';
const Layer: string = 'configure';
export async function configurePipeline(node: AzureTreeItem) {
await telemetryHelper.executeFunctionWithTimeTelemetry(async () => {
try {
if (!(await extensionVariables.azureAccountExtensionApi.waitForLogin())) {
// set telemetry
telemetryHelper.setTelemetry(TelemetryKeys.AzureLoginRequired, 'true');
export async function configurePipeline() {
if (!(await extensionVariables.azureAccountExtensionApi.waitForLogin())) {
telemetryHelper.setTelemetry(TelemetryKeys.AzureLoginRequired, 'true');
let signIn = await vscode.window.showInformationMessage(Messages.azureLoginRequired, Messages.signInLabel);
if (signIn && signIn.toLowerCase() === Messages.signInLabel.toLowerCase()) {
await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, title: Messages.waitForAzureSignIn },
async () => {
await vscode.commands.executeCommand("azure-account.login");
});
}
else {
let error = new Error(Messages.azureLoginRequired);
telemetryHelper.setResult(Result.Failed, error);
throw error;
}
}
let signIn = await vscode.window.showInformationMessage(Messages.azureLoginRequired, Messages.signInLabel);
if (signIn && signIn.toLowerCase() === Messages.signInLabel.toLowerCase()) {
await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, title: Messages.waitForAzureSignIn },
async () => {
await vscode.commands.executeCommand("azure-account.login");
});
}
else {
throw new Error(Messages.azureLoginRequired);
}
}
var configurer = new PipelineConfigurer();
await configurer.configure(node);
}
catch (error) {
if (!(error instanceof UserCancelledError)) {
extensionVariables.outputChannel.appendLine(error.message);
vscode.window.showErrorMessage(error.message);
telemetryHelper.setResult(Result.Failed, error);
}
else {
telemetryHelper.setResult(Result.Canceled, error);
}
}
}, TelemetryKeys.CommandExecutionDuration);
const configurer = new PipelineConfigurer();
await configurer.configure();
}
class PipelineConfigurer {
@ -79,9 +62,9 @@ class PipelineConfigurer {
this.controlProvider = new ControlProvider();
}
public async configure(node: any) {
public async configure() {
telemetryHelper.setCurrentStep('GetAllRequiredInputs');
await this.getAllRequiredInputs(node);
await this.getAllRequiredInputs();
telemetryHelper.setCurrentStep('CreatePreRequisites');
await this.createPreRequisites();
@ -116,8 +99,7 @@ class PipelineConfigurer {
});
}
private async getAllRequiredInputs(node: any) {
await this.analyzeNode(node);
private async getAllRequiredInputs() {
await this.getSourceRepositoryDetails();
await this.getSelectedPipeline();
@ -171,16 +153,6 @@ class PipelineConfigurer {
}
}
private async analyzeNode(node: any): Promise<void> {
if (!!node && !!node.fullId) {
await this.extractAzureResourceFromNode(node);
}
else if (node && node.fsPath) {
this.workspacePath = node.fsPath;
telemetryHelper.setTelemetry(TelemetryKeys.SourceRepoLocation, SourceOptions.CurrentWorkspace);
}
}
private async getSourceRepositoryDetails(): Promise<void> {
try {
if (!this.workspacePath) { // This is to handle when we have already identified the repository details.
@ -325,37 +297,6 @@ class PipelineConfigurer {
return githubPat;
}
private async extractAzureResourceFromNode(node: any): Promise<void> {
this.inputs.targetResource.subscriptionId = node.root.subscriptionId;
this.inputs.azureSession = getSubscriptionSession(this.inputs.targetResource.subscriptionId);
this.appServiceClient = new AppServiceClient(this.inputs.azureSession.credentials, this.inputs.azureSession.tenantId, this.inputs.azureSession.environment.portalUrl, this.inputs.targetResource.subscriptionId);
try {
let azureResource: GenericResource = await this.appServiceClient.getAppServiceResource((<AzureTreeItem>node).fullId);
switch (azureResource.type ? azureResource.type.toLowerCase() : '') {
case 'Microsoft.Web/sites'.toLowerCase():
switch (azureResource.kind ? azureResource.kind.toLowerCase() : '') {
case WebAppKind.WindowsApp:
this.inputs.targetResource.resource = azureResource;
break;
case WebAppKind.FunctionApp:
case WebAppKind.LinuxApp:
case WebAppKind.LinuxContainerApp:
default:
throw new Error(utils.format(Messages.appKindIsNotSupported, azureResource.kind));
}
break;
default:
throw new Error(utils.format(Messages.resourceTypeIsNotSupported, azureResource.type));
}
}
catch (error) {
telemetryHelper.logError(Layer, TracePoints.ExtractAzureResourceFromNodeFailed, error);
throw error;
}
}
private async getAzureDevOpsDetails(): Promise<void> {
try {
this.createAzureDevOpsClient();
@ -451,10 +392,10 @@ class PipelineConfigurer {
let selectedSubscription: QuickPickItemWithData = await this.controlProvider.showQuickPick(constants.SelectSubscription, subscriptionList, { placeHolder: Messages.selectSubscription });
this.inputs.targetResource.subscriptionId = selectedSubscription.data.subscription.subscriptionId;
this.inputs.azureSession = getSubscriptionSession(this.inputs.targetResource.subscriptionId);
// show available resources and get the chosen one
this.appServiceClient = new AppServiceClient(this.inputs.azureSession.credentials, this.inputs.azureSession.tenantId, this.inputs.azureSession.environment.portalUrl, this.inputs.targetResource.subscriptionId);
let resourceArray: Promise<Array<{label: string, data: GenericResource}>> = null;
let selectAppText: string = "";
let placeHolderText: string = "";
@ -474,7 +415,7 @@ class PipelineConfigurer {
resourceArray,
{ placeHolder: placeHolderText },
TelemetryKeys.WebAppListCount);
this.inputs.targetResource.resource = selectedResource.data;
} else if(subscriptionList.length > 0 ) {
this.inputs.targetResource.subscriptionId = subscriptionList[0].data.subscription.subscriptionId;
@ -623,7 +564,7 @@ class PipelineConfigurer {
}
else {
telemetryHelper.setTelemetry(TelemetryKeys.PipelineDiscarded, 'true');
throw new UserCancelledError(Messages.operationCancelled);
throw new UserCancelledError();
}
}
}

Просмотреть файл

@ -8,8 +8,8 @@ import * as vscode from 'vscode';
import { RemoteWithoutRefs } from 'simple-git/typings/response';
import {AzureDevOpsHelper} from './devOps/azureDevOpsHelper';
import {GitHubProvider} from './gitHubHelper';
import { telemetryHelper } from "./telemetryHelper";
import { TelemetryKeys } from "../resources/telemetryKeys";
import { telemetryHelper } from "../../helpers/telemetryHelper";
import { TelemetryKeys } from "../../helpers/telemetryKeys";
export class LocalGitRepoHelper {
private gitReference: git.SimpleGit;

Просмотреть файл

@ -1,15 +1,13 @@
import { InputBoxOptions, QuickPickItem, window } from 'vscode';
import { IAzureQuickPickOptions, UserCancelledError } from 'vscode-azureextensionui';
import { telemetryHelper } from '../helper/telemetryHelper';
import { extensionVariables } from '../model/models';
import {Messages} from '../resources/messages';
import { TelemetryKeys } from '../resources/telemetryKeys';
import { InputBoxOptions, QuickPickItem, QuickPickOptions, window } from 'vscode';
import { telemetryHelper } from '../../helpers/telemetryHelper';
import { TelemetryKeys } from '../../helpers/telemetryKeys';
import { UserCancelledError } from './userCancelledError';
export class ControlProvider {
public async showQuickPick<T extends QuickPickItem>(listName: string, listItems: T[] | Thenable<T[]>, options: IAzureQuickPickOptions, itemCountTelemetryKey?: string): Promise<T> {
public async showQuickPick<T extends QuickPickItem>(listName: string, listItems: T[] | Thenable<T[]>, options: QuickPickOptions, itemCountTelemetryKey?: string): Promise<T> {
try {
telemetryHelper.setTelemetry(TelemetryKeys.CurrentUserInput, listName);
return await extensionVariables.ui.showQuickPick(listItems, options);
return window.showQuickPick(listItems, options);
}
finally {
if (itemCountTelemetryKey) {
@ -20,7 +18,7 @@ export class ControlProvider {
public async showInputBox(inputName: string, options: InputBoxOptions): Promise<string> {
telemetryHelper.setTelemetry(TelemetryKeys.CurrentUserInput, inputName);
return await extensionVariables.ui.showInputBox(options);
return window.showInputBox(options);
}
public async showInformationBox(informationIdentifier: string, informationMessage: string, ...actions: string[]): Promise<string> {
@ -28,13 +26,13 @@ export class ControlProvider {
if (!!actions && actions.length > 0) {
let result = await window.showInformationMessage(informationMessage, ...actions);
if (!result) {
throw new UserCancelledError(Messages.userCancelledExcecption);
throw new UserCancelledError();
}
return result;
}
else {
return await window.showInformationMessage(informationMessage, ...actions);
return window.showInformationMessage(informationMessage, ...actions);
}
}

Просмотреть файл

@ -2,7 +2,7 @@ import { AzureDevOpsClient } from '../../clients/devOps/azureDevOpsClient';
import { BuildDefinition, BuildDefinitionRepositoryProperties, Build } from '../../model/azureDevOps';
import { HostedVS2017QueueName } from '../../resources/constants';
import { Messages } from '../../resources/messages';
import { telemetryHelper } from '../telemetryHelper';
import { telemetryHelper } from '../../../helpers/telemetryHelper';
import { TracePoints } from '../../resources/tracePoints';
import { WizardInputs, RepositoryProvider } from '../../model/models';
import * as util from 'util';

Просмотреть файл

@ -1,87 +0,0 @@
import { IActionContext, ITelemetryReporter, parseError } from "vscode-azureextensionui";
import { extensionVariables } from "../model/models";
import { TelemetryKeys } from '../resources/telemetryKeys';
import * as logger from '../../logger';
const uuid = require('uuid/v4');
class TelemetryHelper {
private actionContext: IActionContext;
private telemetryReporter: ITelemetryReporter;
private journeyId: string;
private command: string;
public initialize(actionContext: IActionContext, command: string): void {
this.actionContext = actionContext;
this.telemetryReporter = extensionVariables.reporter;
this.journeyId = uuid();
this.command = command;
this.setTelemetry(TelemetryKeys.JourneyId, this.journeyId);
}
public getJourneyId(): string {
return this.journeyId;
}
public setTelemetry(key: string, value: string): void {
if (key) {
this.actionContext.telemetry.properties[key] = value;
}
}
public setResult(result: Result, error?: Error): void {
this.actionContext.telemetry.properties.result = result;
if (error) {
let parsedError = parseError(error);
this.actionContext.telemetry.properties.error = JSON.stringify(parsedError);
this.actionContext.telemetry.properties.errorMessage = parsedError.message;
}
}
public setCurrentStep(stepName: string): void {
this.actionContext.telemetry.properties.cancelStep = stepName;
}
public logError(layer: string, tracePoint: string, error: Error): void {
let parsedError = parseError(error);
this.telemetryReporter.sendTelemetryEvent(
tracePoint,
{
'journeyId': this.journeyId,
'command': this.command,
'layer': layer,
'error': JSON.stringify(parsedError)
});
logger.log(JSON.stringify(parsedError));
}
public logInfo(layer: string, tracePoint: string, info: string): void {
this.telemetryReporter.sendTelemetryEvent(
tracePoint,
{
'journeyId': this.journeyId,
'command': this.command,
'layer': layer,
'info': info
});
}
public async executeFunctionWithTimeTelemetry<T>(callback: () => T, telemetryKey: string): Promise<T> {
let startTime = Date.now();
try {
return await callback();
}
finally {
this.setTelemetry(telemetryKey, ((Date.now() - startTime) / 1000).toString());
}
}
}
export let telemetryHelper = new TelemetryHelper();
export enum Result {
'Succeeded' = 'Succeeded',
'Failed' = 'Failed',
'Canceled' = 'Canceled'
}

Просмотреть файл

@ -0,0 +1,8 @@
// https://github.com/microsoft/vscode-azuretools/blob/5999c2ad4423e86f22d2c648027242d8816a50e4/ui/src/errors.ts
// minus localization
export class UserCancelledError extends Error {
constructor() {
super('Operation cancelled.');
}
}

Просмотреть файл

@ -1,18 +1,13 @@
import { AzureEnvironment } from 'ms-rest-azure';
import { GenericResource } from 'azure-arm-resource/lib/resource/models';
import { OutputChannel, ExtensionContext, QuickPickItem } from 'vscode';
import { OutputChannel, QuickPickItem, window } from 'vscode';
import { ServiceClientCredentials } from 'ms-rest';
import { SubscriptionModels } from 'azure-arm-resource';
import { UIExtensionVariables, IAzureUserInput, ITelemetryReporter } from 'vscode-azureextensionui';
import { Messages } from '../resources/messages';
class ExtensionVariables implements UIExtensionVariables {
class ExtensionVariables {
public azureAccountExtensionApi: AzureAccountExtensionExports;
public context: ExtensionContext;
public outputChannel: OutputChannel;
public reporter: ITelemetryReporter;
public ui: IAzureUserInput;
public outputChannel: OutputChannel = window.createOutputChannel('Azure Pipelines');
}
let extensionVariables = new ExtensionVariables();

Просмотреть файл

@ -29,7 +29,6 @@ export class Messages {
public static notAGitRepository: string = 'Selected workspace is not a [Git](https://git-scm.com/docs/git) repository. Please select a Git repository.';
public static notAzureRepoUrl: string = 'The repo isn\'t hosted with Azure Repos.';
public static noWorkSpaceSelectedError: string = 'Please select a workspace folder to configure pipeline.';
public static operationCancelled: string = 'Operation cancelled.';
public static operationTimedOut: string = 'Operation timed out.';
public static organizationNameReservedMessage: string = 'The organization name %s isn\'t available. Please try another organization name.';
public static organizationNameStaticValidationMessage: string = 'Organization names must start and end with a letter or number and can contain only letters, numbers, and hyphens.';
@ -52,11 +51,8 @@ export class Messages {
public static retryFailedMessage: string =`Failed after retrying: %s times. Internal Error: %s`;
public static azureServicePrincipalFailedMessage: string =`Failed while creating Azure service principal.`;
public static roleAssignmentFailedMessage: string =`Failed while role assignement.`;
public static waitForAzureSignIn: string =`Waiting for Azure sign-in...`;
public static userCancelledExcecption = 'User cancelled the action';
public static waitForAzureSignIn: string = `Waiting for Azure sign-in...`;
public static cannotFindPipelineUrlInMetaDataException = 'We were unable to find pipeline associated with the Azure Web App. Please click on "Browse Pipeline" to explore.';
public static cannotFindOrganizationWithName = 'Unable to find organization with name: %s';
public static browseNotAvailableConfigurePipeline = 'Unable to find a pipeline for this Azure Web App. Please click on "Configure Pipeline" to setup.';
public static didNotRecieveAzureResourceNodeToProcess = 'Unable to browse the pipeline for you. Please raise an issue in the [repo](https://github.com/Microsoft/azure-pipelines-vscode/issues).';
}

Просмотреть файл

@ -5,50 +5,31 @@
import * as path from 'path';
import * as vscode from 'vscode';
import { createTelemetryReporter, callWithTelemetryAndErrorHandling, IActionContext, AzureUserInput, registerUIExtensionVariables } from 'vscode-azureextensionui';
import * as languageclient from 'vscode-languageclient/node';
import { extensionVariables } from './configure/model/models';
import * as logger from './logger';
import { SchemaAssociationService, SchemaAssociationNotification } from './schema-association-service';
import { schemaContributor, CUSTOM_SCHEMA_REQUEST, CUSTOM_CONTENT_REQUEST } from './schema-contributor';
import { telemetryHelper } from './configure/helper/telemetryHelper';
import { TelemetryKeys } from './configure/resources/telemetryKeys';
import { telemetryHelper } from './helpers/telemetryHelper';
export async function activate(context: vscode.ExtensionContext) {
extensionVariables.reporter = createTelemetryReporter(context);
registerUiVariables(context);
const configurePipelineEnabled = vscode.workspace.getConfiguration('azure-pipelines').get<boolean>('configure', true);
await callWithTelemetryAndErrorHandling('azurePipelines.activate', async (activateContext: IActionContext) => {
activateContext.telemetry.properties.isActivationEvent = 'true';
telemetryHelper.initialize(activateContext, 'activate');
telemetryHelper.setTelemetry('configurePipelineEnabled', `${configurePipelineEnabled}`);
await telemetryHelper.executeFunctionWithTimeTelemetry(
async () => {
await activateYmlContributor(context);
if (configurePipelineEnabled) {
const { activateConfigurePipeline } = await import('./configure/activate');
await activateConfigurePipeline();
}
},
TelemetryKeys.ExtensionActivationDuration);
telemetryHelper.initialize('azurePipelines.activate', {
isActivationEvent: 'true',
configurePipelineEnabled: `${configurePipelineEnabled}`,
});
await telemetryHelper.callWithTelemetryAndErrorHandling(async () => {
await activateYmlContributor(context);
if (configurePipelineEnabled) {
const { activateConfigurePipeline } = await import('./configure/activate');
await activateConfigurePipeline();
}
});
logger.log('Extension has been activated!', 'ExtensionActivated');
return schemaContributor;
}
function registerUiVariables(context: vscode.ExtensionContext) {
// Register ui extension variables is required to be done for telemetry to start flowing for extension activation and other events.
// It also facilitates registering command and called events telemetry.
extensionVariables.outputChannel = vscode.window.createOutputChannel('Azure Pipelines');
context.subscriptions.push(extensionVariables.outputChannel);
extensionVariables.context = context;
extensionVariables.ui = new AzureUserInput(context.globalState);
registerUIExtensionVariables(extensionVariables);
}
async function activateYmlContributor(context: vscode.ExtensionContext) {
const serverOptions: languageclient.ServerOptions = getServerOptions(context);
const clientOptions: languageclient.LanguageClientOptions = getClientOptions();
@ -61,25 +42,23 @@ async function activateYmlContributor(context: vscode.ExtensionContext) {
const initialSchemaAssociations = schemaAssociationService.getSchemaAssociation();
await client.onReady().then(() => {
// Notify the server which schemas to use.
client.sendNotification(SchemaAssociationNotification.type, initialSchemaAssociations);
// If this throws, the telemetry event in activate() will catch & log it
await client.onReady();
// Fired whenever the server is about to validate a YAML file (e.g. on content change),
// and allows us to return a custom schema to use for validation.
client.onRequest(CUSTOM_SCHEMA_REQUEST, (resource: string) => {
// TODO: Have a single instance for the extension but dont return a global from this namespace
return schemaContributor.requestCustomSchema(resource);
});
// Notify the server which schemas to use.
client.sendNotification(SchemaAssociationNotification.type, initialSchemaAssociations);
// Fired whenever the server encounters a URI scheme that it doesn't recognize,
// and allows us to use the URI to determine the schema's content.
client.onRequest(CUSTOM_CONTENT_REQUEST, (uri: string) => {
return schemaContributor.requestCustomSchemaContent(uri);
});
}).catch((reason) => {
logger.log(JSON.stringify(reason), 'ClientOnReadyError');
extensionVariables.reporter.sendTelemetryEvent('extension.languageserver.onReadyError', { 'reason': JSON.stringify(reason) });
// Fired whenever the server is about to validate a YAML file (e.g. on content change),
// and allows us to return a custom schema to use for validation.
client.onRequest(CUSTOM_SCHEMA_REQUEST, (resource: string) => {
// TODO: Have a single instance for the extension but dont return a global from this namespace
return schemaContributor.requestCustomSchema(resource);
});
// Fired whenever the server encounters a URI scheme that it doesn't recognize,
// and allows us to use the URI to determine the schema's content.
client.onRequest(CUSTOM_CONTENT_REQUEST, (uri: string) => {
return schemaContributor.requestCustomSchemaContent(uri);
});
// TODO: Can we get rid of this since it's set in package.json?
@ -125,4 +104,5 @@ function getClientOptions(): languageclient.LanguageClientOptions {
// this method is called when your extension is deactivated
export function deactivate() {
telemetryHelper.dispose();
}

244
src/helpers/parseError.ts Normal file
Просмотреть файл

@ -0,0 +1,244 @@
// Copied from https://github.com/microsoft/vscode-azuretools/blob/5999c2ad4423e86f22d2c648027242d8816a50e4/ui/src/parseError.ts
// with inline IParsedError interface and no localization
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as htmlToText from 'html-to-text';
export interface IParsedError {
errorType: string;
message: string;
stack?: string;
isUserCancelledError: boolean;
}
// tslint:disable:no-unsafe-any
// tslint:disable:no-any
export function parseError(error: any): IParsedError {
let errorType: string = '';
let message: string = '';
let stack: string | undefined;
if (typeof (error) === 'object' && error !== null) {
if (error.constructor !== Object) {
errorType = error.constructor.name;
}
stack = getCallstack(error);
errorType = getCode(error, errorType);
// See https://github.com/Microsoft/vscode-azureappservice/issues/419 for an example error that requires these 'unpack's
error = unpackErrorFromField(error, 'value');
error = unpackErrorFromField(error, '_value');
error = unpackErrorFromField(error, 'error');
error = unpackErrorFromField(error, 'error');
if (Array.isArray(error.errors) && error.errors.length) {
error = error.errors[0];
}
errorType = getCode(error, errorType);
message = getMessage(error, message);
if (!errorType || !message || /error.*deserializing.*response.*body/i.test(message)) {
error = unpackErrorFromField(error, 'response');
error = unpackErrorFromField(error, 'body');
errorType = getCode(error, errorType);
message = getMessage(error, message);
}
// Azure errors have a JSON object in the message
let parsedMessage: any = parseIfJson(error.message);
// For some reason, the message is sometimes serialized twice and we need to parse it again
parsedMessage = parseIfJson(parsedMessage);
// Extract out the "internal" error if it exists
if (parsedMessage && parsedMessage.error) {
parsedMessage = parsedMessage.error;
}
errorType = getCode(parsedMessage, errorType);
message = getMessage(parsedMessage, message);
message = message || convertCodeToError(errorType) || JSON.stringify(error);
} else if (error !== undefined && error !== null && error.toString && error.toString().trim() !== '') {
errorType = typeof (error);
message = error.toString();
}
message = unpackErrorsInMessage(message);
[message, errorType] = parseIfFileSystemError(message, errorType);
// tslint:disable-next-line:strict-boolean-expressions
errorType = errorType || typeof (error);
message = message || 'Unknown Error';
message = parseIfHtml(message);
// Azure storage SDK errors are presented in XML
// https://github.com/Azure/azure-sdk-for-js/issues/6927
message = parseIfXml(message);
return {
errorType: errorType,
message: message,
stack: stack,
// NOTE: Intentionally not using 'error instanceof UserCancelledError' because that doesn't work if multiple versions of the UI package are used in one extension
// See https://github.com/Microsoft/vscode-azuretools/issues/51 for more info
isUserCancelledError: errorType === 'UserCancelledError'
};
}
function convertCodeToError(errorType: string | undefined): string | undefined {
if (errorType) {
const code: number = parseInt(errorType, 10);
if (!isNaN(code)) {
return `Failed with code "${code}".`;
}
}
return undefined;
}
function parseIfJson(o: any): any {
if (typeof o === 'string' && o.indexOf('{') >= 0) {
try {
return JSON.parse(o);
} catch (err) {
// ignore
}
}
return o;
}
function parseIfHtml(message: string): string {
if (/<html/i.test(message)) {
try {
return htmlToText.fromString(message, { wordwrap: false, uppercaseHeadings: false, ignoreImage: true });
} catch (err) {
// ignore
}
}
return message;
}
function parseIfXml(message: string): string {
const matches: RegExpMatchArray | null = message.match(/<Message>(.*)<\/Message>/si);
if (matches) {
return matches[1];
}
return message;
}
function getMessage(o: any, defaultMessage: string): string {
return (o && (o.message || o.Message || o.detail || (typeof parseIfJson(o.body) === 'string' && o.body))) || defaultMessage;
}
function getCode(o: any, defaultCode: string): string {
const code: any = o && (o.code || o.Code || o.errorCode || o.statusCode);
return code ? String(code) : defaultCode;
}
function unpackErrorsInMessage(message: string): string {
// Handle messages like this from Azure (just handle first error for now)
// ["Errors":["The offer should have valid throughput]]",
if (message) {
const errorsInMessage: RegExpMatchArray | null = message.match(/"Errors":\[\s*"([^"]+)"/);
if (errorsInMessage !== null) {
const [, firstError] = errorsInMessage;
return firstError;
}
}
return message;
}
function unpackErrorFromField(error: any, prop: string): any {
// Handle objects from Azure SDK that contain the error information in a "body" field (serialized or not)
let field: any = error && error[prop];
if (field) {
if (typeof field === 'string' && field.indexOf('{') >= 0) {
try {
field = JSON.parse(field);
} catch (err) {
// Ignore
}
}
if (typeof field === 'object') {
return field;
}
}
return error;
}
/**
* Example line in the stack:
* at FileService.StorageServiceClient._processResponse (/path/ms-azuretools.vscode-azurestorage-0.6.0/node_modules/azure-storage/lib/common/services/storageserviceclient.js:751:50)
*
* Final minified line:
* FileService.StorageServiceClient._processResponse azure-storage/storageserviceclient.js:751:50
*/
function getCallstack(error: { stack?: string }): string | undefined {
// tslint:disable-next-line: strict-boolean-expressions
const stack: string = error.stack || '';
const minifiedLines: (string | undefined)[] = stack
.split(/(\r\n|\n)/g) // split by line ending
.map(l => {
let result: string = '';
// Get just the file name, line number and column number
// From above example: storageserviceclient.js:751:50
const fileMatch: RegExpMatchArray | null = l.match(/[^\/\\\(\s]+\.(t|j)s:[0-9]+:[0-9]+/i);
// Ignore any lines without a file match (e.g. "at Generator.next (<anonymous>)")
if (fileMatch) {
// Get the function name
// From above example: FileService.StorageServiceClient._processResponse
const functionMatch: RegExpMatchArray | null = l.match(/^[\s]*at ([^\(\\\/]+(?:\\|\/)?)+/i);
if (functionMatch) {
result += functionMatch[1];
}
const parts: string[] = [];
// Get the name of the node module (and any sub modules) containing the file
// From above example: azure-storage
const moduleRegExp: RegExp = /node_modules(?:\\|\/)([^\\\/]+)/ig;
let moduleMatch: RegExpExecArray | null;
do {
moduleMatch = moduleRegExp.exec(l);
if (moduleMatch) {
parts.push(moduleMatch[1]);
}
} while (moduleMatch);
parts.push(fileMatch[0]);
result += parts.join('/');
}
return result;
})
.filter(l => !!l);
return minifiedLines.length > 0 ? minifiedLines.join('\n') : undefined;
}
/**
* See https://github.com/microsoft/vscode-cosmosdb/issues/1580 for an example error
*/
function parseIfFileSystemError(message: string, errorType: string): [string, string] {
const match: RegExpMatchArray | null = message.match(/\((([a-z]*) \(FileSystemError\).*)\)$/i);
if (match) {
message = match[1];
errorType = match[2];
}
return [message, errorType];
}

Просмотреть файл

@ -0,0 +1,160 @@
import * as vscode from 'vscode';
import TelemetryReporter from 'vscode-extension-telemetry';
import { TelemetryKeys } from './telemetryKeys';
import * as logger from '../logger';
import { parseError } from './parseError';
const uuid = require('uuid/v4');
const extensionName = 'ms-azure-devops.azure-pipelines';
const packageJSON = vscode.extensions.getExtension(extensionName).packageJSON;
const extensionVersion = packageJSON.version;
const aiKey = packageJSON.aiKey;
interface TelemetryProperties {
[key: string]: string;
}
interface TelemetryOptions {
suppressIfSuccessful: boolean;
}
class TelemetryHelper {
private journeyId: string;
private command: string;
private properties: TelemetryProperties;
private options: TelemetryOptions;
private static reporter = new TelemetryReporter(extensionName, extensionVersion, aiKey);
public initialize(command: string, properties: TelemetryProperties = {}) {
this.journeyId = uuid();
this.command = command;
this.properties = properties;
this.options = {
suppressIfSuccessful: false,
};
this.setTelemetry(TelemetryKeys.JourneyId, this.journeyId);
this.setTelemetry(TelemetryKeys.Result, Result.Succeeded);
}
public dispose() {
TelemetryHelper.reporter.dispose();
}
public getJourneyId(): string {
return this.journeyId;
}
public setOptions(options: Partial<TelemetryOptions>): void {
this.options = {
...this.options,
...options,
};
}
public setTelemetry(key: string, value: string): void {
this.properties[key] = value;
}
public setCurrentStep(stepName: string): void {
this.properties.cancelStep = stepName;
}
// Log an error.
// No custom properties are logged alongside the error.
// FIXME: This should really be sendTelemetryException but I'm maintaining
// backwards-compatibility with how it used to be sent, especially because
// I don't have access to the Application Insights logs :D (winstonliu).
public logError(layer: string, tracePoint: string, error: Error): void {
TelemetryHelper.reporter.sendTelemetryErrorEvent(
tracePoint, {
[TelemetryKeys.JourneyId]: this.journeyId,
'command': this.command,
'layer': layer,
'errorMessage': error.message,
'stack': error.stack ?? '',
}, undefined, ['errorMesage', 'stack']);
}
// Log an informational message.
// No custom properties are logged alongside the message.
public logInfo(layer: string, tracePoint: string, info: string): void {
TelemetryHelper.reporter.sendTelemetryEvent(
tracePoint, {
[TelemetryKeys.JourneyId]: this.journeyId,
'command': this.command,
'layer': layer,
'info': info
});
}
// Executes the given function, timing how long it takes.
// This *does NOT* send any telemetry and must be called within the context
// of an ongoing `callWithTelemetryAndErrorHandling` session to do anything useful.
// Helpful for reporting fine-grained timing of individual functions.
// TODO: Rename to something with less potential for confusion, like 'time' or 'timeFunction'?
public async executeFunctionWithTimeTelemetry<T>(callback: () => Promise<T>, telemetryKey: string): Promise<T> {
const startTime = Date.now();
try {
return await callback();
}
finally {
this.setTelemetry(telemetryKey, ((Date.now() - startTime) / 1000).toString());
}
}
// Wraps the given function in a telemetry event.
// The telemetry event sent ater function execution will contain how long the function took as well as any custom properties
// supplied through initialize() or setTelemetry().
// If the function errors, the telemetry event will additionally contain metadata about the error that occurred.
// https://github.com/microsoft/vscode-azuretools/blob/5999c2ad4423e86f22d2c648027242d8816a50e4/ui/src/callWithTelemetryAndErrorHandling.ts
public async callWithTelemetryAndErrorHandling<T>(callback: () => Promise<T>): Promise<T | void> {
try {
return await this.executeFunctionWithTimeTelemetry(callback, 'duration');
} catch (error) {
const parsedError = parseError(error);
if (parsedError.isUserCancelledError) {
this.setTelemetry(TelemetryKeys.Result, Result.Canceled);
} else {
this.setTelemetry(TelemetryKeys.Result, Result.Failed);
this.setTelemetry('error', parsedError.errorType);
this.setTelemetry('errorMessage', parsedError.message);
this.setTelemetry('stack', parsedError.stack ?? '');
if (this.options.suppressIfSuccessful) {
this.setTelemetry('suppressTelemetry', 'true');
}
logger.log(parsedError.message);
if (parsedError.message.includes('\n')) {
vscode.window.showErrorMessage('An error has occurred. Check the output window for more details.');
} else {
vscode.window.showErrorMessage(parsedError.message);
}
}
} finally {
if (this.properties.result === Result.Failed) {
TelemetryHelper.reporter.sendTelemetryErrorEvent(
this.command, {
...this.properties,
[TelemetryKeys.JourneyId]: this.journeyId,
}, undefined, ['error', 'errorMesage', 'stack']);
} else if (!(this.options.suppressIfSuccessful && this.properties.result === Result.Succeeded)) {
TelemetryHelper.reporter.sendTelemetryEvent(
this.command, {
...this.properties,
[TelemetryKeys.JourneyId]: this.journeyId,
});
}
}
}
}
export const telemetryHelper = new TelemetryHelper();
enum Result {
'Succeeded' = 'Succeeded',
'Failed' = 'Failed',
'Canceled' = 'Canceled'
}

Просмотреть файл

@ -3,6 +3,7 @@ export class TelemetryKeys {
public static RepoProvider: string = 'repoProvider';
public static AzureLoginRequired: string = 'azureLoginRequired';
public static JourneyId: string = 'journeyId';
public static Result: string = 'result';
public static SourceRepoLocation: string = 'sourceRepoLocation';
public static NewOrganization: string = 'newOrganization';
public static ChosenTemplate: string = 'chosenTemplate';
@ -14,10 +15,6 @@ export class TelemetryKeys {
public static BrowsedDeploymentCenter = 'openedDeploymentCenter';
public static BrowsedExistingPipeline = 'browsedExistingPipeline';
public static ClickedConfigurePipeline = 'clickedConfigurePipeline';
// Durations
public static ExtensionActivationDuration = 'extensionActivationDuration';
public static CommandExecutionDuration = 'commandExecutionDuration';
public static GitHubPatDuration = 'gitHubPatDuration';
// Count of drop down items

Просмотреть файл

@ -3,7 +3,7 @@
* Licensed under the MIT License.
*--------------------------------------------------------------------------------------------*/
import Uri from 'vscode-uri'
import { URI } from 'vscode-uri';
interface SchemaContributorProvider {
readonly requestSchema: (resource: string) => string;
@ -75,10 +75,10 @@ class SchemaContributor {
*/
public requestCustomSchemaContent(uri: string): string {
if (uri) {
let _uri = Uri.parse(uri);
if (_uri.scheme && this._customSchemaContributors[_uri.scheme] &&
this._customSchemaContributors[_uri.scheme].requestSchemaContent) {
return this._customSchemaContributors[_uri.scheme].requestSchemaContent(uri);
const { scheme } = URI.parse(uri);
if (scheme && this._customSchemaContributors[scheme] &&
this._customSchemaContributors[scheme].requestSchemaContent) {
return this._customSchemaContributors[scheme].requestSchemaContent(uri);
}
}

Просмотреть файл

@ -4,7 +4,7 @@
"alwaysStrict": true,
/* List of library files to be included in the compilation. */
"lib": [ "es6" ],
"lib": [ "es2019" ],
/* Specify module code generation. */
"module": "commonjs",
@ -45,10 +45,10 @@
//"strict": true,
/* Specify ECMAScript target version. */
"target": "es6",
"target": "es2019",
/* */
"allowJs": true,
"allowJs": true
},
"include": [
"src/**/*",
@ -59,4 +59,4 @@
".vscode-test",
"tools/**"
]
}
}