#!/usr/bin/env ruby require 'rubygems' require 'nixenvironment' require 'commander/import' require 'yaml' include Nixenvironment # :name is optional, otherwise uses the basename of this executable program :name, 'nixenvironment' program :version, VERSION program :description, 'NIX projects build and deploy utility' command :update do |c| c.syntax = 'nixenvironment update' c.description = 'Install or update ninbas and other environment stuff' c.option '--ninbas NAME', String, 'Select ninbas branch, tag or revision to clone' c.action do |args, options| update(options.ninbas) end end command :build do |c| c.syntax = 'nixenvironment build [options]' c.description = 'Build project for selected configuration and make signed/resigned ipa' c.option '--config NAME', String, 'Select configuration' c.option '--ipa NAME', String, 'Select sign' c.action do |args, options| options.default :config => 'Debug', :ipa => 'ipa' read_config build_settings = setup(options.config) prebuild(build_settings) build(options.config, options.ipa) revert_info_plist end end command :deploy do |c| c.syntax = 'nixenvironment deploy' c.description = 'Deploy build to mds' c.action do |args, options| read_config deploy end end command :clean do |c| c.syntax = 'nixenvironment clean' c.description = 'Remove temp files and clean all targets for xcode project' c.action do |args, options| clean end end def update(ninbas) root_working_directory = Dir.pwd target_directory = File.join(Dir.home, NIXENV_ROOT) begin Dir.mkdir(target_directory) unless Dir.exist?(target_directory) Dir.chdir(target_directory) REPO_LIST.each do |repo_name, repo_url| unless Dir.exist?(repo_name) clone_success = system("git clone #{repo_url} --recursive") unless clone_success p("Authentication failed for #{repo_name} project!") next end end Dir.chdir(repo_name) if repo_name == BUILD_SCRIPTS if ninbas #TODO: rework me please! system("git checkout #{ninbas}") Dir.chdir('..') next end end system("git fetch -t") tags = IO.popen('git tag').readlines tags.map! { |tag| tag = tag.strip } if tags.size > 0 p("Checkout newest #{repo_name} tag...") system("git checkout #{tags.last}") end #TODO: hadle tags.size == 0 p("Checkout #{repo_name} #{tags.last} tag success!") Dir.chdir('..') end rescue @error_message = "#{$!}" ensure p(@error_message) if @error_message Dir.chdir(root_working_directory) end end def read_config begin @config = YAML.load(File.read('Config')) rescue abort('Config file processing error!') end end def working_copy_is_clean? is_clean = system(" LAST_REVISION_FILE=\"_last_revision.sh\" source ${LAST_REVISION_FILE} if [ ${WORKING_COPY_IS_CLEAN} -eq 1 ]; then echo \"Working copy is clean. Continuing...\" else echo \"error: working copy must not have local modifications.\" 1>&2 echo \"You must add following files and folders to .gitignore:\" echo \"$(git status --porcelain)\" exit 1 fi") return is_clean end def setup(config) cmd_output = nil if @config['PROJECT_TO_BUILD'] and @config['PROJECT_TO_BUILD'].length > 0 cmd_output = %x[ xcodebuild -project #{@config['PROJECT_TO_BUILD']}\ -target #{@config['PROJECT_TARGET_TO_BUILD']}\ -configuration #{config}\ -sdk #{@config['SDK']}\ -showBuildSettings ] elsif @config['WORKSPACE_TO_BUILD'] and @config['WORKSPACE_TO_BUILD'].length > 0 cmd_output = %x[ xcodebuild -workspace #{@config['WORKSPACE_TO_BUILD']}\ -scheme #{@config['WORKSPACE_SCHEME_TO_BUILD']}\ -configuration #{config}\ -sdk #{@config['SDK']}\ -showBuildSettings ] end #TODO: handle if cmd_output still nil env_vars_list = cmd_output.split(/\n/).reject(&:empty?) build_settings = Hash.new if env_vars_list and env_vars_list.length > 0 build_settings_to_strip = Hash[env_vars_list.map { |it| it.split('=', 2) }] build_settings_to_strip.each do |key, value| if key and value stripped_key = key.strip stripped_value = value.strip build_settings[stripped_key] = stripped_value end end end return build_settings end def prebuild(build_settings) save_revision_script_path = File.join(BUILD_SCRIPTS_PATH, 'SaveRevision.sh') save_build_env_vars_script_path = File.join(BUILD_SCRIPTS_PATH, 'SaveBuildEnvVars.sh') tag_icons_script_path = File.join(BUILD_SCRIPTS_PATH, 'XcodeIconTagger/tagIcons.sh') update_build_number_script_path = File.join(BUILD_SCRIPTS_PATH, 'UpdateBuildNumber.sh') update_revision_number_script_path = File.join(BUILD_SCRIPTS_PATH, 'UpdateRevisionNumber.sh') icon_path = @config['ICONS_PATH'] system("#{save_revision_script_path}") abort unless working_copy_is_clean? # TODO: rewrite SaveBuildEnvVars.sh #system("#{save_build_env_vars_script_path}") system(" echo \"#!/bin/sh\ ### AUTOGENERATED BY SaveBuildEnvVars.sh; DO NOT EDIT ### PROJECT=\"#{build_settings['PROJECT']}\" BUILT_PRODUCTS_DIR=\"#{build_settings['BUILT_PRODUCTS_DIR']}\" OBJECTS_NORMAL_DIR=\"#{build_settings['OBJECT_FILE_DIR_normal']}\" EXECUTABLE_NAME=\"#{build_settings['EXECUTABLE_NAME']}\" APP_PRODUCT=\"#{build_settings['BUILT_PRODUCTS_DIR']}/#{build_settings['EXECUTABLE_NAME']}.app\" APP_DSYM=\"#{build_settings['BUILT_PRODUCTS_DIR']}/#{build_settings['EXECUTABLE_NAME']}.app.dSYM\" APP_INFOPLIST_FILE=\"#{@config['INFOPLIST_PATH']}\" EMBEDDED_PROFILE=\"#{build_settings['BUILT_PRODUCTS_DIR']}/#{build_settings['EXECUTABLE_NAME']}.app/#{build_settings['EMBEDDED_PROFILE_NAME']}\" TARGET_NAME=\"#{build_settings['TARGET_NAME']}\" CONFIGURATION=\"#{build_settings['CONFIGURATION']}\" SDK_NAME=\"#{build_settings['SDK_NAME']}\" RESIGNED_BUNDLE_ID=\"#{build_settings['RESIGNED_BUNDLE_ID']}\" RESIGNED_BUNDLE_NAME=\"#{build_settings['RESIGNED_BUNDLE_NAME']}\" RESIGNED_ENTITLEMENTS_PATH=\"#{build_settings['RESIGNED_ENTITLEMENTS_PATH']}\"\" > _last_build_vars.sh ") # TODO: rewrite tagIcons.sh # system("#{tag_icons_script_path} #{icon_path}") # Implement all modify things in one script system("#{update_build_number_script_path}") system("#{update_revision_number_script_path}") end def build(config, ipa) build_script_path = File.join(BUILD_SCRIPTS_PATH, 'Build.py') system("#{build_script_path} --project \"#{@config['PROJECT_TO_BUILD']}\"\ --target \"#{@config['PROJECT_TARGET_TO_BUILD']}\"\ --workspace \"#{@config['WORKSPACE_TO_BUILD']}\"\ --scheme \"#{@config['WORKSPACE_SCHEME_TO_BUILD']}\"\ --configuration \"#{config}\"\ --sdk \"#{@config['SDK']}\"\ --env-var-prefix \"#{@config['ENV_VAR_PREFIX']}\"") case ipa # create .ipa file from last built app product when 'ipa' make_script_path = File.join(BUILD_SCRIPTS_PATH, 'MakeIPA.sh') # resign last built app product with iPhone Developer profile and package it into .ipa file when 'resigned_ipa_for_device' make_script_path = File.join(BUILD_SCRIPTS_PATH, 'MakeResignedIPAForDevice.sh') # resign last built app product with iPhone Distribution AdHoc profile and package it into .ipa file when 'resigned_ipa_for_adhoc_distribution' make_script_path = File.join(BUILD_SCRIPTS_PATH, 'MakeResignedIPAForAdHocDistribution.sh') # resign last built app product with Appstore distribution profile and package it into .ipa file when 'resigned_ipa_for_appstore' make_script_path = File.join(BUILD_SCRIPTS_PATH, 'MakeResignedIPAForAppstore.sh') else p("Error: Unknown ipa '#{ipa}'!") end system("#{make_script_path}") if defined? make_script_path end def revert_info_plist p('Reverting Info.plist ...') system("git checkout #{@config['INFOPLIST_PATH']}") p('Done.') end # deploys built artifacts to given server def deploy deploy_script_path = File.join(BUILD_SCRIPTS_PATH, 'Deploy.sh') deploy_host = @config['DEPLOY_HOST'].nil? || @config['DEPLOY_HOST'].empty? ? ENV['DEPLOY_HOST'] : @config['DEPLOY_HOST'] deploy_path = @config['DEPLOY_PATH'].nil? || @config['DEPLOY_PATH'].empty? ? ENV['DEPLOY_PATH'] : @config['DEPLOY_PATH'] deploy_username = @config['DEPLOY_USERNAME'].nil? || @config['DEPLOY_USERNAME'].empty? ? ENV['DEPLOY_USERNAME'] : @config['DEPLOY_USERNAME'] deploy_password = @config['DEPLOY_PASSWORD'].nil? || @config['DEPLOY_PASSWORD'].empty? ? ENV['DEPLOY_PASSWORD'] : @config['DEPLOY_PASSWORD'] system("#{deploy_script_path} #{deploy_host}\ #{deploy_path}\ #{deploy_username}\ #{deploy_password}") end def clean remove_temporary_files_script_path = File.join(BUILD_SCRIPTS_PATH, 'RemoveTemporaryFiles.sh') system("#{remove_temporary_files_script_path}") system('rm -rf test-results/') system("find . -name \"*.pyc\" -exec rm -rf {} \;") system('xcodebuild -alltargets clean') end # # build unit tests and run them in simulator # def test # ${BUILD_SCRIPTS_PATH}Build.py --project "${PROJECT_TO_BUILD}" --target "${PROJECT_TARGET_TO_TEST}" --workspace "${WORKSPACE_TO_BUILD}" --scheme "${WORKSPACE_SCHEME_TO_TEST}" --configuration "Release" --sdk "${SDK_FOR_TESTS}" --env-var-prefix "${ENV_VAR_PREFIX}" # ${BUILD_SCRIPTS_PATH}RunTests.sh # ${BUILD_SCRIPTS_PATH}GenerateCodeCoverageReport.sh "${EXCLUDE_PATTERN_FOR_CODE_COVERAGE}" coverage.xml # end # # # generates code duplication report # def code_duplication_report # ${BUILD_SCRIPTS_PATH}GenerateCodeDuplicationReport.sh "${EXCLUDE_PATTERN_FOR_CODE_DUPLICATION}" duplication.xml # end # # # make SVN/git/mercurial tag, SCM_USERNAME and SCM_PASSWORD must be defined on calling this target # def tag # ${BUILD_SCRIPTS_PATH}MakeTag.sh "${SCM_USERNAME}" "${SCM_PASSWORD}" # end # # # Jenkins stores SVN credentials locally in XML, so this target gets and uses them on making tag by finding the first credential in local credential storage # # additional |echo| is needed in order to add newline, otherwise base64 encoding doesn't work # # ATTENTION: if this script picks up wrong credentials, then you should manually edit subversion.credentials file on Jenkins in order to remove the wrong credential # def svn_tag_from_jenkins: # SCM_USERNAME="$(shell xpath ../subversion.credentials \(//userName\)[1]/text\(\))"\ # SCM_PASSWORD="$(shell echo $$(xpath ../subversion.credentials \(//password\)[1]/text\(\) 2>/dev/null && echo) | openssl base64 -d)" # # tag # end # # def clean_working_copy # ${BUILD_SCRIPTS_PATH}CleanWorkingCopy.sh # end