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.

Methods

Constants

COLORS = { 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 }

Attributes

[R] results

Class Public methods

new()

πŸ“ Source code
# File activesupport/lib/active_support/continuous_integration.rb, line 64
    def initialize
      @results = []
    end
πŸ”Ž See on GitHub

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.

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
πŸ“ Source code
# File activesupport/lib/active_support/continuous_integration.rb, line 55
    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
πŸ”Ž See on GitHub

Instance Public methods

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 ActiveSupport::ContinuousIntegration::COLORS for a complete list of options.

πŸ“ Source code
# File activesupport/lib/active_support/continuous_integration.rb, line 111
    def echo(text, type:)
      puts colorize(text, type)
    end
πŸ”Ž See on GitHub

failure(title, subtitle = nil)

Display an error heading with the title and optional subtitle to reflect that the run failed.

πŸ“ Source code
# File activesupport/lib/active_support/continuous_integration.rb, line 86
    def failure(title, subtitle = nil)
      heading title, subtitle, type: :error
    end
πŸ”Ž See on GitHub

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 ActiveSupport::ContinuousIntegration::COLORS for a complete list of options.

πŸ“ Source code
# File activesupport/lib/active_support/continuous_integration.rb, line 98
    def heading(heading, subtitle = nil, type: :banner, padding: true)
      echo "#{padding ? "\n\n" : ""}#{heading}", type: type
      echo "#{subtitle}#{padding ? "\n" : ""}", type: :subtitle if subtitle
    end
πŸ”Ž See on GitHub

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"
πŸ“ Source code
# File activesupport/lib/active_support/continuous_integration.rb, line 75
    def step(title, *command)
      heading title, command.join(" "), type: :title
      report(title) { results << system(*command) }
    end
πŸ”Ž See on GitHub

success?()

Returns true if all steps were successful.

πŸ“ Source code
# File activesupport/lib/active_support/continuous_integration.rb, line 81
    def success?
      results.all?
    end
πŸ”Ž See on GitHub