Module: Rails::Hyperdrive::AutoInstall

Defined in:
lib/rails/hyperdrive/auto_install.rb

Overview

Requires no booted Rails and never raises — every failure comes back as a skipped result.

Defined Under Namespace

Classes: Result

Constant Summary collapse

DEVELOPMENT =
"development".freeze

Class Method Summary collapse

Class Method Details

.current_envObject



97
98
99
# File 'lib/rails/hyperdrive/auto_install.rb', line 97

def current_env
  ENV["RAILS_ENV"] || ENV["RACK_ENV"] || DEVELOPMENT
end

.development?(env) ⇒ Boolean

Rails may never boot in this process, and this guard fronts a write into the working tree, which a deploy or a CI container must never have modified underneath it.

Returns:

  • (Boolean)


91
92
93
94
95
# File 'lib/rails/hyperdrive/auto_install.rb', line 91

def development?(env)
  return false unless env.to_s == DEVELOPMENT
  return false if ENV["CI"] && !ENV["CI"].empty?
  !frozen_bundle?
end

.frozen_bundle?Boolean

Returns:

  • (Boolean)


101
102
103
104
105
# File 'lib/rails/hyperdrive/auto_install.rb', line 101

def frozen_bundle?
  ::Bundler.respond_to?(:frozen_bundle?) && ::Bundler.frozen_bundle?
rescue StandardError
  false
end

.halted(reason) ⇒ Object

A completed run that installed nothing and has a reason to print.



113
114
115
# File 'lib/rails/hyperdrive/auto_install.rb', line 113

def halted(reason)
  Result.new(installed: [], outdated: [], orphaned: [], advisories: [], fence_warnings: [], halted: reason)
end

.run(root: Dir.pwd, env: current_env, io: nil) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/rails/hyperdrive/auto_install.rb', line 51

def run(root: Dir.pwd, env: current_env, io: nil)
  root = File.expand_path(root.to_s)

  return skip(:not_development) unless development?(env)
  return skip(:not_initialized) unless File.exist?(File.join(root, InstallLayout::LOCK_PATH))

  lock = LockFile.load(File.join(root, InstallLayout::LOCK_PATH))
  return halted(lock.schema_ahead_message(InstallLayout::LOCK_PATH)) if lock.schema_ahead?

  report = BundlerArtifactDiscovery::Report.new
  artifacts = BundlerArtifactDiscovery.discover(enabled_gems: lock.enabled_gems, report: report)
  status = ArtifactStatus.compare(root: root, artifacts: artifacts, bundled_gems: report.bundled_gems)

  installed = []
  unwired = false

  unless status.missing.empty?
    pipeline = InstallPipeline.new(
      root: root,
      shell: InstallShell.new(root: root, io: io),
      artifacts: artifacts,
      mode: :additive,
      report: report
    )
    installed = pipeline.call.installed
    # This runs from a bundle install, which must not edit CLAUDE.md, so a
    # guideline landing in an app with no import line stays out of context
    # until the user runs a sync.
    unwired = pipeline.lock.claude_md_state.nil? && (installed & pipeline.lock.guideline_paths).any?
  end

  Result.new(installed: installed, outdated: status.outdated, orphaned: status.orphaned,
    unwired: unwired, advisories: report.advisories, fence_warnings: report.fence_warnings)
rescue StandardError => e
  skip(:error, e)
end

.skip(reason, error = nil) ⇒ Object



107
108
109
110
# File 'lib/rails/hyperdrive/auto_install.rb', line 107

def skip(reason, error = nil)
  Result.new(installed: [], outdated: [], orphaned: [], advisories: [], fence_warnings: [],
    skipped: reason, error: error)
end