Skip to content

Commit

Permalink
Implement partial support for multiple runtime managers
Browse files Browse the repository at this point in the history
  • Loading branch information
maxfierke committed Feb 13, 2024
1 parent 7cb86b6 commit 4b5610f
Show file tree
Hide file tree
Showing 18 changed files with 126 additions and 29 deletions.
6 changes: 3 additions & 3 deletions src/mstrap/bootstrappers/default_bootstrapper.cr
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module MStrap
def bootstrap(project : Project) : Bool
logd "Bootstrapping '#{project.name}' with runtime defaults."

runtime_impls(project).each do |runtime|
runtime_impls(project).each_value do |runtime|
Dir.cd(project.path) do
if runtime.matches?
logd "Detected #{runtime.language_name}. Installing #{runtime.language_name}, project #{runtime.language_name} packages, and other relevant dependencies"
Expand All @@ -23,9 +23,9 @@ module MStrap

def runtime_impls(project)
if project.runtimes.empty?
config.runtime_manager.runtimes
config.runtimes
else
config.runtime_manager.runtimes.select do |runtime|
config.runtimes.select do |_, runtime|
project.runtimes.includes?(runtime.language_name)
end
end
Expand Down
22 changes: 18 additions & 4 deletions src/mstrap/configuration.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ module MStrap
include DSL

@config_def : Defs::ConfigDef
@default_runtime_manager : RuntimeManager
@loaded_profile_configs : Array(Defs::ProfileConfigDef)
@loaded_profiles : Array(Defs::ProfileDef)
@known_profile_configs : Array(Defs::ProfileConfigDef)
@resolved_profile : Defs::ProfileDef
@runtime_manager : RuntimeManager
@runtime_managers : Array(RuntimeManager)
@runtimes : Hash(String, Runtime)
@user : User

DEFAULT_PROFILE_CONFIG_DEF = Defs::DefaultProfileConfigDef.new
Expand All @@ -28,8 +30,18 @@ module MStrap
# profiles with the default profiles.
getter :resolved_profile

# Returns the runtime manager specified by the configuration
getter :runtime_manager
# Returns the default runtime manager specified by the configuration
getter :default_runtime_manager

# Returns the runtime managers specified by the configuration
getter :runtime_managers

# Returns the language runtimes with their resolved runtime manager
#
# Raises UnsupportedLanguageRuntimeManagerError if the configuration of a
# language runtime to a runtime manager is invalid
# Raises InvalidRuntimeManagerError if an invalid runtime manager is provided
getter :runtimes

# Returns the mstrap user
getter :user
Expand All @@ -40,11 +52,13 @@ module MStrap
)
@config_def = config
@config_path = config_path
@default_runtime_manager = RuntimeManager.for(config.runtimes.default_manager)
@loaded_profile_configs = [] of Defs::ProfileConfigDef
@loaded_profiles = [] of Defs::ProfileDef
@known_profile_configs = config.profiles + [DEFAULT_PROFILE_CONFIG_DEF]
@resolved_profile = Defs::ProfileDef.new
@runtime_manager = RuntimeManager.for(config.runtimes.default_manager)
@runtime_managers = RuntimeManager.resolve_managers(config)
@runtimes = RuntimeManager.resolve_runtimes(config)
@user = User.new(user: config.user)
end

Expand Down
15 changes: 15 additions & 0 deletions src/mstrap/defs/runtime_config_def.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module MStrap
module Defs
class RuntimeConfigDef
include HCL::Serializable

@[HCL::Label]
property name : String

@[HCL::Attribute]
property manager : String? = nil

def_equals_and_hash @name, @manager
end
end
end
5 changes: 4 additions & 1 deletion src/mstrap/defs/runtimes_config_def.cr
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ module MStrap
@[HCL::Attribute]
property default_manager = "asdf"

def_equals_and_hash @default_manager
@[HCL::Block(key: "runtime")]
property runtimes = [] of ::MStrap::Defs::RuntimeConfigDef

def_equals_and_hash @default_manager, @runtimes

def initialize
end
Expand Down
6 changes: 6 additions & 0 deletions src/mstrap/errors.cr
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ module MStrap
end
end

class UnsupportedLanguageRuntimeManagerError < MStrapError
def initialize(manager_name, language_name)
super("#{manager_name} does not support the language provided: #{language_name}")
end
end

# Exception class to indicate a failure involving language runtime setup
class RuntimeSetupError < MStrapError
def initialize(language_name, message)
Expand Down
47 changes: 41 additions & 6 deletions src/mstrap/runtime_manager.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ module MStrap
abstract class RuntimeManager
include DSL

@runtimes : Array(Runtime)?

def name : String
{{ @type.name.stringify.split("::").last.downcase }}
end
Expand All @@ -28,6 +30,7 @@ module MStrap
abstract def set_version(language_name : String, version : String?) : Bool
abstract def set_global_version(language_name : String, version : String) : Bool
abstract def shell_activation(shell_name : String) : String
abstract def supported_languages : Array(String)

macro finished
# :nodoc:
Expand All @@ -40,12 +43,44 @@ module MStrap
end

# :nodoc:
def runtimes
@runtimes ||= [
{% for subclass in Runtime.subclasses %}
{{ subclass.name }}.new(self),
{% end %}
]
def self.resolve_managers(config_def : Defs::ConfigDef) : Array(RuntimeManager)
default_runtime_manager = self.for(config_def.runtimes.default_manager)
managers = [default_runtime_manager]

config_def.runtimes.runtimes.map(&.manager).uniq!.each do |manager_name|
next if !manager_name
managers << RuntimeManager.for(manager_name)
end

managers
end

# :nodoc:
def self.resolve_runtimes(config_def : Defs::ConfigDef) : Hash(String, Runtime)
impls = Hash(String, Runtime).new
default_manager = {{ @type }}.all[config_def.runtimes.default_manager]

{% for subclass, index in Runtime.subclasses %}
{% language_name = subclass.name.stringify.split("::").last.downcase %}

%runtime_def{index} = config_def.runtimes.runtimes.find { |r| r.name == {{ language_name }} }

if %runtime_def{index} && (runtime_manager_name = %runtime_def{index}.manager)
runtime_manager = self.for(runtime_manager_name)

if !runtime_manager.supported_languages.includes?({{ language_name }})
raise UnsupportedLanguageRuntimeManagerError.new(runtime_manager.name, {{ language_name }})
end

impls[{{language_name}}] = {{ subclass.name }}.new(runtime_manager)
elsif default_manager.supported_languages.includes?({{ language_name }})
impls[{{language_name}}] = {{ subclass.name }}.new(default_manager)
else
raise UnsupportedLanguageRuntimeManagerError.new(default_manager.name, {{ language_name }})
end
{% end %}

impls
end
end
end
Expand Down
4 changes: 4 additions & 0 deletions src/mstrap/runtime_managers/asdf.cr
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ module MStrap
SHELL
end

def supported_languages : Array(String)
%w(crystal go node php python ruby rust)
end

private def version_env_var(language_name) : String
if asdf_plugin_name = plugin_name(language_name)
"ASDF_#{asdf_plugin_name.upcase}_VERSION"
Expand Down
4 changes: 4 additions & 0 deletions src/mstrap/runtime_managers/mise.cr
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ module MStrap
fi
SHELL
end

def supported_languages : Array(String)
%w(crystal go node php python ruby rust)
end
end
end
end
14 changes: 10 additions & 4 deletions src/mstrap/step.cr
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ module MStrap
@docker : Docker? = nil
# BUG?: Why aren't these inferred correctly?
@profile : Defs::ProfileDef
@runtime_manager : RuntimeManager
@runtime_managers : Array(RuntimeManager)
@runtimes : Hash(String, Runtime)
@user : User

# Extra arguments passed to the step not processed by the main CLI
Expand All @@ -21,8 +22,11 @@ module MStrap
# Resolved profile for mstrap
getter :profile

# Language runtime manager for mstrap
getter :runtime_manager
# Language runtime managers in use
getter :runtime_managers

# Language runtimes
getter :runtimes

# User configured for mstrap
getter :user
Expand All @@ -33,8 +37,10 @@ module MStrap
@args = args
@config = config
@options = cli_options

@profile = config.resolved_profile
@runtime_manager = config.runtime_manager
@runtime_managers = config.runtime_managers
@runtimes = config.runtimes
@user = config.user
end

Expand Down
10 changes: 9 additions & 1 deletion src/mstrap/steps/debug_step.cr
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,15 @@ module MStrap
puts "Loaded Config:"
puts " #{options.config_path}"
puts "Default runtime manager:"
puts " #{runtime_manager.name}"
puts " #{config.default_runtime_manager.name}"
puts "Resolved runtime managers:"
config.runtime_managers.each do |runtime_manager|
puts " #{runtime_manager.name}"
end
puts "Resolved runtimes:"
config.runtimes.each do |runtime_name, runtime|
puts " #{runtime_name} (via #{runtime.runtime_manager.name})"
end
puts "Known Profiles:"
config.known_profile_configs.each do |profile|
puts " #{profile.name}"
Expand Down
2 changes: 1 addition & 1 deletion src/mstrap/steps/dependencies_step.cr
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ module MStrap
end

def bootstrap
install_mise if runtime_manager.name == "mise"
install_mise if config.default_runtime_manager.name == "mise"
set_strap_env!
strap_sh
load_profile!
Expand Down
2 changes: 1 addition & 1 deletion src/mstrap/steps/init_step.cr
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ module MStrap
if !File.exists?(Paths::BREWFILE) || force?
logw "No Brewfile found or update requested with --force"
log "--> Copying default Brewfile to #{Paths::BREWFILE}: "
brewfile_contents = Templates::Brewfile.new(runtime_manager).to_s
brewfile_contents = Templates::Brewfile.new(config.default_runtime_manager).to_s
File.write(Paths::BREWFILE, brewfile_contents)
success "OK"
end
Expand Down
4 changes: 2 additions & 2 deletions src/mstrap/steps/runtimes_step.cr
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module MStrap
end

def bootstrap
runtime_manager.runtimes.each do |runtime|
runtimes.each_value do |runtime|
if runtime.has_runtime_plugin? && runtime.has_versions?
logn "==> Setting global #{runtime.language_name} settings"
set_default_to_latest(runtime)
Expand Down Expand Up @@ -49,7 +49,7 @@ module MStrap
return unless latest_version

log "--> Setting default #{runtime.language_name} version to #{latest_version}: "
unless runtime_manager.set_global_version(runtime.language_name, latest_version)
unless runtime.runtime_manager.set_global_version(runtime.language_name, latest_version)
logc "Could not set global #{runtime.language_name} version to #{latest_version}"
end
success "OK"
Expand Down
2 changes: 1 addition & 1 deletion src/mstrap/steps/shell_step.cr
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ module MStrap
def bootstrap
Dir.mkdir_p(MStrap::Paths::RC_DIR)

contents = Templates::EnvSh.new(shell_name, runtime_manager).to_s
contents = Templates::EnvSh.new(shell_name, runtime_managers).to_s
File.write(env_sh_path, contents, perm: 0o600)

exit_if_shell_changed!
Expand Down
4 changes: 2 additions & 2 deletions src/mstrap/templates/Brewfile.cr
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ module MStrap
class Brewfile
ECR.def_to_s "#{__DIR__}/Brewfile.ecr"

getter :runtime_manager
getter :default_runtime_manager

def initialize(@runtime_manager : RuntimeManager)
def initialize(@default_runtime_manager : RuntimeManager)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion src/mstrap/templates/Brewfile.ecr
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ brew 'pkg-config'
brew 'zlib'

# Language runtime managers
<% if runtime_manager.name == "asdf" %>
<% if default_runtime_manager.name == "asdf" %>
brew 'asdf'
<% end %>

Expand Down
2 changes: 2 additions & 0 deletions src/mstrap/templates/env.sh.ecr
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ export MSTRAP_RC_DIR="<%= MStrap::Paths::RC_DIR %>"
test -d <%= MStrap::Paths::HOMEBREW_PREFIX %> && eval $(<%= MStrap::Paths::HOMEBREW_PREFIX %>/bin/brew shellenv)
<% end -%>

<%- runtime_managers.each do |runtime_manager| %>
<%= runtime_manager.shell_activation(shell_name) %>
<% end -%>
4 changes: 2 additions & 2 deletions src/mstrap/templates/env_sh.cr
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ module MStrap
ECR.def_to_s "#{__DIR__}/env.sh.ecr"

getter :shell_name
getter :runtime_manager
getter :runtime_managers

def initialize(@shell_name : String, @runtime_manager : RuntimeManager)
def initialize(@shell_name : String, @runtime_managers : Array(RuntimeManager))
end

def needs_homebrew_shellenv?
Expand Down

0 comments on commit 4b5610f

Please sign in to comment.