Class: ActiveSupport::ContinuousIntegration
| Relationships & Source Files | |
| Inherits: | Object |
| Defined in: | activesupport/lib/active_support/continuous_integration.rb |
Overview
Provides a DSL for declaring a continuous integration workflow that can be run either locally or in the cloud. Each step is timed, reports success/error, and is aggregated into a collective report that reports total runtime, as well as whether the entire run was successful or not.
Example:
ActiveSupport::ContinuousIntegration.run do
step "Setup", "bin/setup --skip-server"
step "Style: Ruby", "bin/rubocop"
step "Security: Gem audit", "bin/bundler-audit"
step "Tests: Rails", "bin/rails test test:system"
if success?
step "Signoff: Ready for merge and deploy", "gh signoff"
else
failure "Skipping signoff; CI failed.", "Fix the issues and try again."
end
end
Starting with ::Rails 8.1, a default bin/ci and config/ci.rb file are created to provide out-of-the-box CI.
Constant Summary
-
COLORS =
# File 'activesupport/lib/active_support/continuous_integration.rb', line 25{ banner: "\033[1;32m", # Green title: "\033[1;35m", # Purple subtitle: "\033[1;90m", # Medium Gray error: "\033[1;31m", # Red success: "\033[1;32m" # Green }
Class Method Summary
Instance Attribute Summary
- #results readonly
-
#success? ⇒ Boolean
readonly
Returns true if all steps were successful.
- #fail_fast? ⇒ Boolean readonly Internal use only
- #multiple_results? ⇒ Boolean readonly Internal use only
Instance Method Summary
-
#echo(text, type:)
Echo text to the terminal in the color corresponding to the type of the text.
-
#failure(title, subtitle = nil)
Display an error heading with the title and optional subtitle to reflect that the run failed.
-
#heading(heading, subtitle = nil, type: :banner, padding: true)
Display a colorized heading followed by an optional subtitle.
-
#step(title, *command)
Declare a step with a title and a command.
- #colorize(text, type) private
- #timing private
- #failures Internal use only
- #report(title, &block) Internal use only
Constructor Details
.new ⇒ ContinuousIntegration
# File 'activesupport/lib/active_support/continuous_integration.rb', line 66
def initialize @results = [] end
Class Method Details
.run(title = "Continuous Integration", subtitle = "Running tests, style checks, and security audits", &block)
Perform a CI run. Execute each step, show their results and runtime, and exit with a non-zero status if there are any failures.
Pass an optional title, subtitle, and a block that declares the steps to be executed.
Sets the CI environment variable to “true” to allow for conditional behavior in the app, like enabling eager loading and disabling logging.
A ‘fail fast’ option can be passed as a CLI argument (-f or –fail-fast). This exits with a non-zero status directly after a step fails.
Example:
ActiveSupport::ContinuousIntegration.run do
step "Setup", "bin/setup --skip-server"
step "Style: Ruby", "bin/rubocop"
step "Security: Gem audit", "bin/bundler-audit"
step "Tests: Rails", "bin/rails test test:system"
if success?
step "Signoff: Ready for merge and deploy", "gh signoff"
else
failure "Skipping signoff; CI failed.", "Fix the issues and try again."
end
end
# File 'activesupport/lib/active_support/continuous_integration.rb', line 57
def self.run(title = "Continuous Integration", subtitle = "Running tests, style checks, and security audits", &block) new.tap do |ci| ENV["CI"] = "true" ci.heading title, subtitle, padding: false ci.report(title, &block) abort unless ci.success? end end
Instance Attribute Details
#fail_fast? ⇒ Boolean (readonly)
# File 'activesupport/lib/active_support/continuous_integration.rb', line 156
def fail_fast? ARGV.include?("-f") || ARGV.include?("--fail-fast") end
#multiple_results? ⇒ Boolean (readonly)
# File 'activesupport/lib/active_support/continuous_integration.rb', line 151
def multiple_results? results.size > 1 end
#results (readonly)
[ GitHub ]# File 'activesupport/lib/active_support/continuous_integration.rb', line 33
attr_reader :results
#success? ⇒ Boolean (readonly)
Returns true if all steps were successful.
# File 'activesupport/lib/active_support/continuous_integration.rb', line 83
def success? results.map(&:first).all? end
Instance Method Details
#colorize(text, type) (private)
[ GitHub ]# File 'activesupport/lib/active_support/continuous_integration.rb', line 168
def colorize(text, type) "#{COLORS.fetch(type)}#{text}\033[0m" end
#echo(text, type:)
Echo text to the terminal in the color corresponding to the type of the text.
Examples:
echo "This is going to be green!", type: :success
echo "This is going to be red!", type: :error
See COLORS for a complete list of options.
# File 'activesupport/lib/active_support/continuous_integration.rb', line 113
def echo(text, type:) puts colorize(text, type) end
#failure(title, subtitle = nil)
Display an error heading with the title and optional subtitle to reflect that the run failed.
# File 'activesupport/lib/active_support/continuous_integration.rb', line 88
def failure(title, subtitle = nil) heading title, subtitle, type: :error end
#failures
# File 'activesupport/lib/active_support/continuous_integration.rb', line 146
def failures results.reject(&:first) end
#heading(heading, subtitle = nil, type: :banner, padding: true)
Display a colorized heading followed by an optional subtitle.
Examples:
heading "Smoke Testing", "End-to-end tests verifying key functionality", padding: false
heading "Skipping video encoding tests", "Install FFmpeg to run these tests", type: :error
See COLORS for a complete list of options.
#report(title, &block)
# File 'activesupport/lib/active_support/continuous_integration.rb', line 118
def report(title, &block) Signal.trap("INT") { abort colorize("\n❌ #{title} interrupted", :error) } ci = self.class.new elapsed = timing { ci.instance_eval(&block) } if ci.success? echo "\n✅ #{title} passed in #{elapsed}", type: :success else echo "\n❌ #{title} failed in #{elapsed}", type: :error abort if ci.fail_fast? if ci.multiple_results? ci.failures.each do |success, title| unless success echo " ↳ #{title} failed", type: :error end end end end results.concat ci.results ensure Signal.trap("INT", "-") end
#step(title, *command)
Declare a step with a title and a command. The command can either be given as a single string or as multiple strings that will be passed to system as individual arguments (and therefore correctly escaped for paths etc).
Examples:
step "Setup", "bin/setup"
step "Single test", "bin/rails", "test", "--name", "test_that_is_one"