#!/usr/bin/env ruby require 'pp' require 'yaml' require 'fileutils' # Disable stdout buffer STDOUT.sync = true # Static files CONFIG_DIR = '~/.sf' CONFIG_FILE = '~/.sf/credentials.yaml' GLOBAL_CONFIG_FILE = '~/.sf/salesforce.yaml' SANDBOX_CONFIG_FILE = '~/.sf/salesforce.sbox' # Load configurations, if directories and files don't # exists, create them empty FileUtils.mkdir File.expand_path CONFIG_DIR if not Dir.exists? File.expand_path CONFIG_DIR config = {} [CONFIG_FILE,GLOBAL_CONFIG_FILE].each do |file| file = File.expand_path file if File.exists? file config_content = YAML::load(File.open(file).read) config.merge! config_content if config_content else FileUtils.touch file end end # Grab variables from env if not in the config files config[:git_dir] = ENV["GIT_DIR"] || config[:git_dir] config[:tmp_dir] = ENV["TMP_DIR"] || config[:tmp_dir] config[:version_file] = ENV["VERSION_FILE"] || config[:version_file] config[:build_number_pattern] = ENV["BUILD_NUMBER_PATTERN"] || config[:build_number_pattern] config[:commit_hash_pattern] = ENV["COMMIT_HASH_PATTERN"] || config[:commit_hash_pattern] config[:git_repo] = ENV["GIT_REPO"] || config[:git_repo] config[:username] = ENV["USERNAME"] || config[:username] config[:password] = ENV["PASSWORD"] || config[:password] # Normalize file paths: config[:git_dir] = File.expand_path config[:git_dir] config[:tmp_dir] = File.expand_path config[:tmp_dir] config[:version_file] = File.expand_path config[:version_file] # Relative ignore files config[:deploy_ignore_files] = ENV["DEPLOY_IGNORE_FILES"].nil? ? config[:deploy_ignore_files] : ENV["DEPLOY_IGNORE_FILES"].split(',') # Read sandbox environment begin sandbox = File.open(File.expand_path(SANDBOX_CONFIG_FILE)).read rescue sandbox = nil end require 'salesforcedeploytool' program :version, SalesforceDeployTool::VERSION program :description, 'A cli tool to help manage and deploy salesforce sandboxes with git' command :init do |c| c.syntax = 'sf init [options]' c.summary = 'Initialize salesforce sandbox from git' c.description = "Clone the #{config[:git_repo]} to #{config[:git_dir]}" c.example 'usage', 'sf init' c.option "--sandbox NAME", "-s NAME", "use 'prod' to deploy production or sandbox name" c.action do |args, options| # short flag mapping options.sandbox = options.s if options.s # Parameter validation: if options.sandbox.nil? and sandbox.nil? puts "error: please specify sandbox using --sandbox or sf sandbox" exit 1 end config[:sandbox] = options.sandbox || sandbox # Initialize sfdt = SalesforceDeployTool::App.new config # Clone sfdt.clone end end command :pull do |c| c.syntax = 'sf pull' c.summary = 'Pull code from the sandbox' c.description = "Pull code from sandbox and update #{config[:git_dir]}" c.example 'usage:', 'sf pull' c.option "--append", "Pull code appending it to the local repository" c.option "--debug", "Verbose output" c.option "--sandbox NAME", "-s NAME", "use 'prod' to deploy production or sandbox name" c.action do |args, options| # short flag mapping options.sandbox = options.s if options.s # Parameter validation: if options.sandbox.nil? and sandbox.nil? puts "error: please specify sandbox using --sandbox or sf sandbox" exit 1 end config[:sandbox] = options.sandbox || sandbox config[:debug] = options.debug.nil? ? false : true # Initialize sfdt = SalesforceDeployTool::App.new config # Clean all files from repo sfdt.clean_git_dir unless options.append # Pull the changes sfdt.pull "INFO: Pulling changes from #{config[:sandbox]} " sfdt.clean_version end end command :push do |c| c.syntax = 'sf push [options]' c.summary = 'Push code into a sandbox' c.description = '' c.example 'description', "Push the code that is located into #{config[:git_dir]} into the active sandbox" c.option "--sandbox NAME", "-s NAME", "use 'prod' to deploy production or sandbox name" c.option "--debug", "Verbose output" c.option "--test", "-T", "Deploy and test" c.option "--exclude LIST", "-x LIST", "a CSV list of metadata to exclude when creating destructiveChange.xml" c.option "--append", "Disable destructive change and do an append deploy" c.option "--build_number NUMBER","Record build number on version file" c.action do |args, options| # short flag mapping options.test = true if options.T options.exclude = options.x if options.x options.sandbox = options.s if options.s # Parameter validation: if options.sandbox.nil? and sandbox.nil? puts "error: please specify the sandbox to pull from using --sandbox" exit 1 end config[:sandbox] = options.sandbox || sandbox config[:test] = options.test.nil? ? false : true config[:debug] = options.debug.nil? ? false : true # Initialize sfdt = SalesforceDeployTool::App.new config.clone # Remove destructive change if there is one DESTRUCTIVE_CHANGE_FILE = File.join(config[:git_dir],'src','destructiveChanges.xml') FileUtils.rm DESTRUCTIVE_CHANGE_FILE if File.exists? DESTRUCTIVE_CHANGE_FILE if ! options.append # Pull changes from sandbox to temporary directory: config_tmp = config.clone config_tmp[:git_dir] = config_tmp[:tmp_dir] FileUtils.rm_rf config_tmp[:git_dir] if File.exists? config_tmp[:git_dir] sfdt_tmp = SalesforceDeployTool::App.new config_tmp sfdt_tmp.clone sfdt_tmp.clean_git_dir sfdt_tmp.pull "INFO: Pulling changes from #{config[:sandbox]} to temporary directory #{config[:tmp_dir]} to generate destructiveChanges.xml " # Create destructiveChanges.xml puts "INFO: Creating destructive changes xml" dc_gen = Dcgen::App.new dc_gen.master = File.join(config[:git_dir],'src') dc_gen.destination = File.join(config[:tmp_dir],'src') dc_gen.output = DESTRUCTIVE_CHANGE_FILE dc_gen.exclude = options.exclude.split(',') unless options.exclude.nil? dc_gen.verbose = false if not options.debug dc_gen.generate_destructive_changes # Push code to sandbox with Destructive changes begin # Set version sfdt.build_number = options.build_number if not options.build_number.nil? sfdt.set_version # Enable test if option enabled sfdt.test = options.test.nil? ? false : true # Push sfdt.push ensure sfdt.clean_version end end end end command :sandbox do |c| c.syntax = 'sf sandbox SANDBOX_NAME' c.summary = 'Set sandbox to work on, this can be overriden by --sandbox ' c.description = 'Set the sandbox to work with pull and push. If no parameter defined, it will print the current sandbox selected.' c.action do |args, options| if args.size > 1 puts "error: Wrong number of arguments" end if args.size == 0 if ! sandbox.nil? puts "sandbox: " + sandbox exit 0 else puts "WARN: Sandbox has not been set yet" exit 1 end end File.open(File.expand_path(SANDBOX_CONFIG_FILE),'w').write args.first end end command :config do |c| c.syntax = 'sf config' c.summary = 'Go through the configuration wizard to config your environment' c.action do |args, options| if args.size != 0 puts "error: Wrong number of arguments" end config_new = {} config_new[:username] = ask "Please enter your SalesForce production login user name" do |q| q.validate = /^\S+@\S+$/ end.to_s config_new[:password] = ask "Please enter your Salesforce production password" do |q| q.echo = "x" end.to_s git_name = ask "Please enter your Full name to be used as commit owner on GIT" do |q| q.validate = /^[a-zA-Z\s]+$/ end git_email = ask "Please enter your email to be used as commit email on GIT" do |q| q.validate = /^\S+@\S+$/ end %x[git config --global user.email "#{git_email}"] %x[git config --global user.name "#{git_name}"] config[:username] = config_new[:username] config[:password] = config_new[:password] File.open(File.expand_path(CONFIG_FILE),'w').write config_new.to_yaml end end default_command :help