import type { WebSiteManagementModels } from '@azure/arm-appservice' ; import * as git from 'simple-git/promise' ; import * as vscode from 'vscode' ; import { callWithMaskHandling , IActionContext } from 'vscode-azureextensionui' ; import { ext } from '../extensionVariables' ; import { localize } from '../localize' ; import { ParsedSite } from '../SiteClient' ; import { nonNullProp } from '../utils/nonNull' ; import { openUrl } from '../utils/openUrl' ; import { verifyNoRunFromPackageSetting } from '../verifyNoRunFromPackageSetting' ; import { waitForDeploymentToComplete } from './waitForDeploymentToComplete' ; type localGitOptions = { fsPath : string ; /**
     * Set if you want to specify what branch to push to. Default is `HEAD:master`.
     */ branch ? : string ; /**
     * Set to `true` if you want to commit changes before pushing.
     */ commit ? : boolean ; } ; export async function localGitDeploy ( site , options , context ) { const client = await site . createClient ( context ) ; const publishCredentials = await client . getWebAppPublishCredential ( ) ; const publishingPassword = nonNullProp ( publishCredentials , 'publishingPassword' ) ; const publishingUserName = nonNullProp ( publishCredentials , 'publishingUserName' ) ; await callWithMaskHandling ( async ( ) : Promise < void > => { const remote = `https://${ encodeURIComponent ( publishingUserName ) }:${ encodeURIComponent ( publishingPassword ) }@${ site . gitUrl }` ; const localGit = git ( options . fsPath ) ; let status ; try { status = await localGit . status ( ) ; if ( status . files . length > 0 && ! options . commit ) { const message = localize ( 'localGitUncommit' , '{0} uncommitted change(s) in local repo "{1}"' , status . files . length , options . fsPath ) ; const deployAnyway = { title : localize ( 'deployAnyway' , 'Deploy Anyway' ) } ; await context . ui . showWarningMessage ( message , { modal : true , stepName : 'pushWithUncommitChanges' } , deployAnyway ) ; context . telemetry . properties . pushWithUncommitChanges = 'true' ; } await verifyNoRunFromPackageSetting ( context , site ) ; ext . outputChannel . appendLog ( localize ( 'localGitDeploy' , `Deploying Local Git repository to "${ site . fullName }"...` ) , { resourceName : site . fullName } ) ; await tryPushAndWaitForDeploymentToComplete ( ) ; } catch ( err ) { if ( err . message . indexOf ( 'spawn git ENOENT' ) >= 0 ) { const installString = localize ( 'Install' , 'Install' ) ; const input = await vscode . window . showErrorMessage ( localize ( 'GitRequired' , 'Git must be installed to use Local Git Deploy.' ) , installString ) ; if ( input === installString ) { await openUrl ( 'https://git-scm.com/downloads' ) ; } context . telemetry . properties . gitNotInstalled = 'true' ; return undefined ; } else if ( err . message . indexOf ( 'error: failed to push' ) >= 0 ) { const forcePushMessage = { title : localize ( 'forcePush' , 'Force Push' ) } ; const pushReject = localize ( 'localGitPush' , 'Push rejected due to Git history diverging.' ) ; await context . ui . showWarningMessage ( pushReject , { modal : true , stepName : 'forcePush' } , forcePushMessage ) ; context . telemetry . properties . forcePush = 'true' ; await tryPushAndWaitForDeploymentToComplete ( true ) ; } else { throw err ; } } async function tryPushAndWaitForDeploymentToComplete ( forcePush = false ) { const tokenSource = new vscode . CancellationTokenSource ( ) ; const token = tokenSource . token ; try { if ( options . commit ) { const commitOptions = { '-a' : null } ; await localGit . commit ( 'Deployed via Azure App Service Extension' , undefined , commitOptions ) ; } const commitId = ( await localGit . log ( ) ) . latest . hash ; await new Promise < void > ( ( resolve , reject ) : void => { const pushOptions = forcePush ? { '-f' : null } : { } ; localGit . push ( remote , `HEAD:${ options . branch ?? 'master' }` , pushOptions ) . catch ( ( error ) => { reject ( error ) ; tokenSource . cancel ( ) ; } ) ; waitForDeploymentToComplete ( context , site , commitId , token ) . then ( resolve ) . catch ( reject ) ; } ) ; } finally { tokenSource . dispose ( ) ; } } } , publishingPassword ) ; }