Skip to content

Commit fa43c78

Browse files
authored
Merge pull request #8 from xgeek-net/dev
Feature of destruct metadata
2 parents 12a72e4 + ed0c832 commit fa43c78

File tree

8 files changed

+134
-33
lines changed

8 files changed

+134
-33
lines changed

package-lock.json

Lines changed: 42 additions & 4 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.0",
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/class/SfdcApi.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ class SfdcApi {
393393
meta['object'] = objName;
394394
meta['objectLabel'] = (objLabelMap.hasOwnProperty(objName)) ? objLabelMap[objName] : objName;
395395
names.shift(); // remove object name
396-
meta['customName'] = names.join('-');
396+
meta['customName'] = decodeURIComponent(names.join('-'));
397397
}
398398
return meta;
399399
};

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ Vue.component('app-newpipeline-detail-sfdc', {
280280
}
281281
for(let key in self.metadataMap) {
282282
for(let meta of self.metadataMap[key]) {
283-
const memberName = (meta.object) ? meta.object + '.' + meta.fullName : meta.fullName;
283+
const memberName = (meta.object && key !== 'Layout') ? meta.object + '.' + meta.fullName : meta.fullName;
284284
if(types.hasOwnProperty(key) && types[key].indexOf(memberName) >= 0) {
285285
// Check target component
286286
meta['MetaChecked'] = true;

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 -->

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

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ Vue.component('app-pipelines', {
108108
<td class="main-col">
109109
<div class="slds-truncate pipeline-name">
110110
<a v-on:click="openDetail(row.id)">{{ row.name }}</a><br />
111-
<p class="pipeline-desc slds-m-top_xx-small" v-if="(connectionmap[row.from] && connectionmap[row.to])">
111+
<p class="pipeline-desc slds-m-top_xx-small" v-if="connectionmap[row.from]">
112112
<i class="fab fa-github type-icon-small" v-if="connectionmap[row.from].type=='github'"></i>
113113
<i class="fab fa-bitbucket type-icon-small" v-if="connectionmap[row.from].type=='bitbucket'"></i>
114114
<span class="slds-icon_container slds-icon-utility-salesforce1 type-icon" v-if="connectionmap[row.from].type=='sfdc'">
@@ -121,16 +121,19 @@ Vue.component('app-pipelines', {
121121
<span v-if="row.type=='branch'">{{ row.branch.name }}</span>
122122
<span v-if="row.type=='commit'">#{{ (row.commits.length > 1) ? row.commits[0].sha.substr(0, 10) + '...' : row.commits[0].sha.substr(0, 10) }}</span>
123123
&nbsp;
124-
<i class="fas fa-arrow-right type-icon-small"></i>
125-
&nbsp;
126-
<i class="fab fa-github type-icon-small" v-if="connectionmap[row.to].type=='github'"></i>
127-
<i class="fab fa-bitbucket type-icon-small" v-if="connectionmap[row.to].type=='bitbucket'"></i>
128-
<span class="slds-icon_container slds-icon-utility-salesforce1 type-icon" v-if="connectionmap[row.to].type=='sfdc'">
129-
<svg class="slds-icon slds-icon_xx-small" aria-hidden="true">
130-
<use xlink:href="components/salesforce-lightning-design-system/assets/icons/utility-sprite/svg/symbols.svg#salesforce1"></use>
131-
</svg>
124+
<i class="fas fa-times type-icon-small" v-if="!connectionmap[row.to]"></i>
125+
<i class="fas fa-arrow-right type-icon-small" v-if="connectionmap[row.to]"></i>
126+
<span v-if="connectionmap[row.to]">
127+
&nbsp;
128+
<i class="fab fa-github type-icon-small" v-if="connectionmap[row.to].type=='github'"></i>
129+
<i class="fab fa-bitbucket type-icon-small" v-if="connectionmap[row.to].type=='bitbucket'"></i>
130+
<span class="slds-icon_container slds-icon-utility-salesforce1 type-icon" v-if="connectionmap[row.to].type=='sfdc'">
131+
<svg class="slds-icon slds-icon_xx-small" aria-hidden="true">
132+
<use xlink:href="components/salesforce-lightning-design-system/assets/icons/utility-sprite/svg/symbols.svg#salesforce1"></use>
133+
</svg>
134+
</span>
135+
{{ connectionmap[row.to].name }}
132136
</span>
133-
{{ connectionmap[row.to].name }}
134137
</p><!-- .pipeline-desc -->
135138
</div>
136139
</td>

0 commit comments

Comments
 (0)