Added browsed pipeline and combined with configure pipeline.

This commit is contained in:
Himanshu Yadav 2019-09-18 13:30:06 +05:30
Родитель e6a15fe9d4
Коммит 274a9f92f0
9 изменённых файлов: 116 добавлений и 23 удалений

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

@ -96,6 +96,11 @@
"command": "configure-pipeline",
"title": "Configure Pipeline",
"category": "Azure Pipelines"
},
{
"command": "browse-pipeline",
"title": "Browse Pipeline",
"category": "Azure Pipelines"
}
],
"menus": {
@ -105,6 +110,12 @@
"group": "Azure Pipelines",
"when": "explorerResourceIsFolder == true"
}
],
"commandPalette": [
{
"command": "browse-pipeline",
"when": "never"
}
]
}
},

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

@ -1,7 +1,8 @@
import * as vscode from 'vscode';
import { registerCommand, IActionContext, createApiProvider } from 'vscode-azureextensionui';
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';
@ -28,9 +29,16 @@ export async function activateConfigurePipeline(): Promise<AzureExtensionApiProv
await configurePipeline(node);
});
registerCommand('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);
});
return createApiProvider([<AzureExtensionApi>
{
configurePipelineApi: configurePipeline,
browsePipeline: browsePipeline,
apiVersion: "0.0.1"
}]);
}
}

64
src/configure/browse.ts Normal file
Просмотреть файл

@ -0,0 +1,64 @@
import * as vscode from 'vscode';
import { AzureTreeItem, UserCancelledError } from 'vscode-azureextensionui';
import { AppServiceClient, ScmTypes } 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, parsedAzureResourceId.subscriptionId);
let siteConfig = await appServiceClient.getAppServiceConfig(node.fullId);
telemetryHelper.setTelemetry(TelemetryKeys.ScmType, siteConfig.scmType);
let controlProvider = new ControlProvider();
if (siteConfig.scmType.toLowerCase() === ScmTypes.VSTSRM.toLowerCase()) {
let pipelineUrl = await appServiceClient.getVstsPipelineUrl(node.fullId);
vscode.env.openExternal(vscode.Uri.parse(pipelineUrl));
telemetryHelper.setTelemetry(TelemetryKeys.BrowsedExistingPipeline, 'true');
}
else if (siteConfig.scmType === '' || siteConfig.scmType.toLowerCase() === ScmTypes.NONE.toLowerCase()) {
let result = await controlProvider.showInformationBox(
constants.BrowseNotAvailableConfigurePipeline,
Messages.browseNotAvailableConfigurePipeline,
'Configure Pipeline');
if (result === 'Configure Pipeline') {
vscode.commands.executeCommand('configure-pipeline', node);
telemetryHelper.setTelemetry(TelemetryKeys.ClickedConfigurePipeline, 'true');
}
}
else {
let result = await controlProvider.showInformationBox(constants.DeploymentResourceAlreadyConfigured, Messages.deploymentCenterAlreadyConfigured, constants.BrowseDeploymentSource);
if (result === constants.BrowseDeploymentSource) {
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);
}

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

@ -6,7 +6,6 @@ import { ServiceClientCredentials } from 'ms-rest';
import { AzureResourceClient } from './azureResourceClient';
import { WebAppKind, ParsedAzureResourceId } from '../../model/models';
import * as Constants from '../../resources/constants';
import {Messages} from '../../resources/messages';
export class AppServiceClient extends AzureResourceClient {
@ -48,7 +47,7 @@ export class AppServiceClient extends AzureResourceClient {
}
public async getVstsPipelineUrl(resourceId: string): Promise<string> {
let metadata = await this.getSiteMetadata(resourceId);
let metadata = await this.getAppServiceMetadata(resourceId);
if (metadata.properties['VSTSRM_BuildDefinitionWebAccessUrl']) {
return metadata.properties['VSTSRM_BuildDefinitionWebAccessUrl'];
}
@ -63,7 +62,7 @@ export class AppServiceClient extends AzureResourceClient {
public async updateScmType(resourceId: string): Promise<SiteConfigResource> {
let siteConfig = await this.getAppServiceConfig(resourceId);
siteConfig.scmType = Constants.VstsRmScmType;
siteConfig.scmType = ScmTypes.VSTSRM;
let parsedResourceId: ParsedAzureResourceId = new ParsedAzureResourceId(resourceId);
return this.webSiteManagementClient.webApps.updateConfiguration(parsedResourceId.resourceGroup, parsedResourceId.resourceName, siteConfig);
}
@ -73,12 +72,12 @@ export class AppServiceClient extends AzureResourceClient {
return this.webSiteManagementClient.webApps.updateConfiguration(parsedResourceId.resourceGroup, parsedResourceId.resourceName, siteConfig);
}
public async getSiteMetadata(resourceId: string): Promise<StringDictionary> {
public async getAppServiceMetadata(resourceId: string): Promise<StringDictionary> {
let parsedResourceId: ParsedAzureResourceId = new ParsedAzureResourceId(resourceId);
return this.webSiteManagementClient.webApps.listMetadata(parsedResourceId.resourceGroup, parsedResourceId.resourceName);
}
public async updateSiteMetadata(resourceId: string, metadata: StringDictionary): Promise<StringDictionary> {
public async updateAppServiceMetadata(resourceId: string, metadata: StringDictionary): Promise<StringDictionary> {
let parsedResourceId: ParsedAzureResourceId = new ParsedAzureResourceId(resourceId);
return this.webSiteManagementClient.webApps.updateMetadata(parsedResourceId.resourceGroup, parsedResourceId.resourceName, metadata);
}
@ -112,6 +111,11 @@ export class AppServiceClient extends AzureResourceClient {
}
}
export enum ScmTypes {
VSTSRM = 'VSTSRM',
NONE = 'NONE'
}
interface DeploymentMessage {
type: string;
message: string;

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

@ -1,8 +1,8 @@
const uuid = require('uuid/v4');
import { AppServiceClient } from './clients/azure/appServiceClient';
import { AppServiceClient, ScmTypes } from './clients/azure/appServiceClient';
import { AzureDevOpsClient } from './clients/devOps/azureDevOpsClient';
import { AzureDevOpsHelper } from './helper/devOps/azureDevOpsHelper';
import { AzureTreeItem, UserCancelledError } from 'vscode-azureextensionui';
import { AzureTreeItem, UserCancelledError, AzureParentTreeItem, SubscriptionTreeItemBase } from 'vscode-azureextensionui';
import { generateDevOpsProjectName, generateDevOpsOrganizationName } from './helper/commonHelper';
import { GenericResource } from 'azure-arm-resource/lib/resource/models';
import { GraphHelper } from './helper/graphHelper';
@ -170,7 +170,7 @@ class PipelineConfigurer {
}
private async analyzeNode(node: any): Promise<void> {
if (node instanceof AzureTreeItem) {
if (!!node && !!node.fullId) {
await this.extractAzureResourceFromNode(node);
}
else if (node && node.fsPath) {
@ -359,7 +359,7 @@ class PipelineConfigurer {
private async validateIfPipelineCanBeSetupOnResource(resourceId: string): Promise<void> {
// Check for SCM type, if its value is set then a pipeline is already setup.
let siteConfig = await this.appServiceClient.getAppServiceConfig(resourceId);
if (siteConfig.scmType && siteConfig.scmType.toLowerCase() === 'vstsrm') {
if (siteConfig.scmType && siteConfig.scmType.toLowerCase() === ScmTypes.VSTSRM.toLowerCase()) {
// if pipeline is already setup, the ask the user if we should continue.
telemetryHelper.setTelemetry(TelemetryKeys.PipelineAlreadyConfigured, 'true');
telemetryHelper.setTelemetry(TelemetryKeys.ScmType, siteConfig.scmType);
@ -369,17 +369,18 @@ class PipelineConfigurer {
Messages.pipelineAlreadyConfigured,
constants.BrowsePipeline);
if (browsePipeline) {
vscode.commands.executeCommand('browse-pipeline', { fullId: resourceId });
let existingPipelineUrl = await this.appServiceClient.getVstsPipelineUrl(resourceId);
telemetryHelper.setTelemetry(TelemetryKeys.BrowsedExistingPipeline, 'true');
vscode.env.openExternal(vscode.Uri.parse(existingPipelineUrl));
throw new UserCancelledError();
}
}
else if (siteConfig.scmType && siteConfig.scmType.toLowerCase() !== '' && siteConfig.scmType.toLowerCase() !== 'none') {
else if (siteConfig.scmType && siteConfig.scmType !== '' && siteConfig.scmType.toLowerCase() !== ScmTypes.NONE.toLowerCase()) {
let result = await this.controlProvider.showInformationBox(constants.DeploymentResourceAlreadyConfigured, Messages.deploymentCenterAlreadyConfigured, constants.BrowseDeploymentSource);
if (result === constants.BrowseDeploymentSource) {
let deploymentCenterUrl: string = await this.appServiceClient.getDeploymentCenterUrl(resourceId);
telemetryHelper.setTelemetry(TelemetryKeys.OpenedDeploymentCenter, 'true');
telemetryHelper.setTelemetry(TelemetryKeys.BrowsedDeploymentCenter, 'true');
await vscode.env.openExternal(vscode.Uri.parse(deploymentCenterUrl));
throw new UserCancelledError();
}
@ -496,7 +497,7 @@ class PipelineConfigurer {
private async updateScmType(queuedPipeline: Build) {
await this.appServiceClient.updateScmType(this.inputs.targetResource.resource.id);
let metadata = await this.appServiceClient.getSiteMetadata(this.inputs.targetResource.resource.id);
let metadata = await this.appServiceClient.getAppServiceMetadata(this.inputs.targetResource.resource.id);
let organizationId = await this.azureDevOpsClient.getOrganizationIdFromName(this.inputs.organizationName);
metadata["properties"] = {
"VSTSRM_ProjectId": `${this.inputs.project.id}`,
@ -506,7 +507,7 @@ class PipelineConfigurer {
"VSTSRM_ConfiguredCDEndPoint": `${queuedPipeline.definition._links.web.href}`,
"VSTSRM_ReleaseDefinitionId": `${queuedPipeline.definition.id}`
};
await this.appServiceClient.updateSiteMetadata(this.inputs.targetResource.resource.id, metadata);
await this.appServiceClient.updateAppServiceMetadata(this.inputs.targetResource.resource.id, metadata);
await this.appServiceClient.publishDeploymentToAppService(
this.inputs.targetResource.resource.id,
queuedPipeline.definition._links.web.href,

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

@ -124,7 +124,7 @@ export class QuickPickItemWithData implements QuickPickItem {
export class ParsedAzureResourceId {
public resourceId: string;
public subscription: string;
public subscriptionId: string;
public resourceGroup: string;
public resourceType: string;
public resourceProvider: string;
@ -148,7 +148,7 @@ export class ParsedAzureResourceId {
for (let i = 0; i < parts.length; i++) {
switch (i) {
case 1:
this.subscription = parts[1];
this.subscriptionId = parts[1];
break;
case 3:
this.resourceGroup = parts[3];
@ -189,4 +189,4 @@ export interface AadApplication {
export interface GitBranchDetails {
remoteName: string;
branch: string;
}
}

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

@ -147,4 +147,5 @@ export const VstsRmScmType = 'VSTSRM';
export const DeploymentResourceAlreadyConfigured = 'DeploymentResourceAlreadyConfigured';
export const PipelineAlreadyConfigure = 'PipelineAlreadyConfigure';
export const BrowseDeploymentSource = 'BrowseDeploymentSource';
export const BrowsePipeline = 'BrowsePipeline';
export const BrowsePipeline = 'BrowsePipeline';
export const BrowseNotAvailableConfigurePipeline = 'BrowseNotAvailableConfigurePipeline';

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

@ -52,10 +52,13 @@ export class Messages {
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 invalidAzureResourceId: string = 'Azure Resource Id: %s, could not be parsed correctly. Kindly verify if the Id is correct.'
public static invalidAzureResourceId: string = 'Azure Resource Id: %s, could not be parsed correctly. Kindly verify if the Id is correct.';
public static userCancelledExcecption = 'User cancelled the action';
public static deploymentCenterAlreadyConfigured = 'Deployment source of type: %s is already setup for your web app, you can view more by browsing to deployment center.';
public static pipelineAlreadyConfigured = 'Pipeline is already setup for your web app.';
public static cannotFindPipelineUrlInMetaDataException = 'We were unable to find pipeline url for the app service. This can be caused due to corrupt/invalida metadata of site. You can open deployment center for more information.'
public static cannotFindPipelineUrlInMetaDataException = 'We were unable to find pipeline url for the app service. This can be caused due to corrupt/invalida metadata of site. You can open deployment center for more information.';
public static cannotFindOrganizationWithName = 'Unable to find organization with name: %s';
public static browseNotAvailableConfigurePipeline = 'Pipeline is not setup on the Azure resource. You can configure pipeline on the resource by click on the below button.';
public static didNotRecieveAzureResourceNodeToProcess = 'Unable to browse pipeline as the selected item is not a valid Azure Resource node. Kindly select a valid Azure Resource.';
}

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

@ -12,8 +12,9 @@ export class TelemetryKeys {
public static GitFolderExists: string = 'gitFolderExists';
public static PipelineAlreadyConfigured: string = 'pipelineAlreadyConfigured';
public static ScmType: string = 'scmType';
public static OpenedDeploymentCenter = 'openedDeploymentCenter';
public static BrowsedDeploymentCenter = 'openedDeploymentCenter';
public static BrowsedExistingPipeline = 'browsedExistingPipeline';
public static ClickedConfigurePipeline = 'ClickedConfigurePipeline';
// Durations
public static ExtensionActivationDuration = 'extensionActivationDuration';
@ -25,4 +26,4 @@ export class TelemetryKeys {
public static ProjectListCount = 'ProjectListCount';
public static WebAppListCount = 'WebAppListCount';
public static PipelineTempateListCount = 'pipelineTempateListCount';
}
}