Repository: rails/rails
File: activesupport/lib/active_support/continuous_integration.rb (Ruby)

Code before:
module ActiveSupport
  class ContinuousIntegration
    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
    }

    def self.run(ci_declaration_path)
      new.instance_eval(ci_declaration_path.read)
    end

    def initialize
    end

    def step(title, command)
      echo :title, "\n\n#{title}"
      echo :subtitle, "#{command}\n"

      report(title) { system command }
    end

    def report(title)
      Signal.trap("INT") { abort message(:error, "\n\n❌ #{title} interrupted") }

      result = nil
      elapsed = timing { result = yield }

      if result
        echo :success, "\n\n✅ #{title} passed in #{elapsed}"
        true
      else
        echo :error, "\n\n❌ #{title} failed in #{elapsed}"
        false
      end
    ensure
      Signal.trap("INT", "-")
    end

    def echo(type, text)
      puts message(type, text)
    end

    private
      def timing

Review comment (suggestion):
```suggestion
      report(title) { system { "CI" => "true" }, command }
```

If the goal is to behave like a CI, setting this env variable is preferable as some tools will behave differently, including Rails' default environment:  https://github.com/rails/rails/blob/c218fb6cd2bdd67771aac5bfce8097e43c05a26c/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt#L16

Code after:
# frozen_string_literal: true

module ActiveSupport
  # 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.
  class ContinuousIntegration
    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
    }

    attr_reader :results

    # 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"