Skip to content

Commit

Permalink
Custom Script Feature for Azure App Service Deploy task (#3688)
Browse files Browse the repository at this point in the history
  • Loading branch information
vincent1173 authored Mar 3, 2017
1 parent 181ab89 commit c9a9cd3
Show file tree
Hide file tree
Showing 8 changed files with 378 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
"loc.input.help.AdditionalArguments": "Additional Web Deploy arguments following the syntax -key:value .<br />These will be applied when deploying the Azure App Service. Example: -disableLink:AppPoolExtension -disableLink:ContentExtension.<br />For more examples of Web Deploy operation settings, refer to [this](https://go.microsoft.com/fwlink/?linkid=838471).",
"loc.input.label.RenameFilesFlag": "Rename locked files",
"loc.input.help.RenameFilesFlag": "Select the option to enable msdeploy flag MSDEPLOY_RENAME_FILES_FLAG in Azure App Service application settings. The option if set enables msdeploy to rename locked files that are locked during app deployment",
"loc.input.label.ScriptType": "Deployment script type",
"loc.input.help.ScriptType": "Customize the deployment by providing a script that will run on the Azure App service. For example restore packages for Node, PHP, Python applications. [Learn more](https://go.microsoft.com/fwlink/?linkid=843471).",
"loc.input.label.InlineScript": "Inline Script",
"loc.input.label.ScriptPath": "Deployment script path",
"loc.input.label.XmlTransformation": "XML transformation",
"loc.input.help.XmlTransformation": "The config transfoms will be run for `*.Release.config` and `*.<EnvironmentName>.config` on the `*.config file`.<br/> Config transforms will be run prior to the Variable Substitution.<br/>XML transformations are supported only for Windows platform.",
"loc.input.label.XmlVariableSubstitution": "XML variable substitution",
Expand All @@ -57,8 +61,8 @@
"loc.messages.Unabletoretrievewebappsettings": "Unable to retrieve App Service application settings. [Status Code: '%s', Error Message: '%s']",
"loc.messages.Unabletoupdatewebappsettings": "Unable to update App service application settings. [Status Code: '%s', Error Message: '%s']",
"loc.messages.CannotupdatedeploymentstatusuniquedeploymentIdCannotBeRetrieved": "Cannot update deployment status : Unique Deployment ID cannot be retrieved",
"loc.messages.WebappsuccessfullypublishedatUrl0": "App Service successfully deployed at url %s",
"loc.messages.Failedtodeploywebsite": "Failed to deploy website.",
"loc.messages.PackageDeploymentSuccess": "Successfully deployed web package to App Service.",
"loc.messages.PackageDeploymentFailed": "Failed to deploy web package to App Service.",
"loc.messages.Runningcommand": "Running command: %s",
"loc.messages.Deployingwebapplicationatvirtualpathandphysicalpath": "Deploying web package : %s at virtual path (physical path) : %s (%s)",
"loc.messages.Successfullydeployedpackageusingkuduserviceat": "Successfully deployed package %s using kudu service at %s",
Expand Down Expand Up @@ -96,5 +100,19 @@
"loc.messages.VirtualApplicationDoesNotExist": "Virtual application doesn't exists : %s",
"loc.messages.JSONParseError": "Unable to parse JSON file: %s. Error: %s",
"loc.messages.JSONvariablesubstitutionappliedsuccessfully": "JSON variable substitution applied successfully.",
"loc.messages.XMLvariablesubstitutionappliedsuccessfully": "XML variable substitution applied successfully."
"loc.messages.XMLvariablesubstitutionappliedsuccessfully": "XML variable substitution applied successfully.",
"loc.messages.failedtoUploadFileToKudu": "Unable to upload file: %s to Kudu (%s). Status Code: %s (%s)",
"loc.messages.failedtoUploadFileToKuduError": "Unable to upload file: %s to Kudu (%s). Error: %s",
"loc.messages.ExecuteScriptOnKudu": "Executing given script on Kudu: %s",
"loc.messages.FailedToRunScriptOnKuduError": "Unable to run the script on Kudu: %s. Error: %s",
"loc.messages.FailedToRunScriptOnKudu": "Unable to run the script on Kudu: %s. Status Code: %s (%s)",
"loc.messages.SciptExecutionOnKuduSuccess": "Successfully executed script on Kudu.",
"loc.messages.SciptExecutionOnKuduFailed": "Executed script returned '%s' as return code. Error: %s",
"loc.messages.FailedtoDeleteFileFromKudu": "Unable to delete file: %s from Kudu (%s). Status Code: %s (%s)",
"loc.messages.FailedtoDeleteFileFromKuduError": "Unable to delete file: %s from Kudu (%s). Error: %s",
"loc.messages.ScriptFileNotFound": "Script file '%s' not found.",
"loc.messages.InvalidScriptFile": "Invalid script file '%s' provided. Valid extensions are .bat and .cmd",
"loc.messages.RetryForTimeoutIssue": "Script execution failed with timeout issue. Retrying once again.",
"loc.messages.stdoutFromScript": "Standard output from script: ",
"loc.messages.stderrFromScript": "Standard error from script: "
}
12 changes: 12 additions & 0 deletions Tasks/AzureRmWebAppDeployment/Tests/L0.ts
Original file line number Diff line number Diff line change
Expand Up @@ -467,4 +467,16 @@ describe('AzureRmWebAppDeployment Suite', function() {
assert(tr.stdout.search('## mkdir Successful ##') >= 0, 'should have created dir including dest folder');
done();
});

it('Validate webdepoyment-common.utility.runPostDeploymentScript()', (done:MochaDone) => {
let tp = path.join(__dirname, "..", "node_modules", "webdeployment-common", "Tests", 'L0RunPostDeploymentScript.js');
let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp);
tr.run();

assert(tr.succeeded, 'task should have succeeded');
assert(tr.stdout.search('PUT:https://mytestappKuduUrl/api/vfs//site/wwwroot/kuduPostDeploymentScript.cmd') >= 0, 'should have uploaded file');
assert(tr.stdout.search('POST:https://mytestappKuduUrl/api/command') >= 0, 'should have executed script');
assert(tr.stdout.search('DELETED:https://mytestappKuduUrl/api/vfs//site/wwwroot/kuduPostDeploymentScript.cmd') >= 0, 'should have removed file');
done();
});
});
11 changes: 8 additions & 3 deletions Tasks/AzureRmWebAppDeployment/azurermwebappdeployment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ async function run() {
var xmlTransformation: boolean = tl.getBoolInput('XmlTransformation', false);
var JSONFiles = tl.getDelimitedInput('JSONFiles', '\n', false);
var xmlVariableSubstitution: boolean = tl.getBoolInput('XmlVariableSubstitution', false);
var scriptType: string = tl.getInput('ScriptType', false);
var inlineScript: string = tl.getInput('InlineScript', false);
var scriptPath: string = tl.getPathInput('ScriptPath', false);
var endPointAuthCreds = tl.getEndpointAuthorization(connectedServiceName, true);

var isDeploymentSuccess: boolean = true;
var tempPackagePath = null;

Expand Down Expand Up @@ -126,6 +128,9 @@ async function run() {
await DeployUsingKuduDeploy(webDeployPkg, azureWebAppDetails, publishingProfile, virtualApplication, isFolderBasedDeployment, takeAppOfflineFlag);

}
if(scriptType) {
await kuduUtility.runPostDeploymentScript(publishingProfile, scriptType, inlineScript, scriptPath, takeAppOfflineFlag);
}
await updateScmType(endPoint, webAppName, resourceGroupName, deployToSlotFlag, slotName);

} catch (error) {
Expand Down Expand Up @@ -187,10 +192,10 @@ async function DeployUsingKuduDeploy(webDeployPkg, azureWebAppDetails, publishin
}
}
await kuduUtility.deployWebAppPackage(webAppZipFile, publishingProfile, virtualPath, physicalPath, takeAppOfflineFlag);
console.log(tl.loc('WebappsuccessfullypublishedatUrl0', publishingProfile.destinationAppUrl));
console.log(tl.loc('PackageDeploymentSuccess'));
}
catch(error) {
tl.error(tl.loc('Failedtodeploywebsite'));
tl.error(tl.loc('PackageDeploymentFailed'));
throw Error(error);
}
finally {
Expand Down
58 changes: 53 additions & 5 deletions Tasks/AzureRmWebAppDeployment/task.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"author": "Microsoft Corporation",
"version": {
"Major": 3,
"Minor": 0,
"Patch": 9
"Minor": 1,
"Patch": 0
},
"preview": "true",
"releaseNotes": "What's new in Version 3.0: <br/>&nbsp;&nbsp;Supports File Transformations (XDT) <br/>&nbsp;&nbsp;Supports Variable Substitutions(XML, JSON) <br/>Click [here](https://aka.ms/azurermwebdeployreadme) for more Information.",
Expand Down Expand Up @@ -183,6 +183,40 @@
"groupName": "AdditionalDeploymentOptions",
"helpMarkDown": "Select the option to enable msdeploy flag MSDEPLOY_RENAME_FILES_FLAG in Azure App Service application settings. The option if set enables msdeploy to rename locked files that are locked during app deployment"
},
{
"name": "ScriptType",
"type": "pickList",
"label": "Deployment script type",
"defaultValue": "",
"options": {
"": "Select deployment script type",
"Inline Script": "Inline Script",
"File Path": "Script File Path"
},
"groupName": "AdditionalDeploymentOptions",
"helpMarkDown": "Customize the deployment by providing a script that will run on the Azure App service. For example restore packages for Node, PHP, Python applications. [Learn more](https://go.microsoft.com/fwlink/?linkid=843471)."
},
{
"name": "InlineScript",
"type": "multiLine",
"label": "Inline Script",
"defaultValue": ":: You can provide your deployment commands here. One command per line.",
"groupName": "AdditionalDeploymentOptions",
"visibleRule": "ScriptType == Inline Script",
"properties": {
"resizable": "true",
"rows": "10",
"maxLength": "500"
}
},
{
"name": "ScriptPath",
"type": "filePath",
"label": "Deployment script path",
"required": true,
"groupName": "AdditionalDeploymentOptions",
"visibleRule": "ScriptType == File Path"
},
{
"name": "XmlTransformation",
"type": "boolean",
Expand Down Expand Up @@ -258,8 +292,8 @@
"Unabletoretrievewebappsettings": "Unable to retrieve App Service application settings. [Status Code: '%s', Error Message: '%s']",
"Unabletoupdatewebappsettings": "Unable to update App service application settings. [Status Code: '%s', Error Message: '%s']",
"CannotupdatedeploymentstatusuniquedeploymentIdCannotBeRetrieved": "Cannot update deployment status : Unique Deployment ID cannot be retrieved",
"WebappsuccessfullypublishedatUrl0": "App Service successfully deployed at url %s",
"Failedtodeploywebsite": "Failed to deploy website.",
"PackageDeploymentSuccess": "Successfully deployed web package to App Service.",
"PackageDeploymentFailed": "Failed to deploy web package to App Service.",
"Runningcommand": "Running command: %s",
"Deployingwebapplicationatvirtualpathandphysicalpath": "Deploying web package : %s at virtual path (physical path) : %s (%s)",
"Successfullydeployedpackageusingkuduserviceat": "Successfully deployed package %s using kudu service at %s",
Expand Down Expand Up @@ -297,6 +331,20 @@
"VirtualApplicationDoesNotExist": "Virtual application doesn't exists : %s",
"JSONParseError": "Unable to parse JSON file: %s. Error: %s",
"JSONvariablesubstitutionappliedsuccessfully": "JSON variable substitution applied successfully.",
"XMLvariablesubstitutionappliedsuccessfully": "XML variable substitution applied successfully."
"XMLvariablesubstitutionappliedsuccessfully": "XML variable substitution applied successfully.",
"failedtoUploadFileToKudu": "Unable to upload file: %s to Kudu (%s). Status Code: %s (%s)",
"failedtoUploadFileToKuduError": "Unable to upload file: %s to Kudu (%s). Error: %s",
"ExecuteScriptOnKudu": "Executing given script on Kudu: %s",
"FailedToRunScriptOnKuduError": "Unable to run the script on Kudu: %s. Error: %s",
"FailedToRunScriptOnKudu": "Unable to run the script on Kudu: %s. Status Code: %s (%s)",
"SciptExecutionOnKuduSuccess": "Successfully executed script on Kudu.",
"SciptExecutionOnKuduFailed": "Executed script returned '%s' as return code. Error: %s",
"FailedtoDeleteFileFromKudu": "Unable to delete file: %s from Kudu (%s). Status Code: %s (%s)",
"FailedtoDeleteFileFromKuduError": "Unable to delete file: %s from Kudu (%s). Error: %s",
"ScriptFileNotFound": "Script file '%s' not found.",
"InvalidScriptFile": "Invalid script file '%s' provided. Valid extensions are .bat and .cmd",
"RetryForTimeoutIssue": "Script execution failed with timeout issue. Retrying once again.",
"stdoutFromScript": "Standard output from script: ",
"stderrFromScript": "Standard error from script: "
}
}
58 changes: 53 additions & 5 deletions Tasks/AzureRmWebAppDeployment/task.loc.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"author": "Microsoft Corporation",
"version": {
"Major": 3,
"Minor": 0,
"Patch": 9
"Minor": 1,
"Patch": 0
},
"preview": "true",
"releaseNotes": "What's new in Version 3.0: <br/>&nbsp;&nbsp;Supports File Transformations (XDT) <br/>&nbsp;&nbsp;Supports Variable Substitutions(XML, JSON) <br/>Click [here](https://aka.ms/azurermwebdeployreadme) for more Information.",
Expand Down Expand Up @@ -183,6 +183,40 @@
"groupName": "AdditionalDeploymentOptions",
"helpMarkDown": "ms-resource:loc.input.help.RenameFilesFlag"
},
{
"name": "ScriptType",
"type": "pickList",
"label": "ms-resource:loc.input.label.ScriptType",
"defaultValue": "",
"options": {
"": "Select deployment script type",
"Inline Script": "Inline Script",
"File Path": "Script File Path"
},
"groupName": "AdditionalDeploymentOptions",
"helpMarkDown": "ms-resource:loc.input.help.ScriptType"
},
{
"name": "InlineScript",
"type": "multiLine",
"label": "ms-resource:loc.input.label.InlineScript",
"defaultValue": ":: You can provide your deployment commands here. One command per line.",
"groupName": "AdditionalDeploymentOptions",
"visibleRule": "ScriptType == Inline Script",
"properties": {
"resizable": "true",
"rows": "10",
"maxLength": "500"
}
},
{
"name": "ScriptPath",
"type": "filePath",
"label": "ms-resource:loc.input.label.ScriptPath",
"required": true,
"groupName": "AdditionalDeploymentOptions",
"visibleRule": "ScriptType == File Path"
},
{
"name": "XmlTransformation",
"type": "boolean",
Expand Down Expand Up @@ -258,8 +292,8 @@
"Unabletoretrievewebappsettings": "ms-resource:loc.messages.Unabletoretrievewebappsettings",
"Unabletoupdatewebappsettings": "ms-resource:loc.messages.Unabletoupdatewebappsettings",
"CannotupdatedeploymentstatusuniquedeploymentIdCannotBeRetrieved": "ms-resource:loc.messages.CannotupdatedeploymentstatusuniquedeploymentIdCannotBeRetrieved",
"WebappsuccessfullypublishedatUrl0": "ms-resource:loc.messages.WebappsuccessfullypublishedatUrl0",
"Failedtodeploywebsite": "ms-resource:loc.messages.Failedtodeploywebsite",
"PackageDeploymentSuccess": "ms-resource:loc.messages.PackageDeploymentSuccess",
"PackageDeploymentFailed": "ms-resource:loc.messages.PackageDeploymentFailed",
"Runningcommand": "ms-resource:loc.messages.Runningcommand",
"Deployingwebapplicationatvirtualpathandphysicalpath": "ms-resource:loc.messages.Deployingwebapplicationatvirtualpathandphysicalpath",
"Successfullydeployedpackageusingkuduserviceat": "ms-resource:loc.messages.Successfullydeployedpackageusingkuduserviceat",
Expand Down Expand Up @@ -297,6 +331,20 @@
"VirtualApplicationDoesNotExist": "ms-resource:loc.messages.VirtualApplicationDoesNotExist",
"JSONParseError": "ms-resource:loc.messages.JSONParseError",
"JSONvariablesubstitutionappliedsuccessfully": "ms-resource:loc.messages.JSONvariablesubstitutionappliedsuccessfully",
"XMLvariablesubstitutionappliedsuccessfully": "ms-resource:loc.messages.XMLvariablesubstitutionappliedsuccessfully"
"XMLvariablesubstitutionappliedsuccessfully": "ms-resource:loc.messages.XMLvariablesubstitutionappliedsuccessfully",
"failedtoUploadFileToKudu": "ms-resource:loc.messages.failedtoUploadFileToKudu",
"failedtoUploadFileToKuduError": "ms-resource:loc.messages.failedtoUploadFileToKuduError",
"ExecuteScriptOnKudu": "ms-resource:loc.messages.ExecuteScriptOnKudu",
"FailedToRunScriptOnKuduError": "ms-resource:loc.messages.FailedToRunScriptOnKuduError",
"FailedToRunScriptOnKudu": "ms-resource:loc.messages.FailedToRunScriptOnKudu",
"SciptExecutionOnKuduSuccess": "ms-resource:loc.messages.SciptExecutionOnKuduSuccess",
"SciptExecutionOnKuduFailed": "ms-resource:loc.messages.SciptExecutionOnKuduFailed",
"FailedtoDeleteFileFromKudu": "ms-resource:loc.messages.FailedtoDeleteFileFromKudu",
"FailedtoDeleteFileFromKuduError": "ms-resource:loc.messages.FailedtoDeleteFileFromKuduError",
"ScriptFileNotFound": "ms-resource:loc.messages.ScriptFileNotFound",
"InvalidScriptFile": "ms-resource:loc.messages.InvalidScriptFile",
"RetryForTimeoutIssue": "ms-resource:loc.messages.RetryForTimeoutIssue",
"stdoutFromScript": "ms-resource:loc.messages.stdoutFromScript",
"stderrFromScript": "ms-resource:loc.messages.stderrFromScript"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
var mockery = require('mockery');
mockery.enable({
useCleanCache: true,
warnOnReplace: false,
warnOnUnregistered: false
});

mockery.registerMock('vso-node-api/HttpClient', {
HttpCallbackClient: function () {
return {
send: function (verb, url) {
if(verb == 'POST' && url == 'https://mytestappKuduUrl/api/command') {
console.log('POST:https://mytestappKuduUrl/api/command');
return;
}
throw Error('Unknown verb or URL - SEND');
},
sendStream: function (verb, url) {
if(verb == 'PUT' && url == 'https://mytestappKuduUrl/api/vfs//site/wwwroot/kuduPostDeploymentScript.cmd') {
console.log('PUT:https://mytestappKuduUrl/api/vfs//site/wwwroot/kuduPostDeploymentScript.cmd');
return;
}
throw Error('Unknown verb or URL - sendStream');
},
get: function(verb, url) {
if(verb == 'DELETE' && url == 'https://mytestappKuduUrl/api/vfs//site/wwwroot/kuduPostDeploymentScript.cmd') {
console.log("DELETED:https://mytestappKuduUrl/api/vfs//site/wwwroot/kuduPostDeploymentScript.cmd");
return;
}
throw Error('Unknown verb or URL - GET');
}
};
}
});

mockery.registerMock('vsts-task-lib/task', {
exist: function() {
return true;
},
getVariable: function() {
return 'workigDirectory';
},
debug: function(message) {
console.log('##debug : ' + message);
},
loc: function(message, argument) {
console.log('##LOC: ' + message + ' : ' + argument);
}

});
mockery.registerMock('q', {
'defer': function() {
return {
promise: 'promise'
}
}
});

var fs = require('fs');
mockery.registerMock('fs', {
'createReadStream': function() {
return '';
},
statSync: fs.statSync,
writeFileSync: fs.writeFileSync
})
var mockPublishProfile = {
profileName: 'mytestapp - Web Deploy',
publishMethod: 'MSDeploy',
publishUrl: 'mytestappKuduUrl',
msdeploySite: 'mytestapp',
userName: '$mytestapp',
userPWD: 'mytestappPwd',
destinationAppUrl: 'mytestappUrl',
SQLServerDBConnectionString: '',
mySQLDBConnectionString: '',
hostingProviderForumLink: '',
controlPanelLink: '',
webSystem: 'WebSites'
};

var kuduUtility = require('webdeployment-common/kuduutility.js');
kuduUtility.runPostDeploymentScript(mockPublishProfile, "File Path", null, 'myscript.cmd', false);
Loading

0 comments on commit c9a9cd3

Please sign in to comment.