Module: RepoIntrospection
- Defined in:
- lib/core/repo_introspection.rb
Constant Summary collapse
- DEFAULT_APP_PREFIX =
"my-app"- RUBY_VERSION_DIRECTIVE_PATTERN =
/^\s*ruby\s+['"\d]/- RUBY_VERSION_DIRECTIVE_PREFIX =
/^\s*ruby\s+/
Class Method Summary collapse
- .database_connection_config?(config) ⇒ Boolean
-
.direct_sqlite_database_config?(config) ⇒ Boolean
Returns true/false when a direct ‘url` or `adapter` key is conclusive, or nil when neither key is present so the caller can check nested sub-configs.
-
.inferred_app_prefix(root) ⇒ Object
Returns a Control Plane-safe app prefix derived from the basename of ‘root`: lower-cased, with non-alphanumeric runs collapsed to dashes and stripped from the ends.
-
.inferred_ruby_version_string(root) ⇒ Object
Returns the first Ruby version string the repo declares, checked in the order Bundler itself uses: ‘.ruby-version`, then `.tool-versions`, then `Gemfile`.
-
.nested_sqlite_database_config?(config) ⇒ Boolean
rubocop:enable Style/ReturnNilInPredicateMethodDefinition.
-
.parse_ruby_version_string(source) ⇒ Object
Pure string → version-string extractor.
- .ruby_version_from_gemfile(root) ⇒ Object
- .ruby_version_from_ruby_version_file(root) ⇒ Object
- .ruby_version_from_tool_versions(root) ⇒ Object
- .safe_load_database_yml(raw_contents) ⇒ Object
- .sqlite_adapter_in_hash?(config) ⇒ Boolean
-
.sqlite_database_config?(config) ⇒ Boolean
Determines whether a database config hash uses SQLite.
-
.sqlite_database_in_production?(root) ⇒ Boolean
Returns true if ‘config/database.yml` under `root` configures SQLite for production.
- .sqlite_database_url?(url) ⇒ Boolean
- .warn_dynamic_ruby_directive ⇒ Object
Class Method Details
.database_connection_config?(config) ⇒ Boolean
127 128 129 |
# File 'lib/core/repo_introspection.rb', line 127 def self.database_connection_config?(config) config.is_a?(Hash) && (config.key?("adapter") || config.key?("url")) end |
.direct_sqlite_database_config?(config) ⇒ Boolean
Returns true/false when a direct ‘url` or `adapter` key is conclusive, or nil when neither key is present so the caller can check nested sub-configs. rubocop:disable Style/ReturnNilInPredicateMethodDefinition – ternary nil/true/false is load-bearing for the caller
107 108 109 110 111 112 113 114 |
# File 'lib/core/repo_introspection.rb', line 107 def self.direct_sqlite_database_config?(config) url = config["url"] return sqlite_database_url?(url) if url.is_a?(String) && !url.strip.empty? return sqlite_adapter_in_hash?(config) if config["adapter"].is_a?(String) nil end |
.inferred_app_prefix(root) ⇒ Object
Returns a Control Plane-safe app prefix derived from the basename of ‘root`: lower-cased, with non-alphanumeric runs collapsed to dashes and stripped from the ends. Falls back to DEFAULT_APP_PREFIX when the result is empty.
65 66 67 68 69 70 71 72 |
# File 'lib/core/repo_introspection.rb', line 65 def self.inferred_app_prefix(root) sanitized = File.basename(root) .downcase .gsub(/[^a-z0-9]+/, "-") .gsub(/\A-+|-+\z/, "") sanitized.empty? ? DEFAULT_APP_PREFIX : sanitized end |
.inferred_ruby_version_string(root) ⇒ Object
Returns the first Ruby version string the repo declares, checked in the order Bundler itself uses: ‘.ruby-version`, then `.tool-versions`, then `Gemfile`. Returns nil when no source declares a version. Both `Command::Generator` and `GithubFlowReadinessService` call into this so a future format change (e.g. `.tool-versions`) only updates here.
21 22 23 24 25 |
# File 'lib/core/repo_introspection.rb', line 21 def self.inferred_ruby_version_string(root) ruby_version_from_ruby_version_file(root) || ruby_version_from_tool_versions(root) || ruby_version_from_gemfile(root) end |
.nested_sqlite_database_config?(config) ⇒ Boolean
rubocop:enable Style/ReturnNilInPredicateMethodDefinition
117 118 119 120 121 122 123 124 125 |
# File 'lib/core/repo_introspection.rb', line 117 def self.nested_sqlite_database_config?(config) # In Rails multi-database configs, hash values with adapter/url keys are named # connections such as primary:, cache:, or queue:. Scalar and incidental hash # settings are ignored. sub_configs = config.values.select { |value| database_connection_config?(value) } return false if sub_configs.empty? sub_configs.all? { |sub| sqlite_database_config?(sub) } end |
.parse_ruby_version_string(source) ⇒ Object
Pure string → version-string extractor. Strips a leading ‘ruby-` prefix and returns the first `MAJOR.MINOR` found in the source, or nil.
12 13 14 15 |
# File 'lib/core/repo_introspection.rb', line 12 def self.parse_ruby_version_string(source) normalized = source.strip.sub(/\Aruby-/, "") normalized[/\d+\.\d+(?:\.\d+)?/] end |
.ruby_version_from_gemfile(root) ⇒ Object
44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/core/repo_introspection.rb', line 44 def self.ruby_version_from_gemfile(root) path = File.join(root, "Gemfile") return unless File.file?(path) ruby_lines = File.readlines(path, chomp: true).grep(RUBY_VERSION_DIRECTIVE_PREFIX) ruby_line = ruby_lines.find { |line| line.match?(RUBY_VERSION_DIRECTIVE_PATTERN) } warn_dynamic_ruby_directive if ruby_lines.any? && ruby_line.nil? return unless ruby_line parse_ruby_version_string(ruby_line.sub(RUBY_VERSION_DIRECTIVE_PREFIX, "")) end |
.ruby_version_from_ruby_version_file(root) ⇒ Object
27 28 29 30 31 32 |
# File 'lib/core/repo_introspection.rb', line 27 def self.ruby_version_from_ruby_version_file(root) path = File.join(root, ".ruby-version") return unless File.file?(path) parse_ruby_version_string(File.read(path)) end |
.ruby_version_from_tool_versions(root) ⇒ Object
34 35 36 37 38 39 40 41 42 |
# File 'lib/core/repo_introspection.rb', line 34 def self.ruby_version_from_tool_versions(root) path = File.join(root, ".tool-versions") return unless File.file?(path) ruby_line = File.readlines(path, chomp: true).find { |line| line.match?(RUBY_VERSION_DIRECTIVE_PREFIX) } return unless ruby_line parse_ruby_version_string(ruby_line.sub(RUBY_VERSION_DIRECTIVE_PREFIX, "")) end |
.safe_load_database_yml(raw_contents) ⇒ Object
131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/core/repo_introspection.rb', line 131 def self.safe_load_database_yml(raw_contents) # ERB conditionals can change YAML structure, so avoid guessing. Output-only # ERB is stubbed as a scalar so common Rails defaults like `pool: <%= ... %>` # still parse, but control-flow ERB returns unknown. `<%- ... %>` is a # whitespace-trimming code tag, not an output tag, so treat it as unknown too. # Callers treat unknown as non-SQLite and # emit the default Postgres scaffold rather than guessing wrong. return nil if raw_contents.match?(/<%(?![=#])/m) stubbed = raw_contents.gsub(/<%=.*?%>/m, "__erb__").gsub(/<%#.*?%>/m, "") YAML.safe_load(stubbed, aliases: true, permitted_classes: [Symbol]) rescue Psych::SyntaxError nil end |
.sqlite_adapter_in_hash?(config) ⇒ Boolean
146 147 148 149 150 151 |
# File 'lib/core/repo_introspection.rb', line 146 def self.sqlite_adapter_in_hash?(config) return false unless config.is_a?(Hash) adapter = config["adapter"] adapter.is_a?(String) && adapter.strip.start_with?("sqlite3") end |
.sqlite_database_config?(config) ⇒ Boolean
Determines whether a database config hash uses SQLite. Handles both the single-database shape (top-level ‘adapter`/`url`) and Rails 6.1+ multi-database shape where each connection sits one level deeper (`primary:`, `cache:`, etc.). Returns false on any explicit non-SQLite adapter so a mixed config (e.g. Postgres primary + SQLite cache) keeps the Postgres scaffold rather than a volume scaffold.
95 96 97 98 99 100 101 102 |
# File 'lib/core/repo_introspection.rb', line 95 def self.sqlite_database_config?(config) return false unless config.is_a?(Hash) direct_result = direct_sqlite_database_config?(config) return direct_result unless direct_result.nil? nested_sqlite_database_config?(config) end |
.sqlite_database_in_production?(root) ⇒ Boolean
Returns true if ‘config/database.yml` under `root` configures SQLite for production. YAML merge keys such as `<<: *default` are resolved by safe_load, so only the final production hash should be inspected.
77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/core/repo_introspection.rb', line 77 def self.sqlite_database_in_production?(root) path = File.join(root, "config/database.yml") return false unless File.file?(path) parsed = safe_load_database_yml(File.read(path)) return false unless parsed.is_a?(Hash) production = parsed["production"] return false unless production.is_a?(Hash) sqlite_database_config?(production) end |
.sqlite_database_url?(url) ⇒ Boolean
153 154 155 |
# File 'lib/core/repo_introspection.rb', line 153 def self.sqlite_database_url?(url) url.strip.downcase.start_with?("sqlite:", "sqlite3:") end |
.warn_dynamic_ruby_directive ⇒ Object
56 57 58 59 60 |
# File 'lib/core/repo_introspection.rb', line 56 def self.warn_dynamic_ruby_directive return unless ENV["CPFLOW_DEBUG"] warn "cpflow: Gemfile has a dynamic `ruby` directive; falling back to the default Ruby version" end |