require 'rake/clean' require 'rake/packagetask' require 'bundler' # We setup our configuration to create a fake "installation" to this directory. # In cases you run everything locally, it dumps data, pid, and log files under # here. # # See tasks like basic:start or cloud:start for real usage. INSTALL_DIR = '/tmp/hws' CLEAN.include(INSTALL_DIR) task :set_writable do Dir.glob('**/Gemfile.lock').each do |f| File.chmod(0644, f) end end Rake::PackageTask.new('helix-web-services', :noversion) do |package| package.need_tar = true package.package_files.include('data/certs/*') package.package_files.include('contrib/**/*') package.package_files.include('doc/**/*') package.package_files.include('git_fusion_strings/**/*') package.package_files.include('helix_web_services/**/*') package.package_files.include('helix_web_services_client/**/*') package.package_files.include('packaging/**/*') package.package_files.include('Gemfile') package.package_files.include('Gemfile.lock') package.package_files.include('Rakefile') package.package_files.include('LICENSE') package.package_files.include('helix-web-services-notes.txt') end CLEAN.include('pkg') task :package => [:set_writable] desc 'Create primary source deliverables' task :build => [:package] desc 'Rebuild the main ASCIIDoc documentation' task :asciidoc do system('bundle exec asciidoctor -o doc-output/p4ws.html doc/p4ws.asc') || fail('asciidoctor failed') end CLEAN.include('**/doc-output') namespace :all do desc 'Rebuild documentation across system' task :doc => [:asciidoc] do system('cd helix_web_services_client && bundle exec rake yard') || fail('rake yard failed for helix_web_services_client') system('cd helix_web_services && bundle exec rake yard') || fail('rake yard failed for helix_web_services') end end # "Raymond" is an internal server API used in Helix Cloud, that Helix Web # Services integration uses for Helix Sync methods. This mock instance lets # us run tests in our "cloud" configuration setup. namespace :mock_raymond do desc 'start mock cloud server' task :start do start_mock_raymond end desc 'kill mock cloud server' task :stop do stop_mock_raymond end end namespace :p4d do namespace :basic do desc 'init p4d in basic mode' task :start do initialize_p4d('data/p4init') end end namespace :cloud do desc 'init p4d in cloud mode' task :start do initialize_p4d('data/cloudinit') end end desc 'halt running p4d' task :stop do stop_p4d end end # The 'basic' configuration does not include Helix Cloud or Git Fusion # # Note: Git Fusion only runs on Linux, so that's left out of the dev environment # for now, until I decide to switch my dev environment to run on Linux. :) namespace :basic do desc 'Start nginx, p4d, unicorn with basic configuration' task :start do |t| err = initialize_tmp_install err ||= initialize_p4d('data/p4init') err ||= hws_launch_tmp_install('start') fail('start failed') if err end desc 'Run helix_web_services and helix_web_services_client specs against the local basic config' task :spec do err = run_hws_server_tests(spec_output: 'basic_helix_web_services_specs.html') err ||= run_hws_client_specs(spec_output: 'basic_helix_web_services_client_specs.html', p4port: 'localhost:1666') fail('spec failed') if err end desc 'Stop and clean up the basic configuration' task :stop do err ||= hws_launch_tmp_install('stop') err ||= stop_p4d fail('stop failed') if err end end namespace :cloud do desc 'Start mock_raymond, nginx, unicorn, and p4d for Helix Cloud mock testing' task :start => 'mock_raymond:start' do err = initialize_tmp_install err ||= initialize_p4d('data/cloudinit') err ||= hws_launch_tmp_install('start', cloud_settings: 'helix_web_services/config/hws_cloud_settings.conf.example') end desc 'Run helix_web_services_client specs against the local basic config' task :spec do err = run_hws_server_tests(cloud_settings: 'helix_web_services/config/hws_cloud_settings.conf.example', spec_output: 'cloud_helix_web_services_specs.html') err ||= run_hws_client_specs(spec_output: 'cloud_helix_web_services_client_specs.html', p4port: 'localhost:1666', cloud_test: true) fail('spec failed') if err end desc 'Shut down Helix Cloud setup' task :stop => 'mock_raymond:stop' do err ||= hws_launch_tmp_install('stop') err ||= stop_p4d fail('stop failed') if err end end namespace :remote do desc 'Exec specs against a remote HWS instance' task :spec, [:hws_url, :p4port] do |t, args| hws_url = args[:host] p4port = args[:p4port] err = run_hws_client_specs(spec_output: "remote_#{host}_helix_web_services_client_specs.html", p4port: p4port, hws_url: hws_url) fail('spec failed') if err end end #============================================================================= # Helper Methods #============================================================================= # Execute helix_web_services_client specs, with different configurations: # # - cloud_test: if true, we assume the mock_raymond server is running # # - spec_output: specify different names for different configurations, output # will go under ./spec-output # # - p4port: Some tests require seeding test data directly, indicate the # p4d port, defaults to 'localhost:1666' # # - hws_url: The base URL to the HWS server, defaults "https://localhost:9000/" def run_hws_client_specs(cloud_test: false, spec_output: 'helix_web_services_client_specs.html', p4port: 'localhost:1666', hws_url: 'https://localhost:9000/') output_file = File.absolute_path("../spec-output/#{spec_output}", __FILE__) ok = system('cd helix_web_services_client && ' + "CLOUD_TEST=#{cloud_test.to_s} " + "SPEC_OUT=#{output_file} " + "P4PORT=#{p4port} " + "HWS_URL=#{hws_url} " + 'bundle exec rake spec') return true unless ok end # Execute helix_web_services specs, with different configurations. # # Note: This typically does *not* require unicorn and nginx to run, just p4d. # It is a different configuration that *can* be easier for debugging purposes. # # - cloud_settings: Path to the cloud settings file if you want to validate # Helix Cloud logic # # - spec_output: Test result output file name. def run_hws_server_tests(cloud_settings: '', spec_output: '') output_file = File.absolute_path("../spec-output/#{spec_output}", __FILE__) settings_file = File.absolute_path("../#{cloud_settings}", __FILE__) if !cloud_settings.empty? ok = system('cd helix_web_services && ' + "CLOUD_SETTINGS=#{settings_file} " + "SPEC_OUT=#{output_file} " + 'bundle exec rake spec') return true unless ok end # Create the temporary install configuration file that points to files in # a temporary directory, /tmp/hws. # # It is very annoying in development if this becomes a long-winded path. def initialize_tmp_install unless Dir.exist?(INSTALL_DIR) FileUtils.mkdir_p(INSTALL_DIR) end unless Dir.exist?(data_dir) FileUtils.mkdir_p(data_dir) end unless Dir.exist?(log_dir) FileUtils.mkdir_p(log_dir) end unless Dir.exist?(run_dir) FileUtils.mkdir_p(run_dir) end create_dev_config_file end # Runs hws_launch in temp install directory. def hws_launch_tmp_install(cmd, cloud_settings: nil) puts "hws_launch #{cmd}" if cloud_settings.nil? cloud_settings = '' else cloud_settings = "CLOUD_SETTINGS=#{File.absolute_path("../#{cloud_settings}", __FILE__)} " end launch_ok = system('cd helix_web_services && ' + "HWS_CONFIG=#{hws_config_path} " + cloud_settings + "bundle exec #{hws_launch_path} #{cmd}") !launch_ok end # This generates /tmp/helix-web-services.conf that points to locations in the # local directory... as appropriate. def create_dev_config_file(enable_https: false) IO.write(hws_config_path, <<-END.gsub(/^[ ]{4}/, '') DATA_DIR: '#{data_dir}' ENABLE_HTTPS: #{enable_https} LOG_DIR: '#{log_dir}' NGINX_COMMAND: '#{nginx_command}' NGINX_CONFIG_PATH: '#{nginx_config_path}' NGINX_PORT: 9000 P4TRUST: '#{p4trust_path}' RACKUP_CONFIG: '#{rackup_config_path}' RUN_DIR: '#{run_dir}' SSL_CERTIFICATE_PATH: '#{ssl_certificate_path}' SSL_CERTIFICATE_KEY_PATH: '#{ssl_certificate_key_path}' SYSTEM_GROUP: null SYSTEM_USER: '#{ENV['USER']}' WORKSPACE_DIR: '#{workspace_dir}' UNICORN_COMMAND: '#{unicorn_command}' UNICORN_CONFIG_PATH: '#{unicorn_config_path}' UNICORN_CONNECTION: '#{unicorn_socket_path}' END ) nil end def initialize_p4d(data_dir) puts 'starting p4d from ' + data_dir ok = system('p4util kill') return true unless ok if Dir.exist?('/tmp/p4util/p4droot') require 'fileutils' FileUtils.rmtree('/tmp/p4util/p4droot') end ok = system('p4util start') return true unless ok ok = system("p4util init #{data_dir}") return true unless ok end def stop_p4d ok = system('p4util kill') return true unless ok if Dir.exist?('/tmp/p4util/p4droot') require 'fileutils' FileUtils.rmtree('/tmp/p4util/p4droot') end nil end def initialize_remote_p4d(host, data_dir) ok = system("p4util init -p #{host}:1666 -a #{data_dir}") return true unless ok end def start_mock_raymond(cloud_settings: '') # return puts 'starting mock_raymond' begin Bundler.with_clean_env do ok = system( 'cd helix_web_services/mock_raymond && ' + 'UNICORN_PID=/tmp/mock_raymond_unicorn.pid ' + 'RAILS_ENV=test ' + 'MOCKRAYMOND_STDOUT_PATH=/tmp/mock_raymond_unicorn.out ' + 'MOCKRAYMOND_STDERR_PATH=/tmp/mock_raymond_unicorn.err ' + 'P4PORT=localhost:1666 ' + 'P4CHARSET=utf8 ' + 'bundle exec unicorn -c config/unicorn.rb -D' ) return true unless ok while connect_to_server(3000) == false sleep(0.1) end end return false rescue Exception => e puts "Error: #{e.message}" return true end end def stop_mock_raymond if File.exist?('/tmp/mock_raymond_unicorn.pid') rails_pid = IO.read('/tmp/mock_raymond_unicorn.pid').to_i puts "killing pid: #{rails_pid}" Process.kill('TERM', rails_pid) File.unlink('/tmp/mock_raymond_unicorn.pid') end end def hws_config_path "#{INSTALL_DIR}/helix-web-services.conf" end # In development mode, we use the rackup configuration directly in the tree def rackup_config_path File.absolute_path('../helix_web_services/config.ru', __FILE__) end # Note: if you're not running within bundler, or RVM with fancy bundler # shell integration, this is possibly going to fail def hws_launch_path File.absolute_path('../helix_web_services/bin/hws_launch', __FILE__) end # In general, unicorn should be installed already, and in most dev environments # is in the path via RVM. def unicorn_command unicorn_path = `which unicorn`.strip fail 'unicorn not found' if unicorn_path.empty? unicorn_path end def nginx_command nginx_path = `which nginx`.strip fail 'nginx not found' if nginx_path.empty? nginx_path end def nginx_config_path "#{INSTALL_DIR}/nginx.conf" end def unicorn_config_path "#{INSTALL_DIR}/unicorn.conf" end def data_dir "#{INSTALL_DIR}/data" end def log_dir "#{INSTALL_DIR}/logs" end def run_dir "#{INSTALL_DIR}/run" end def unicorn_socket_path "unix:#{run_dir}/unicorn.sock" end def workspace_dir "#{data_dir}/workspaces" end def ssl_certificate_path File.absolute_path('../data/certs/nginx.crt', __FILE__) end def ssl_certificate_key_path File.absolute_path('../data/certs/nginx.key', __FILE__) end def p4trust_path "#{INSTALL_DIR}/p4trust" end def connect_to_server(port) require 'socket' begin s = TCPSocket.new 'localhost', port s.close return true rescue Exception => e return false end end