├── .gitignore ├── LICENSE ├── README.md ├── action.yml ├── dist └── index.js ├── package-lock.json ├── package.json └── src ├── index.js ├── index.test.js └── utils ├── exec-command.js ├── install-dependencies.js └── sfdx.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Editors 2 | .vscode 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | *.pid.lock 16 | 17 | # Directory for instrumented libs generated by jscoverage/JSCover 18 | lib-cov 19 | 20 | # Coverage directory used by tools like istanbul 21 | coverage 22 | 23 | # nyc test coverage 24 | .nyc_output 25 | 26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 27 | .grunt 28 | 29 | # Node components 30 | node_modules/ 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Other Dependency directories 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # Optional npm cache directory 48 | .npm 49 | 50 | # Optional eslint cache 51 | .eslintcache 52 | 53 | # Optional REPL history 54 | .node_repl_history 55 | 56 | # Output of 'npm pack' 57 | *.tgz 58 | 59 | # Yarn Integrity file 60 | .yarn-integrity 61 | 62 | # dotenv environment variables file 63 | .env 64 | 65 | # next.js build output 66 | .next 67 | 68 | #Salesforce DX CLI 69 | sfdx-cli/ 70 | 71 | force-app/ 72 | manifest/ 73 | devops/ 74 | 75 | DATOS.js 76 | deploy.properties 77 | server.key 78 | sfdx-project.json 79 | config/ 80 | Retag.sh 81 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Tiago Nascimento 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SFDX Org Development Model Github Action - Build & Deploy 2 | 3 | This repository implements a Github Action that's is a variation of the [Bitbucket Pipelines examples with org development](https://github.com/forcedotcom/sfdx-bitbucket-org/). 4 | 5 | This action is usefull to deploy to non-scratch orgs (sandbox or production) with [Github Workflow](https://guides.github.com/introduction/flow/). 6 | 7 | It's a Javascript [Github Action](https://github.com/features/actions), that will once is executed will run the following steps: 8 | - Download and install Salesforce CLI 9 | - Decrypt the certificate 10 | - Auth in the target sandbox/org 11 | - Deploy/Check one or more deploy package 12 | - Deploy/Check destructive changes (optional) 13 | - Execute Data Factory Apex 14 | 15 | ## Getting Started 16 | 17 | 1) If you intent to use it, please go through the Readme of the referred repo, make sure you understand the concept and execute the *Getting Started* (of the referred repo) steps #4, 5, 6 and 10 18 | 19 | 2) Also you will need to add the following [Github Secrets](https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets) on your github repository, that will be later used on your Github Workflow: 20 | 21 | - CONSUMER_KEY: holds the client_id of the connected app that you created on step #4 of the referred repo 22 | - USERNAME: holds the username that will be used to connect to the target org/sandbox 23 | - DECRYPTION_KEY: holds the Key value you generated on step #6 of the referred repo - will be used to decrypt the certificate 24 | - DECRYPTION_IV: holds the IV value you generated on step #6 of the referred repo - will be used to decrypt the certificate 25 | 26 | ## Usage Sample 27 | 28 | You can execute the action as per the following sample: 29 | 30 | ```yaml 31 | # Unique name for this workflow 32 | name: On push develop - Deploy to TI01 33 | 34 | # Definition when the workflow should run 35 | on: 36 | push: 37 | branches: 38 | - develop 39 | 40 | # Jobs to be executed 41 | jobs: 42 | build-deploy: 43 | runs-on: ubuntu-latest 44 | steps: 45 | 46 | # Checkout the code in the pull request 47 | - name: 'Checkout source code' 48 | uses: actions/checkout@v2 49 | 50 | - name: 'Build and Deploy' 51 | uses: tiagonnascimento/sfdx-orgdev-build-deploy@v2 52 | with: 53 | type: 'sandbox' 54 | certificate_path: devops/server.key.enc 55 | decryption_key: ${{ secrets.DECRYPTION_KEY_NON_PRODUCTIVE }} 56 | decryption_iv: ${{ secrets.DECRYPTION_IV_NON_PRODUCTIVE }} 57 | client_id: ${{ secrets.CONSUMER_KEY_TI01 }} 58 | username: ${{ secrets.USERNAME_TI01 }} 59 | checkonly: false 60 | deploy_wait_time: '10' 61 | manifest_path: manifest/package-01.xml,manifest/package-02.xml,manifest/package-03.xml 62 | destructive_path: destructive 63 | data_factory: scripts/apex/CreateBaseData.apex 64 | default_source_path: force-app/main/default 65 | ``` 66 | 67 | ### Inputs: 68 | | Name | Requirement | Description | 69 | | --------------------- | ----------- | ----------- | 70 | | `type` | _required_ | Whether to deploy on `production` or on `sandbox` (default value) | 71 | | `certificate_path` | _required_ | Path on the repo where the encrypted certificate is stored | 72 | | `decryption_key` | _required_ | Decryption KEY to be used to decrypt the certificate | 73 | | `decryption_iv` | _required_ | Decryption IV to be used to decrypt the certificate | 74 | | `client_id` | _required_ | Client ID that will be used to connect into the target org/sandbox | 75 | | `username` | _required_ | Username that will be used to connect into the target org/sandbox | 76 | | `checkonly` | _required_ | Boolean value to indicate whether this action should execute the deploy or only check it, default is false, but if true it will add -c parameter on the force:mdapi:deploy commands | 77 | | `manifest_path` | _required_ | Path on the current repository (or on the root folder of the sfdx project if sfdx_root_folder is set) to one or more package.xml that represents the packages to be deployed. Based on this files the metadata package will be created and deployed in the order specified. Ex: | manifest/package-01.xml,manifest/package-02.xml,manifest/package-03.xml 78 | | `sfdx_root_folder` | _optional_ | Path on the current repository to the root folder of sfdx folder. Ex: sfdx_project 79 | | `deploy_testlevel` | _optional_ | TestLevel applied on the deploy. Default is `RunLocalTests`. If specified `RunSpecifiedTests`, the action will execute only the classes mentioned in the manifest file that contains `@isTest` tag | 80 | | `deploy_wait_time` | _optional_ | Wait time for deployment to finish in minutes. Default is `60` | 81 | | `destructive_path` | _optional_ | Path on the repo where the destructive changes directory is - if not informed, it's not executed | 82 | | `data_factory` | _optional_ | Path on the repo where an APEX script used as a data factory is stored. if not informed, it's not executed | 83 | | `default_source_path` | _optional_ | Path on the repo where your source files are stored, for SFDX project this is normally `force-app/main/default` 84 | 85 | 86 | # Note for destructives changes. 87 | 88 | The `destructive_path` input is a path folder, with two files inside. For example if we have the following folder structure 89 | 90 | ```bash 91 | |-- config 92 | |-- devops 93 | |-- force-app 94 | |-- manifest 95 | |-- releases 96 | |-- 01_releases 97 | |-- destructive 98 | |-- package.xml 99 | |-- destructiveChanges.xml 100 | |-- scripts 101 | |-- .forceignore 102 | |-- .gitignore 103 | ... 104 | |-- sfdx-project.json 105 | ``` 106 | 107 | The `destructive_path` will be `releases/01_releases/destructive`. 108 | 109 | 110 | ## Contributing to the Repository 111 | 112 | If you find any issues or opportunities for improving this repository, fix them! Feel free to contribute to this project by [forking](http://help.github.com/fork-a-repo/) this repository and making changes to the content. Once you've made your changes, share them back with the community by sending a pull request. See [How to send pull requests](http://help.github.com/send-pull-requests/) for more information about contributing to GitHub projects. 113 | 114 | ## Reporting Issues 115 | 116 | If you find any issues with this demo that you can't fix, feel free to report them in the [issues](https://github.com/forcedotcom/sfdx-bitbucket-org/issues) section of this repository. 117 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: 'sfdx-orgdev-build-deploy' 2 | author: Tiago Nascimento - tnascimento@salesforce.com 3 | description: 'Convert a Salesforce DX project into a metadata package and deploy it' 4 | inputs: 5 | type: 6 | description: 'Type of deployment, could be one of the two: production or sandbox (default)' 7 | required: true 8 | default: 'sandbox' 9 | 10 | certificate_path: 11 | description: 'Path for the encrypted certificate to be used on force:auth:jwt:grant' 12 | required: true 13 | 14 | decryption_key: 15 | description: 'Decryption key to be used to decrypt the certificate' 16 | required: true 17 | 18 | decryption_iv: 19 | description: 'Decryption IV to be used to decrypt the certificate' 20 | required: true 21 | 22 | client_id: 23 | description: 'Client ID / Consumer Key of a connected app to the target destination' 24 | required: true 25 | 26 | username: 27 | description: 'Username to be used to connect in the target org' 28 | required: true 29 | 30 | checkonly: 31 | description: 'Boolean (true/false) that will indicate whether the deploy is a checkonly deploy or not. If is a checkonly, on force:mdapi:deploy command the option -c will be appended' 32 | required: true 33 | default: true 34 | 35 | sfdx_root_folder: 36 | description: 'Path on the current repository to the root folder of the sfdx project' 37 | required: false 38 | 39 | manifest_path: 40 | description: 'Path on the current repository (or on the root folder of the sfdx project if sfdx_root_folder is set) to one or more package.xml that represents the packages to be deployed. Based on this files the metadata package will be created and deployed in the order specified' 41 | required: true 42 | 43 | deploy_testlevel: 44 | description: 'Specifies which tests are run as part of a deployment. Valid values are: NoTestRun, RunSpecifiedTests, RunLocalTests, RunAllTestsInOrg' 45 | required: true 46 | default: RunLocalTests 47 | 48 | deploy_wait_time: 49 | description: 'Wait time for deployment to finish in minutes. Default is 60' 50 | required: false 51 | default: '60' 52 | 53 | destructive_path: 54 | description: 'Path for the destructive changes directory' 55 | required: false 56 | 57 | data_factory: 58 | description: 'Path on the current repository to the apex script that will be used to create data required by the package' 59 | required: false 60 | 61 | default_source_path: 62 | description: 'Default source path to look for apex classes. Only used when deploy_testlevel is RunSpecifiedTests' 63 | required: false 64 | default: 'force-app/main/default' 65 | 66 | runs: 67 | using: 'node12' 68 | main: 'dist/index.js' 69 | 70 | branding: 71 | icon: 'cloud-lightning' 72 | color: 'blue' 73 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sfdx-orgdev-build-deploy", 3 | "version": "1.0.0", 4 | "description": "SFDX Org Development Model Build & Deploy action", 5 | "main": "index.js", 6 | "scripts": { 7 | "lint": "eslint src/index.js", 8 | "package": "ncc build src/index.js -o dist", 9 | "test": "eslint src/index.js && jest" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/tiagonnascimento/sfdx-orgdev-build-deploy.git" 14 | }, 15 | "keywords": [ 16 | "GitHub", 17 | "Actions", 18 | "JavaScript", 19 | "Salesforce", 20 | "SFDX", 21 | "Org Development Model" 22 | ], 23 | "author": "Tiago Nascimento ", 24 | "contributors": [ 25 | "Manuel Cortes " 26 | ], 27 | "license": "MIT", 28 | "bugs": { 29 | "url": "https://github.com/tiagonnascimento/sfdx-orgdev-build-deploy/issues" 30 | }, 31 | "homepage": "https://github.com/tiagonnascimento/sfdx-orgdev-build-deploy#readme", 32 | "dependencies": { 33 | "@actions/core": "^1.2.6", 34 | "@actions/github": "^4.0.0", 35 | "properties-reader": "^2.0.0", 36 | "xml2js": "^0.4.23" 37 | }, 38 | "devDependencies": { 39 | "@vercel/ncc": "^0.24.0", 40 | "eslint": "^6.3.0", 41 | "jest": "^24.9.0" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const core = require('@actions/core'); 2 | const github = require('@actions/github'); 3 | var propertiesReader = require('properties-reader'); 4 | const dependencies = require('./utils/install-dependencies.js'); 5 | const sfdx = require('./utils/sfdx.js'); 6 | 7 | try { 8 | 9 | core.debug("=== index.js ==="); 10 | const payload = JSON.stringify(github.context.payload, undefined, 2) 11 | core.debug(`The event payload: ${payload}`); 12 | 13 | //Variables declaration 14 | var cert = {}; 15 | var login = {}; 16 | var deploy = {}; 17 | 18 | //Install dependecies 19 | dependencies.install(); 20 | 21 | //Load cert params 22 | cert.certificatePath = core.getInput('certificate_path'); 23 | cert.decryptionKey = core.getInput('decryption_key'); 24 | cert.decryptionIV = core.getInput('decryption_iv'); 25 | 26 | //Load login params 27 | login.clientId = core.getInput('client_id'); 28 | login.orgType = core.getInput('type'); 29 | login.username = core.getInput('username'); 30 | 31 | //Load deploy params 32 | deploy.defaultSourcePath = core.getInput('default_source_path'); 33 | deploy.defaultTestClass = core.getInput('default_test_class'); 34 | deploy.manifestToDeploy = core.getInput('manifest_path'); 35 | deploy.sfdxRootFolder = core.getInput('sfdx_root_folder'); 36 | deploy.destructivePath = core.getInput('destructive_path'); 37 | deploy.dataFactory = core.getInput('data_factory'); 38 | deploy.checkonly = (core.getInput('checkonly') === 'true' )? true : false; 39 | deploy.testlevel = core.getInput('deploy_testlevel'); 40 | deploy.deployWaitTime = core.getInput('deploy_wait_time') || '60'; // Default wait time is 60 minutes 41 | 42 | //Login to Org 43 | sfdx.login(cert,login); 44 | 45 | //Deply/Checkonly to Org 46 | sfdx.deploy(deploy); 47 | 48 | //Destructive deploy 49 | sfdx.destructiveDeploy(deploy); 50 | 51 | //Executes data factory script 52 | sfdx.dataFactory(deploy); 53 | 54 | } catch (error) { 55 | core.setFailed(error.message); 56 | } 57 | 58 | -------------------------------------------------------------------------------- /src/index.test.js: -------------------------------------------------------------------------------- 1 | const cp = require('child_process'); 2 | const path = require('path'); 3 | 4 | // shows how the runner will run a javascript action with env / stdout protocol 5 | test('test runs', () => { 6 | const ip = path.join(__dirname, 'index.js'); 7 | console.log(cp.execSync(`node ${ip}`).toString()); 8 | }) 9 | -------------------------------------------------------------------------------- /src/utils/exec-command.js: -------------------------------------------------------------------------------- 1 | const core = require('@actions/core') 2 | const { spawnSync } = require('child_process'); 3 | 4 | module.exports.run = function(command, args, workingFolder = null) { 5 | var extraParams = {}; 6 | 7 | //extraParams.shell = true; 8 | //extraParams.env = process.env; 9 | //extraParams.stdio = [process.stdin, process.stdout , process.stderr]; 10 | if (workingFolder) { 11 | extraParams.cwd = workingFolder; 12 | } 13 | extraParams.encoding = 'utf-8'; 14 | extraParams.maxBuffer = 1024 * 1024 * 10 15 | 16 | var spawn = spawnSync(command, args, extraParams); 17 | 18 | if (spawn.stdout) { 19 | 20 | core.info("Command executed: " + command) 21 | core.info("With the following args: " + args.toString()); 22 | core.info("Having the following return: " + spawn.stdout.toString()); 23 | } 24 | 25 | if (spawn.error !== undefined || spawn.status !== 0) { 26 | var errorMessage = ''; 27 | if (spawn.error !== undefined) { 28 | errorMessage = spawn.error; 29 | } 30 | 31 | if (spawn.stderr !== undefined) { 32 | errorMessage += " " + spawn.stderr.toString(); 33 | } 34 | core.error(errorMessage); 35 | throw Error(errorMessage); 36 | } 37 | } -------------------------------------------------------------------------------- /src/utils/install-dependencies.js: -------------------------------------------------------------------------------- 1 | const core = require('@actions/core') 2 | const execCommand = require('./exec-command.js'); 3 | 4 | var fnInstallSFDX = function(){ 5 | core.info('=== Downloading and installing SFDX cli ==='); 6 | //execCommand.run('wget', ['https://developer.salesforce.com/media/salesforce-cli/sfdx-cli/channels/stable/sfdx-cli-v7.72.0-697e9faee2-linux-x64.tar.xz']); 7 | execCommand.run('wget', ['https://developer.salesforce.com/media/salesforce-cli/sfdx-linux-amd64.tar.xz']); 8 | execCommand.run('mkdir', ['-p', 'sfdx-cli']); 9 | //execCommand.run('tar', ['xJf', 'sfdx-cli-v7.72.0-697e9faee2-linux-x64.tar.xz', '-C', 'sfdx-cli', '--strip-components', '1']); 10 | execCommand.run('tar', ['xJf', 'sfdx-linux-amd64.tar.xz', '-C', 'sfdx-cli', '--strip-components', '1']); 11 | execCommand.run('./sfdx-cli/install', []); 12 | core.info('=== SFDX cli installed ==='); 13 | }; 14 | 15 | module.exports.install = function(command, args) { 16 | //Installs Salesforce DX CLI 17 | fnInstallSFDX(); 18 | 19 | }; 20 | -------------------------------------------------------------------------------- /src/utils/sfdx.js: -------------------------------------------------------------------------------- 1 | const core = require('@actions/core') 2 | const path = require('path'); 3 | const execCommand = require('./exec-command.js'); 4 | const fs = require('fs'); 5 | const xml2js = require('xml2js'); 6 | 7 | let getApexTestClass = function(manifestpath, classesPath, defaultTestClass){ 8 | core.info("=== getApexTestClass ==="); 9 | var parser = new xml2js.Parser(); 10 | var typeTmp = null; 11 | var classes = null; 12 | var classNameTmp = null; 13 | var testClasses = []; 14 | var xml = fs.readFileSync(manifestpath, "utf8"); 15 | var fileContentTmp = null; 16 | 17 | parser.parseString(xml, function (err, result) { 18 | for(var i in result.Package.types){ 19 | typeTmp = result.Package.types[i]; 20 | if("ApexClass" === typeTmp.name[0]){ 21 | classes = typeTmp.members; 22 | } 23 | } 24 | }); 25 | 26 | if(classes){ 27 | for(var i = 0; i < classes.length; i++){ 28 | classNameTmp = classes[i]; 29 | fileContentTmp = fs.readFileSync(classesPath+"/"+classNameTmp+".cls", "utf8"); 30 | if(fileContentTmp.toLowerCase().includes("@istest")){ 31 | testClasses.push(classNameTmp); 32 | } 33 | } 34 | }else{ 35 | if(defaultTestClass){ 36 | testClasses.push(defaultTestClass); 37 | } 38 | 39 | } 40 | 41 | return testClasses.join(","); 42 | } 43 | 44 | let login = function (cert, login){ 45 | core.info("=== login ==="); 46 | core.debug('=== Decrypting certificate'); 47 | execCommand.run('openssl', ['enc', '-nosalt', '-aes-256-cbc', '-d', '-in', cert.certificatePath, '-out', 'server.key', '-base64', '-K', cert.decryptionKey, '-iv', cert.decryptionIV]); 48 | 49 | core.info('==== Authenticating in the target org'); 50 | const instanceurl = login.orgType === 'sandbox' ? 'https://test.salesforce.com' : 'https://login.salesforce.com'; 51 | core.info('Instance URL: ' + instanceurl); 52 | execCommand.run('sfdx', ['force:auth:jwt:grant', '--instanceurl', instanceurl, '--clientid', login.clientId, '--jwtkeyfile', 'server.key', '--username', login.username, '--setalias', 'sfdc']); 53 | }; 54 | 55 | let deploy = function (deploy){ 56 | core.info("=== deploy ==="); 57 | 58 | var manifestsArray = deploy.manifestToDeploy.split(","); 59 | var sfdxRootFolder = deploy.sfdxRootFolder; 60 | 61 | var manifestTmp; 62 | var testClassesTmp; 63 | 64 | for(var i = 0; i < manifestsArray.length; i++){ 65 | manifestTmp = manifestsArray[i]; 66 | 67 | var argsDeploy = ['force:source:deploy', '--wait', deploy.deployWaitTime, '--manifest', manifestTmp, '--targetusername', 'sfdc', '--json']; 68 | 69 | if(deploy.checkonly){ 70 | core.info("===== CHECH ONLY ===="); 71 | argsDeploy.push('--checkonly'); 72 | } 73 | 74 | if(deploy.testlevel == "RunSpecifiedTests"){ 75 | testClassesTmp = getApexTestClass( 76 | sfdxRootFolder ? path.join(sfdxRootFolder, manifestTmp) : manifestTmp, 77 | sfdxRootFolder ? path.join(sfdxRootFolder, deploy.defaultSourcePath, 'classes') : path.join(deploy.defaultSourcePath, 'classes'), 78 | deploy.defaultTestClass); 79 | 80 | core.info("classes are : " + testClassesTmp); 81 | 82 | if(testClassesTmp){ 83 | argsDeploy.push("--testlevel"); 84 | argsDeploy.push(deploy.testlevel); 85 | 86 | argsDeploy.push("--runtests"); 87 | argsDeploy.push(testClassesTmp); 88 | }else{ 89 | argsDeploy.push("--testlevel"); 90 | argsDeploy.push("RunLocalTests"); 91 | } 92 | }else{ 93 | argsDeploy.push("--testlevel"); 94 | argsDeploy.push(deploy.testlevel); 95 | } 96 | 97 | execCommand.run('sfdx', argsDeploy, sfdxRootFolder); 98 | } 99 | }; 100 | 101 | let destructiveDeploy = function (deploy){ 102 | core.info("=== destructiveDeploy ==="); 103 | if (deploy.destructivePath !== null && deploy.destructivePath !== '') { 104 | core.info('=== Applying destructive changes ===') 105 | var argsDestructive = ['force:mdapi:deploy', '-d', deploy.destructivePath, '-u', 'sfdc', '--wait', deploy.deployWaitTime, '-g', '--json']; 106 | if (deploy.checkonly) { 107 | argsDestructive.push('--checkonly'); 108 | } 109 | execCommand.run('sfdx', argsDestructive); 110 | } 111 | }; 112 | 113 | let dataFactory = function (deploy){ 114 | core.info("=== dataFactory ==="); 115 | if (deploy.dataFactory && !deploy.checkonly) { 116 | core.info('Executing data factory'); 117 | execCommand.run('sfdx', ['force:apex:execute', '-f', deploy.dataFactory, '-u', 'sfdc']); 118 | } 119 | }; 120 | 121 | 122 | module.exports.deploy = deploy; 123 | module.exports.login = login; 124 | module.exports.destructiveDeploy = destructiveDeploy; 125 | module.exports.dataFactory = dataFactory; --------------------------------------------------------------------------------