Extract backend into a separate repo (#11360)
[spree.git] / bin / build-ci.rb
blob5a24cea0a1c3346895c023ae72cd3a692fc328bc
1 #!/usr/bin/env ruby
3 require 'pathname'
5 class Project
6   attr_reader :name
8   NODE_TOTAL = Integer(ENV.fetch('CIRCLE_NODE_TOTAL', 1))
9   NODE_INDEX = Integer(ENV.fetch('CIRCLE_NODE_INDEX', 0))
11   ROOT          = Pathname.pwd.freeze
12   VENDOR_BUNDLE = ROOT.join('vendor', 'bundle').freeze
13   ROOT_GEMFILE  = ROOT.join('Gemfile').freeze
15   BUNDLER_JOBS    = 4
16   BUNDLER_RETRIES = 3
18   DEFAULT_MODE = 'test'.freeze
20   def initialize(name)
21     @name = name
22   end
24   ALL = %w[api core emails].map(&method(:new)).freeze
25   CORE_GEMS = %w[api core].freeze
27   # Install subproject
28   #
29   # @raise [RuntimeError]
30   #   in case of failure
31   #
32   # @return [self]
33   #   otherwise
34   def install
35     chdir do
36       bundle_check || bundle_install || raise('Cannot finish gem installation')
37     end
38     self
39   end
41   # Test subproject for passing its tests
42   #
43   # @return [Boolean]
44   #   the success of the build
45   def pass?
46     chdir do
47       setup_test_app
48       run_tests
49     end
50   end
52   # Process CLI arguments
53   #
54   # @param [Array<String>] arguments
55   #
56   # @return [Boolean]
57   #   the success of the CLI run
58   def self.run_cli(arguments)
59     raise ArgumentError if arguments.length > 1
61     mode = arguments.fetch(0, DEFAULT_MODE)
63     case mode
64     when 'install'
65       install
66       true
67     when 'test'
68       test
69     else
70       raise "Unknown mode: #{mode.inspect}"
71     end
72   end
74   private
76   # Check if current bundle is already usable
77   #
78   # @return [Boolean]
79   def bundle_check
80     system("bundle check --path=#{VENDOR_BUNDLE}")
81   end
83   # Install the current bundle
84   #
85   # @return [Boolean]
86   #   the success of the installation
87   def bundle_install
88     system("bundle install --path=#{VENDOR_BUNDLE} --jobs=#{BUNDLER_JOBS} --retry=#{BUNDLER_RETRIES}")
89   end
91   # Setup the test app
92   #
93   # @return [undefined]
94   def setup_test_app
95     gemfile_path = if CORE_GEMS.include?(self.name)
96                      ROOT_GEMFILE
97                    else
98                      './Gemfile'
99                    end
101     system("bundle exec --gemfile=#{gemfile_path} rake test_app ") || raise('Failed to setup the test app')
102   end
104   # Run tests for subproject
105   #
106   # @return [Boolean]
107   #   the success of the tests
108   def run_tests
109     system("bundle exec rspec #{rspec_arguments.join(' ')}")
110   end
112   def rspec_arguments(custom_name = name)
113     args = []
114     args += %w[--order random --format documentation --profile 10]
115     if report_dir = ENV['CIRCLE_TEST_REPORTS']
116       args += %W[-r rspec_junit_formatter --format RspecJunitFormatter -o #{report_dir}/rspec/#{custom_name}.xml]
117     end
118     args
119   end
121   # Change to subproject directory and execute block
122   #
123   # @return [undefined]
124   def chdir(&block)
125     Dir.chdir(ROOT.join(name), &block)
126   end
128   # Install subprojects
129   #
130   # @return [self]
131   def self.install
132     current_projects.each do |project|
133       log("Installing project: #{project.name}")
134       project.install
135     end
136     self
137   end
138   private_class_method :install
140   # Execute tests on subprojects
141   #
142   # @return [Boolean]
143   #   the success of ALL subprojects
144   def self.test
145     projects = current_projects
146     suffix   = "#{projects.length} projects(s) on node #{NODE_INDEX.succ} / #{NODE_TOTAL}"
148     log("Running #{suffix}")
149     projects.each do |project|
150       log("- #{project.name}")
151     end
153     builds = projects.map do |project|
154       log("Building: #{project.name}")
155       project.pass?
156     end
157     log("Finished running #{suffix}")
159     projects.zip(builds).each do |project, build|
160       log("- #{project.name} #{build ? 'SUCCESS' : 'FAILURE'}")
161     end
163     builds.all?
164   end
165   private_class_method :test
167   # Return the projects active on current node
168   #
169   # @return [Array<Project>]
170   def self.current_projects
171     NODE_INDEX.step(ALL.length - 1, NODE_TOTAL).map(&ALL.method(:fetch))
172   end
173   private_class_method :current_projects
175   # Log a progress message to stderr
176   #
177   # @param [String] message
178   #
179   # @return [undefined]
180   def self.log(message)
181     warn(message)
182   end
183   private_class_method :log
186 exit Project.run_cli(ARGV)