Skip to content

Commit 4a44579

Browse files
committed
Add destruct metadata feature
1 parent 4cf63b5 commit 4a44579

File tree

5 files changed

+118
-20
lines changed

5 files changed

+118
-20
lines changed

package-lock.json

Lines changed: 41 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "pipeline",
33
"description": "Be less busy, less mistake in Salesforce metadata deploy",
4-
"version": "1.0.1",
4+
"version": "1.1.0",
55
"private": false,
66
"main": "src/main.js",
77
"dependencies": {
@@ -21,7 +21,8 @@
2121
"rimraf": "^2.6.2",
2222
"sfdc-generate-package": "^2.2.1",
2323
"url": "^0.11.0",
24-
"uuid": "^3.3.2"
24+
"uuid": "^3.3.2",
25+
"xmlbuilder": "^10.1.0"
2526
},
2627
"keywords": [
2728
"Pipeline",

src/class/Metadata.js

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@ const fs = require('fs');
33
const path = require('path');
44
const makeDir = require('make-dir');
55
const rimraf = require('rimraf');
6+
const xmlbuilder = require('xmlbuilder');
67
const archiver = require('archiver');
78
const sgp = require('sfdc-generate-package');
89

910
const SfdcApi = require('./SfdcApi.js');
1011
const Connect = require('./Connect.js');
1112

13+
const PACKAGE_XML_FILE_NAME = 'package.xml';
14+
const DESTRUCTIVE_XML_FILE_NAME = 'destructiveChanges.xml';
15+
1216
class Metadata {
1317
constructor(opts) {
1418

@@ -108,6 +112,37 @@ class Metadata {
108112
});
109113
}
110114

115+
/**
116+
* Generate package.xml and destructiveChanges.xml for Destruct
117+
* @param {String} metaPath - pipeline cache path
118+
*/
119+
createDestructiveChanges(targetPath, opts) {
120+
opts = opts || {};
121+
const metaPath = path.join(targetPath, 'metadata', 'src');
122+
123+
return new Promise(function(resolve, reject) {
124+
// Rename to destructiveChanges.xml
125+
fs.renameSync(metaPath + '/' + PACKAGE_XML_FILE_NAME, metaPath + '/' + DESTRUCTIVE_XML_FILE_NAME);
126+
// Generate blank package.xml
127+
const xml = xmlbuilder.create('Package')
128+
.att('xmlns', 'http://soap.sforce.com/2006/04/metadata')
129+
.dec('1.0', 'UTF-8')
130+
.ele('version')
131+
.t(opts.version || '40.0');
132+
const xmlContent = xml.end({ pretty: true, indent: ' ', newline: '\n' });
133+
fs.writeFileSync(metaPath + '/' + PACKAGE_XML_FILE_NAME, xmlContent);
134+
135+
// Remove metadata files
136+
fs.readdirSync(metaPath).filter(function (file) {
137+
if(fs.statSync(metaPath+'/'+file).isDirectory()) {
138+
rimraf.sync(metaPath+'/'+file);
139+
}
140+
return resolve();
141+
});
142+
});
143+
144+
}
145+
111146
/**
112147
* Archive metadata package zip
113148
* @param {String} targetPath (zip file root path('output/' )
@@ -161,7 +196,7 @@ class Metadata {
161196
})
162197
.catch(function(err){
163198
return reject(err);
164-
})
199+
});
165200
});
166201
}
167202

src/class/Pipeline.js

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -254,32 +254,54 @@ class Pipeline {
254254
// Refresh Token for bitbucket
255255
connect.restoreToken(fromConn, token);
256256
}
257+
if(pipeline.action == 'destruct') {
258+
// Destruct metadata
259+
return Promise.resolve(true);
260+
}
257261
return metadata.checkConnect(toConn);
258262
})
259263
.then(function(success) {
260264
pipelineLog('[SF.api] Authorize : ' + success);
261265
return client.getFiles(pipeline, fromConn, pipelineLog);
266+
//return Promise.resolve(true);
262267
})
263268
.then(function(success) {
264269
// Generate package.xml file
265270
return metadata.createPackageXml(pPath, { version : pipeline.toApiVersion });
266271
})
272+
.then(function() {
273+
if(pipeline.action == 'destruct') {
274+
// Generate destructiveChanges.xml, package.xml files
275+
return metadata.createDestructiveChanges(pPath, { version : pipeline.toApiVersion });
276+
}
277+
return Promise.resolve(true);
278+
})
267279
.then(function() {
268280
pipelineLog('[Metadata] Generate package.xml Done.');
269281
// Zip Metadata
270282
return metadata.archive(pPath);
271283
})
272284
.then(function(zipPath) {
273-
// Do Deploy
274285
// opts @see https://jsforce.github.io/jsforce/doc/Metadata.html#deploy
275-
let opts = { rollbackOnError : true, runAllTests : (pipeline.runTests === true) };
276-
return metadata.deploy(toConn, zipPath, opts, function(deployResult) {
277-
self.outputDeployProcessLog(pipelineLog, deployResult);
278-
});
286+
let opts = { rollbackOnError : true };
287+
if(pipeline.action == 'destruct') {
288+
// Do Destruct
289+
opts['purgeOnDelete'] = true;
290+
return metadata.deploy(fromConn, zipPath, opts, function(deployResult) {
291+
self.outputDeployProcessLog(pipelineLog, deployResult);
292+
});
293+
} else {
294+
// Do Deploy
295+
opts['runAllTests'] = (pipeline.runTests === true);
296+
return metadata.deploy(toConn, zipPath, opts, function(deployResult) {
297+
self.outputDeployProcessLog(pipelineLog, deployResult);
298+
});
299+
}
279300
})
280301
.then(function(deployResult) {
281302
// Save deploy result
282-
deployResult['url'] = toConn.instanceUrl + '/changemgmt/monitorDeploymentsDetails.apexp?asyncId=' + deployResult.id
303+
const targetConn = (pipeline.action == 'destruct') ? fromConn : toConn;
304+
deployResult['url'] = targetConn.instanceUrl + '/changemgmt/monitorDeploymentsDetails.apexp?asyncId=' + deployResult.id
283305
self.outputDeployLog(pipelineLog, deployResult);
284306
const now = new Date();
285307
const endTime = now.toISOString();

src/view/js/vue-components/app-newpipeline.js

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ Vue.component('app-newpipeline', {
77
fromApiVersion : '',
88
to : '',
99
toApiVersion : '',
10+
action : 'deploy',
1011
runTests : false
1112
},
1213
validate : false,
1314
connection : null,
15+
actionList : [ {value : 'deploy', label : 'Deploy (Add/Update)'}, {value : 'destruct', label : 'Destruct (Delete)'} ],
1416
apiVersionList : []
1517
}
1618
},
@@ -34,7 +36,7 @@ Vue.component('app-newpipeline', {
3436
},
3537
methods: {
3638
reload : function() {
37-
this.pipeline = { from : '', fromApiVersion : '', to : '', toApiVersion : '', runTests : false };
39+
this.pipeline = { from : '', fromApiVersion : '', to : '', toApiVersion : '', action : 'deploy', runTests : false };
3840
this.validate = false;
3941
this.connection = null;
4042
this.initApiVerList();
@@ -103,6 +105,7 @@ Vue.component('app-newpipeline', {
103105
pipeline['to'] = self.pipeline.to;
104106
pipeline['toApiVersion'] = self.pipeline.toApiVersion;
105107
pipeline['fromApiVersion'] = self.pipeline.fromApiVersion;
108+
pipeline['action'] = self.pipeline.action;
106109
pipeline['runTests'] = self.pipeline.runTests;
107110
pipeline['status'] = 'ready';
108111
pipeline['created_at'] = now.toISOString();
@@ -149,7 +152,7 @@ Vue.component('app-newpipeline', {
149152
if(!pipeline.from || pipeline.from.length == 0) {
150153
return 'CONNECTION (FROM) is required';
151154
}
152-
if(!pipeline.to || pipeline.to.length == 0) {
155+
if(pipeline.action != 'destruct' && (!pipeline.to || pipeline.to.length == 0)) {
153156
return 'CONNECTION (TO) is required';
154157
}
155158
if(!pipeline.name || pipeline.name.length == 0) {
@@ -222,9 +225,8 @@ Vue.component('app-newpipeline', {
222225
</label>
223226
<div class="slds-form-element__control slds-size_4-of-12">
224227
<div class="slds-select_container">
225-
<select class="slds-select" disabled="disabled">
226-
<option value="deploy">Deploy (Add/Update)</option>
227-
<option value="destruct">Destruct (Delete)</option>
228+
<select class="slds-select" v-model="pipeline.action">
229+
<option v-for="act in actionList" v-bind:value="act.value" v-bind:seleced="pipeline.action==act.value">{{ act.label }}</option>
228230
</select>
229231
</div>
230232
</div>
@@ -233,12 +235,12 @@ Vue.component('app-newpipeline', {
233235
<footer class="slds-card__footer"></footer>
234236
</article>
235237
</div><!-- .slds-size_5-of-12 -->
236-
<div class="new-pipeline-connect-icon mt4">
238+
<div class="new-pipeline-connect-icon mt4" v-if="pipeline.action!='destruct'">
237239
<div class="slds-align_absolute-center">
238240
<span class="pipeline-from-icon"><i class="fas fa-arrow-right"></i></span>
239241
</div>
240242
</div><!-- .slds-size_1-of-12 -->
241-
<div class="new-pipeline-connect">
243+
<div class="new-pipeline-connect" v-if="pipeline.action!='destruct'">
242244
<article class="slds-card ">
243245
<div class="slds-card__header slds-grid">
244246
<div class="slds-media__figure">
@@ -302,7 +304,7 @@ Vue.component('app-newpipeline', {
302304
<div class="slds-size_12-of-12 mt1" v-if="validate==true">
303305
<div class="slds-wrap slds-text-align_right">
304306
<button class="slds-button slds-button_neutral" v-on:click="savePipeline">Save</button>
305-
<button class="slds-button slds-button_brand" v-on:click="runPipeline">Run Pipeline</button>
307+
<button class="slds-button" v-bind:class="{'slds-button_brand': pipeline.action!='destruct', 'slds-button_destructive': pipeline.action=='destruct'}" v-on:click="runPipeline">Run Pipeline</button>
306308
</div>
307309
</div><!-- .slds-size_11-of-12 -->
308310
</div><!-- #pipeline-new -->

0 commit comments

Comments
 (0)