From 6daac19dc7953b7df29926cf6bd06c25ae911309 Mon Sep 17 00:00:00 2001 From: Max Fierke Date: Tue, 23 Jan 2024 22:57:31 -0600 Subject: [PATCH 01/11] Add RuntimeManager abstraction to separate asdf from Runtime implementations --- src/mstrap.cr | 2 + src/mstrap/cli.cr | 2 +- src/mstrap/configuration.cr | 5 ++ src/mstrap/defs/config_def.cr | 5 +- src/mstrap/errors.cr | 6 ++ src/mstrap/project.cr | 12 +-- src/mstrap/runtime.cr | 99 ++++++------------------- src/mstrap/runtime_manager.cr | 49 +++++++++++++ src/mstrap/runtime_managers/asdf.cr | 110 ++++++++++++++++++++++++++++ src/mstrap/runtimes/crystal.cr | 3 +- src/mstrap/runtimes/go.cr | 9 +-- src/mstrap/runtimes/node.cr | 11 +-- src/mstrap/runtimes/php.cr | 5 +- src/mstrap/runtimes/python.cr | 5 +- src/mstrap/runtimes/ruby.cr | 10 +-- src/mstrap/runtimes/rust.cr | 5 +- src/mstrap/step.cr | 5 ++ src/mstrap/steps/projects_step.cr | 2 +- src/mstrap/steps/runtimes_step.cr | 8 +- 19 files changed, 241 insertions(+), 112 deletions(-) create mode 100644 src/mstrap/runtime_manager.cr create mode 100644 src/mstrap/runtime_managers/asdf.cr diff --git a/src/mstrap.cr b/src/mstrap.cr index 8f8bb1d..2b256e8 100644 --- a/src/mstrap.cr +++ b/src/mstrap.cr @@ -26,6 +26,8 @@ require "./mstrap/user" require "./mstrap/configuration" require "./mstrap/supports/**" require "./mstrap/web_bootstrapper" +require "./mstrap/runtime_manager" +require "./mstrap/runtime_managers/**" require "./mstrap/runtime" require "./mstrap/runtimes/**" require "./mstrap/project" diff --git a/src/mstrap/cli.cr b/src/mstrap/cli.cr index b2ff32c..9756ca9 100644 --- a/src/mstrap/cli.cr +++ b/src/mstrap/cli.cr @@ -200,7 +200,7 @@ DESC project_def.runtimes = options.string["runtimes"].split(',') if options.string.has_key?("runtimes") project = MStrap::Project.for(project_def) - project.bootstrap + project.bootstrap(config.runtime_manager) end end diff --git a/src/mstrap/configuration.cr b/src/mstrap/configuration.cr index a93e7a8..fdc22bc 100644 --- a/src/mstrap/configuration.cr +++ b/src/mstrap/configuration.cr @@ -7,6 +7,7 @@ module MStrap @loaded_profiles : Array(Defs::ProfileDef) @known_profile_configs : Array(Defs::ProfileConfigDef) @resolved_profile : Defs::ProfileDef + @runtime_manager : RuntimeManager @user : User DEFAULT_PROFILE_CONFIG_DEF = Defs::DefaultProfileConfigDef.new @@ -27,6 +28,9 @@ module MStrap # profiles with the default profiles. getter :resolved_profile + # Returns the runtime manager specified by the configuration + getter :runtime_manager + # Returns the mstrap user getter :user @@ -40,6 +44,7 @@ module MStrap @loaded_profiles = [] of Defs::ProfileDef @known_profile_configs = config.profiles + [DEFAULT_PROFILE_CONFIG_DEF] @resolved_profile = Defs::ProfileDef.new + @runtime_manager = RuntimeManager.for(config.runtime_manager) @user = User.new(user: config.user) end diff --git a/src/mstrap/defs/config_def.cr b/src/mstrap/defs/config_def.cr index 5411ad1..01cc90c 100644 --- a/src/mstrap/defs/config_def.cr +++ b/src/mstrap/defs/config_def.cr @@ -4,11 +4,14 @@ module MStrap include HCL::Serializable @[HCL::Attribute] - property version = "1.0" + property version = "1.1" @[HCL::Block(key: "profile")] property profiles = [] of ::MStrap::Defs::ProfileConfigDef + @[HCL::Attribute] + property runtime_manager = "asdf" + @[HCL::Block] property user = ::MStrap::Defs::UserDef.new diff --git a/src/mstrap/errors.cr b/src/mstrap/errors.cr index 80d927f..97c463a 100644 --- a/src/mstrap/errors.cr +++ b/src/mstrap/errors.cr @@ -32,6 +32,12 @@ module MStrap end end + class InvalidRuntimeManagerError < MStrapError + def initialize(manager_name) + super("Runtime manager is not recognized or supported by mstrap: #{manager_name}") + end + end + # Exception class to indicate a failure involving language runtime setup class RuntimeSetupError < MStrapError def initialize(language_name, message) diff --git a/src/mstrap/project.cr b/src/mstrap/project.cr index 95ac134..5f11b66 100644 --- a/src/mstrap/project.cr +++ b/src/mstrap/project.cr @@ -168,7 +168,7 @@ module MStrap # Executes `script/bootstrap` and `script/setup` (if either exists and are # configured to run) or executes conventional runtime bootstrapping as # determined by mstrap. - def bootstrap + def bootstrap(runtime_manager : RuntimeManager) if has_scripts? && run_scripts? logd "Found bootstrapping scripts, executing instead of using defaults." begin @@ -183,7 +183,7 @@ module MStrap end else logd "Bootstrapping '#{name}' with runtime defaults." - default_bootstrap + default_bootstrap(runtime_manager) end end @@ -192,12 +192,14 @@ module MStrap # This **does not** run any bootstrapping scripts, and is used mainly for # calling into conventional bootstrapping within a project's # `script/bootstrap` or `script/setup` from `mstrap project`. - protected def default_bootstrap + # + # TODO: Move this somewhere more appropriate + protected def default_bootstrap(runtime_manager : RuntimeManager) runtime_impls = if runtimes.empty? - MStrap::Runtime.all + runtime_manager.runtimes else - MStrap::Runtime.all.select do |runtime| + runtime_manager.runtimes.select do |runtime| runtimes.includes?(runtime.language_name) end end diff --git a/src/mstrap/runtime.cr b/src/mstrap/runtime.cr index 69e58a3..2662808 100644 --- a/src/mstrap/runtime.cr +++ b/src/mstrap/runtime.cr @@ -3,11 +3,15 @@ module MStrap abstract class Runtime include DSL - @version_env_var : String? + getter :runtime_manager + + def initialize(@runtime_manager : RuntimeManager) + end # Execute a command using a specific language runtime version - def asdf_exec(command : String, args : Array(String), runtime_version : String? = nil) + def runtime_exec(command : String, args : Array(String), runtime_version : String? = nil) if runtime_version + version_env_var = runtime_manager.version_env_var(language_name) env = {version_env_var => runtime_version} cmd env, command, args, quiet: true else @@ -15,19 +19,6 @@ module MStrap end end - def asdf_install_plugin - log "--> Adding #{asdf_plugin_name} to asdf for #{language_name} support: " - unless cmd("asdf plugin-add #{asdf_plugin_name}", quiet: true) - logc "There was an error adding the #{asdf_plugin_name} to asdf. Check #{MStrap::Paths::LOG_FILE} or run again with --debug" - end - success "OK" - end - - # Name of the ASDF plugin. Defaults to language_name - def asdf_plugin_name : String - language_name - end - # Bootstrap the current directory for the runtime abstract def bootstrap @@ -36,17 +27,13 @@ module MStrap # # NOTE: This will not traverse parent directories to find versions files. def current_version - [ - version_from_env, - version_from_tool_versions, - version_from_legacy_version_file, - ].find { |version| version } + runtime_manager.current_version(language_name) end # Returns whether the ASDF plugin has been installed for a language runtime # or not - def has_asdf_plugin? - `asdf plugin-list`.chomp.split("\n").includes?(asdf_plugin_name) + def has_runtime_plugin? + runtime_manager.has_plugin?(language_name) end def has_version?(version) @@ -62,13 +49,7 @@ module MStrap # Returns a list of the versions of the language runtime installed # by ASDF. def installed_versions - `asdf list #{asdf_plugin_name} 2>&1` - .chomp - .split("\n") - .map(&.strip.lstrip('*')) - .reject do |version| - version.blank? || version == "No versions installed" - end + runtime_manager.installed_versions(language_name) end # Installs global packages for the runtime with an optional version @@ -82,9 +63,9 @@ module MStrap abstract def language_name : String # Returns the latest version available for the language runtime, according - # to the asdf plugin + # to the runtime manager def latest_version - `asdf latest #{asdf_plugin_name}`.chomp + runtime_manager.latest_version(language_name) end # Returns whether the project uses the runtime @@ -93,12 +74,20 @@ module MStrap # Installs asdf plugin for the language runtime and installs any of the # language runtime dependencies for the project. def setup - asdf_install_plugin unless has_asdf_plugin? + unless runtime_manager.has_plugin?(language_name) + log "--> Installing #{language_name} plugin to #{runtime_manager.name}: " + unless runtime_manager.install_plugin(language_name) + logc "There was an error adding the #{language_name} plugin to #{runtime_manager.name}. Check #{MStrap::Paths::LOG_FILE} or run again with --debug" + end + success "OK" + end with_dir_version(Dir.current) do + current_version = self.current_version + if current_version && current_version != "" && !has_version?(current_version) - log "--> Installing #{language_name} #{current_version} via asdf-#{asdf_plugin_name}: " - unless cmd("asdf install #{asdf_plugin_name} #{current_version}", quiet: true) + log "--> Installing #{language_name} #{current_version} via #{runtime_manager.name}: " + unless runtime_manager.install_version(language_name, current_version) logc "There was an error installing the #{language_name} via asdf. Check #{MStrap::Paths::LOG_FILE} or run again with --debug" end success "OK" @@ -112,6 +101,7 @@ module MStrap # # NOTE: This will not traverse parent directories to find versions files. def with_dir_version(dir, &) + version_env_var = runtime_manager.version_env_var(language_name) env_version = ENV[version_env_var]? begin Dir.cd(dir) do @@ -123,50 +113,9 @@ module MStrap end end - # :nodoc: - def version_env_var - @version_env_var ||= "ASDF_#{asdf_plugin_name.upcase}_VERSION" - end - - # :nodoc: - def version_from_env - ENV[version_env_var]? - end - - # :nodoc: - def version_from_tool_versions - tool_versions_path = File.join(Dir.current, ".tool-versions") - return nil unless File.exists?(tool_versions_path) - - tool_versions = File.read(tool_versions_path).strip - if matches = tool_versions.match(/^#{asdf_plugin_name}\s+([^\s]+)$/m) - matches[1].strip - else - nil - end - end - - # :nodoc: - def version_from_legacy_version_file - version_path = File.join(Dir.current, ".#{language_name}-version") - return nil unless File.exists?(version_path) - File.read(version_path).strip - end - # :nodoc: protected def raise_setup_error!(message) raise RuntimeSetupError.new(language_name, message) end - - macro finished - # :nodoc: - def self.all - @@runtimes ||= [ - {% for subclass in @type.subclasses %} - {{ subclass.name }}.new, - {% end %} - ] - end - end end end diff --git a/src/mstrap/runtime_manager.cr b/src/mstrap/runtime_manager.cr new file mode 100644 index 0000000..43c1c3a --- /dev/null +++ b/src/mstrap/runtime_manager.cr @@ -0,0 +1,49 @@ +module MStrap + abstract class RuntimeManager + include DSL + + abstract def name : String + + def self.for(runtime_manager_name : String) + if manager = all[runtime_manager_name]? + manager + else + raise InvalidRuntimeManagerError.new(runtime_manager_name) + end + end + + abstract def current_version(language_name : String) : String? + + def has_plugin?(language_name : String) : Bool + false + end + + abstract def install_plugin(language_name : String) : Bool + abstract def install_version(language_name : String, version : String) : Bool + abstract def installed_versions(language_name : String) : Array(String) + abstract def latest_version(language_name : String) : String + abstract def plugin_name(language_name : String) : String? + abstract def set_global_version(language_name : String, version : String) : Bool + abstract def version_env_var(language_name : String) : String + + macro finished + # :nodoc: + def self.all + @@runtime_managers ||= { + {% for subclass in @type.subclasses %} + {{subclass.name.stringify.split("::").last.downcase}} => {{ subclass.name }}.new, + {% end %} + } + end + + # :nodoc: + def runtimes + @runtimes ||= [ + {% for subclass in Runtime.subclasses %} + {{ subclass.name }}.new(self), + {% end %} + ] + end + end + end +end diff --git a/src/mstrap/runtime_managers/asdf.cr b/src/mstrap/runtime_managers/asdf.cr new file mode 100644 index 0000000..640aeaf --- /dev/null +++ b/src/mstrap/runtime_managers/asdf.cr @@ -0,0 +1,110 @@ +module MStrap + module RuntimeManagers + class ASDF < RuntimeManager + def asdf? + true + end + + def name : String + "asdf" + end + + def current_version(language_name : String) : String? + [ + version_from_env(language_name), + version_from_tool_versions(language_name), + version_from_legacy_version_file(language_name), + ].find { |version| version } + end + + # Returns whether the ASDF plugin has been installed for a language runtime + # or not + def has_plugin?(language_name : String) : Bool + `asdf plugin-list`.chomp.split("\n").includes?(plugin_name(language_name)) + end + + def install_plugin(language_name : String) : Bool + asdf_plugin_name = plugin_name(language_name) + + if asdf_plugin_name + cmd("asdf plugin-add #{asdf_plugin_name}", quiet: true) + else + logw "Unable to find an ASDF plugin for #{language_name}" + false + end + end + + def install_version(language_name : String, version : String) : Bool + cmd("asdf install #{plugin_name(language_name)} #{version}", quiet: true) + end + + # Returns a list of the versions of the language runtime installed + # by ASDF. + def installed_versions(language_name : String) : Array(String) + `asdf list #{plugin_name(language_name)} 2>&1` + .chomp + .split("\n") + .map(&.strip.lstrip('*')) + .reject do |version| + version.blank? || version == "No versions installed" + end + end + + def latest_version(language_name : String) : String + `asdf latest #{plugin_name(language_name)}`.chomp + end + + # Name of the ASDF plugin for a particular language + def plugin_name(language_name : String) : String? + case language_name + when "go" + "golang" + when "node" + "nodejs" + else + language_name + end + end + + def set_global_version(language_name, version : String) : Bool + cmd "asdf global #{plugin_name(language_name)} #{version}", quiet: true + end + + # :nodoc: + def version_env_var(language_name) : String + if asdf_plugin_name = plugin_name(language_name) + "ASDF_#{asdf_plugin_name.upcase}_VERSION" + else + "ASDF_#{language_name.upcase}_VERSION" + end + end + + # :nodoc: + def version_from_env(language_name) + env_var_name = version_env_var(language_name) + ENV[env_var_name]? + end + + # :nodoc: + def version_from_tool_versions(language_name) + tool_versions_path = File.join(Dir.current, ".tool-versions") + return nil unless File.exists?(tool_versions_path) + + tool_versions = File.read(tool_versions_path).strip + asdf_plugin_name = plugin_name(language_name) + if matches = tool_versions.match(/^#{asdf_plugin_name}\s+([^\s]+)$/m) + matches[1].strip + else + nil + end + end + + # :nodoc: + def version_from_legacy_version_file(language_name) + version_path = File.join(Dir.current, ".#{language_name}-version") + return nil unless File.exists?(version_path) + File.read(version_path).strip + end + end + end +end diff --git a/src/mstrap/runtimes/crystal.cr b/src/mstrap/runtimes/crystal.cr index dc4ca5a..4f3c2ed 100644 --- a/src/mstrap/runtimes/crystal.cr +++ b/src/mstrap/runtimes/crystal.cr @@ -1,7 +1,8 @@ module MStrap module Runtimes # Crystal runtime management implmentation. It contains methods for interacting - # with Crystal via ASDF and bootstrapping a Crystal project based on conventions. + # with Crystal via the chosen runtime manager and bootstrapping a Crystal + # project based on conventions. class Crystal < Runtime class SetupError < RuntimeSetupError def initialize(message) diff --git a/src/mstrap/runtimes/go.cr b/src/mstrap/runtimes/go.cr index 1b6b6d4..719ce9f 100644 --- a/src/mstrap/runtimes/go.cr +++ b/src/mstrap/runtimes/go.cr @@ -1,12 +1,9 @@ module MStrap module Runtimes # Go runtime management implmentation. It contains methods for interacting - # with Go via ASDF and bootstrapping a Go project based on conventions. + # with Go via the chosen runtime manager and bootstrapping a Go project + # based on conventions. class Go < Runtime - def asdf_plugin_name : String - "golang" - end - def language_name : String "go" end @@ -28,7 +25,7 @@ module MStrap end disable_go_modules do - asdf_exec "go", cmd_args, runtime_version: runtime_version + runtime_exec "go", cmd_args, runtime_version: runtime_version end end end diff --git a/src/mstrap/runtimes/node.cr b/src/mstrap/runtimes/node.cr index aa97045..3642c72 100644 --- a/src/mstrap/runtimes/node.cr +++ b/src/mstrap/runtimes/node.cr @@ -1,12 +1,9 @@ module MStrap module Runtimes # Node runtime management implmentation. It contains methods for interacting - # with Node via ASDF and bootstrapping a Node project based on conventions. + # with Node via the chosen runtime manager and bootstrapping a Node project + # based on conventions. class Node < Runtime - def asdf_plugin_name - "nodejs" - end - def language_name : String "node" end @@ -31,7 +28,7 @@ module MStrap end end - skip_reshim { asdf_exec "npm", cmd_args, runtime_version: runtime_version } + skip_reshim { runtime_exec "npm", cmd_args, runtime_version: runtime_version } end def matches? : Bool @@ -49,7 +46,7 @@ module MStrap yield ensure ENV.delete("ASDF_SKIP_RESHIM") - asdf_exec "asdf", ["reshim", "nodejs"] + runtime_exec "asdf", ["reshim", "nodejs"] end end end diff --git a/src/mstrap/runtimes/php.cr b/src/mstrap/runtimes/php.cr index 9f41a81..4e473ef 100644 --- a/src/mstrap/runtimes/php.cr +++ b/src/mstrap/runtimes/php.cr @@ -1,7 +1,8 @@ module MStrap module Runtimes # PHP runtime management implmentation. It contains methods for interacting - # with PHP via ASDF and bootstrapping a PHP project based on conventions. + # with PHP via the chosen runtime manager and bootstrapping a PHP project + # based on conventions. class Php < Runtime def language_name : String "php" @@ -25,7 +26,7 @@ module MStrap end end - cmd "composer", cmd_args, quiet: true + runtime_exec "composer", cmd_args, runtime_version: runtime_version end def matches? : Bool diff --git a/src/mstrap/runtimes/python.cr b/src/mstrap/runtimes/python.cr index e1e7170..2cb7e40 100644 --- a/src/mstrap/runtimes/python.cr +++ b/src/mstrap/runtimes/python.cr @@ -1,7 +1,8 @@ module MStrap module Runtimes # Python runtime management implmentation. It contains methods for interacting - # with Python via ASDF and bootstrapping a Python project based on conventions. + # with Python via the chosen runtime manager and bootstrapping a Python + # project based on conventions. # # TODO: Does not support virtualenv class Python < Runtime @@ -26,7 +27,7 @@ module MStrap end end - asdf_exec "pip", cmd_args, runtime_version: runtime_version + runtime_exec "pip", cmd_args, runtime_version: runtime_version end def matches? : Bool diff --git a/src/mstrap/runtimes/ruby.cr b/src/mstrap/runtimes/ruby.cr index 9fb4aad..640bc84 100644 --- a/src/mstrap/runtimes/ruby.cr +++ b/src/mstrap/runtimes/ruby.cr @@ -1,17 +1,15 @@ module MStrap module Runtimes # Ruby runtime management implmentation. It contains methods for interacting - # with Ruby via ASDF and bootstrapping a Ruby project based on conventions. + # with Ruby via the chosen runtime manager and bootstrapping a Ruby project + # based on conventions. class Ruby < Runtime def language_name : String "ruby" end def bootstrap - if File.exists?("gems.rb") - cmd "gem install bundler", quiet: true - cmd "bundle check || bundle install", quiet: true - elsif File.exists?("Gemfile") + if File.exists?("gems.rb") || File.exists?("Gemfile") cmd "gem install bundler", quiet: true cmd "bundle check || bundle install", quiet: true end @@ -26,7 +24,7 @@ module MStrap cmd_args << version end - asdf_exec "gem", cmd_args, runtime_version: runtime_version + runtime_exec "gem", cmd_args, runtime_version: runtime_version end end diff --git a/src/mstrap/runtimes/rust.cr b/src/mstrap/runtimes/rust.cr index 33577d9..b981f5a 100644 --- a/src/mstrap/runtimes/rust.cr +++ b/src/mstrap/runtimes/rust.cr @@ -1,7 +1,8 @@ module MStrap module Runtimes # Rust runtime management implmentation. It contains methods for interacting - # with Rust via ASDF and bootstrapping a Rust project based on conventions. + # with Rust via the chosen runtime manager and bootstrapping a Rust project + # based on conventions. class Rust < Runtime def language_name : String "rust" @@ -27,7 +28,7 @@ module MStrap cmd_args << version end - asdf_exec "cargo", cmd_args, runtime_version: runtime_version + runtime_exec "cargo", cmd_args, runtime_version: runtime_version end end diff --git a/src/mstrap/step.cr b/src/mstrap/step.cr index f79abab..753c15c 100644 --- a/src/mstrap/step.cr +++ b/src/mstrap/step.cr @@ -6,6 +6,7 @@ module MStrap @docker : Docker? = nil # BUG?: Why aren't these inferred correctly? @profile : Defs::ProfileDef + @runtime_manager : RuntimeManager @user : User # Extra arguments passed to the step not processed by the main CLI @@ -20,6 +21,9 @@ module MStrap # Resolved profile for mstrap getter :profile + # Language runtime manager for mstrap + getter :runtime_manager + # User configured for mstrap getter :user @@ -30,6 +34,7 @@ module MStrap @config = config @options = cli_options @profile = config.resolved_profile + @runtime_manager = config.runtime_manager @user = config.user end diff --git a/src/mstrap/steps/projects_step.cr b/src/mstrap/steps/projects_step.cr index 5ea1553..2611fbc 100644 --- a/src/mstrap/steps/projects_step.cr +++ b/src/mstrap/steps/projects_step.cr @@ -38,7 +38,7 @@ module MStrap end logn "--> Bootstrapping: " - project.bootstrap + project.bootstrap(runtime_manager) success "Finished bootstrapping #{project.name}" end diff --git a/src/mstrap/steps/runtimes_step.cr b/src/mstrap/steps/runtimes_step.cr index e2fcee7..a8f53c4 100644 --- a/src/mstrap/steps/runtimes_step.cr +++ b/src/mstrap/steps/runtimes_step.cr @@ -10,8 +10,8 @@ module MStrap end def bootstrap - MStrap::Runtime.all.each do |runtime| - if runtime.has_asdf_plugin? && runtime.has_versions? + runtime_manager.runtimes.each do |runtime| + if runtime.has_runtime_plugin? && runtime.has_versions? logn "==> Setting global #{runtime.language_name} settings" set_default_to_latest(runtime) packages = runtime_packages(runtime) @@ -46,8 +46,10 @@ module MStrap runtime.installed_versions.last end + return unless latest_version + log "--> Setting default #{runtime.language_name} version to #{latest_version}: " - unless cmd "asdf global #{runtime.asdf_plugin_name} #{latest_version}", quiet: true + unless 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" From 3f91ff052c497b9e142544a609be745aa132721b Mon Sep 17 00:00:00 2001 From: Max Fierke Date: Tue, 23 Jan 2024 23:23:39 -0600 Subject: [PATCH 02/11] Add a RuntimeManager for mise --- src/mstrap/runtime.cr | 2 +- src/mstrap/runtime_managers/asdf.cr | 4 -- src/mstrap/runtime_managers/mise.cr | 62 +++++++++++++++++++++++++++++ src/mstrap/steps/init_step.cr | 2 +- src/mstrap/steps/shell_step.cr | 7 +++- src/mstrap/templates/Brewfile.cr | 5 +++ src/mstrap/templates/Brewfile.ecr | 4 ++ src/mstrap/templates/env.sh.ecr | 12 +++++- src/mstrap/templates/env_sh.cr | 6 +++ 9 files changed, 96 insertions(+), 8 deletions(-) create mode 100644 src/mstrap/runtime_managers/mise.cr diff --git a/src/mstrap/runtime.cr b/src/mstrap/runtime.cr index 2662808..93aa958 100644 --- a/src/mstrap/runtime.cr +++ b/src/mstrap/runtime.cr @@ -88,7 +88,7 @@ module MStrap if current_version && current_version != "" && !has_version?(current_version) log "--> Installing #{language_name} #{current_version} via #{runtime_manager.name}: " unless runtime_manager.install_version(language_name, current_version) - logc "There was an error installing the #{language_name} via asdf. Check #{MStrap::Paths::LOG_FILE} or run again with --debug" + logc "There was an error installing #{language_name} #{current_version} via #{runtime_manager.name}. Check #{MStrap::Paths::LOG_FILE} or run again with --debug" end success "OK" end diff --git a/src/mstrap/runtime_managers/asdf.cr b/src/mstrap/runtime_managers/asdf.cr index 640aeaf..2af6365 100644 --- a/src/mstrap/runtime_managers/asdf.cr +++ b/src/mstrap/runtime_managers/asdf.cr @@ -1,10 +1,6 @@ module MStrap module RuntimeManagers class ASDF < RuntimeManager - def asdf? - true - end - def name : String "asdf" end diff --git a/src/mstrap/runtime_managers/mise.cr b/src/mstrap/runtime_managers/mise.cr new file mode 100644 index 0000000..6d7a919 --- /dev/null +++ b/src/mstrap/runtime_managers/mise.cr @@ -0,0 +1,62 @@ +module MStrap + module RuntimeManagers + class Mise < RuntimeManager + def name : String + "mise" + end + + def current_version(language_name : String) : String? + `mise current #{language_name}`.chomp + end + + # Returns whether the mise plugin has been installed for a language runtime + # or not + def has_plugin?(language_name : String) : Bool + `mise plugins ls --core --user`.chomp.split("\n").includes?(plugin_name(language_name)) + end + + def install_plugin(language_name : String) : Bool + cmd("mise plugins install #{plugin_name(language_name)}", quiet: true) + end + + def install_version(language_name : String, version : String) : Bool + cmd("mise install #{plugin_name(language_name)} #{version}", quiet: true) + end + + # Returns a list of the versions of the language runtime installed + # by mise. + def installed_versions(language_name : String) : Array(String) + mise_json_output = `mise ls -i #{plugin_name(language_name)} --json` + mise_installed = JSON.parse(mise_json_output) + + if installed = mise_installed.as_a? + installed.map { |version| version["version"].as_s } + else + Array(String).new + end + end + + def latest_version(language_name : String) : String + `mise latest #{plugin_name(language_name)}`.chomp + end + + # Name of the mise plugin for a particular language + def plugin_name(language_name : String) : String? + language_name + end + + def set_global_version(language_name, version : String) : Bool + cmd "mise use -g #{plugin_name(language_name)}@#{version}", quiet: true + end + + # :nodoc: + def version_env_var(language_name) : String + if mise_plugin_name = plugin_name(language_name) + "MISE_#{mise_plugin_name.upcase}_VERSION" + else + "MISE_#{language_name.upcase}_VERSION" + end + end + end + end +end diff --git a/src/mstrap/steps/init_step.cr b/src/mstrap/steps/init_step.cr index 2f50002..5c06043 100644 --- a/src/mstrap/steps/init_step.cr +++ b/src/mstrap/steps/init_step.cr @@ -69,7 +69,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.to_s + brewfile_contents = Templates::Brewfile.new(runtime_manager).to_s File.write(Paths::BREWFILE, brewfile_contents) success "OK" end diff --git a/src/mstrap/steps/shell_step.cr b/src/mstrap/steps/shell_step.cr index a11d963..5d4da89 100644 --- a/src/mstrap/steps/shell_step.cr +++ b/src/mstrap/steps/shell_step.cr @@ -26,6 +26,7 @@ module MStrap MSG @login_shell : String? = nil + @shell_name : String? = nil def self.bootstrap(options) new(options).bootstrap @@ -46,7 +47,7 @@ module MStrap def bootstrap Dir.mkdir_p(MStrap::Paths::RC_DIR) - contents = Templates::EnvSh.new.to_s + contents = Templates::EnvSh.new(shell_name, runtime_manager).to_s File.write(env_sh_path, contents, perm: 0o600) exit_if_shell_changed! @@ -92,6 +93,10 @@ module MStrap end end + private def shell_name + @shell_name ||= login_shell.split("/").last + end + private def shell_supported? !shell_file_name.nil? end diff --git a/src/mstrap/templates/Brewfile.cr b/src/mstrap/templates/Brewfile.cr index 37f1e1a..4e21e67 100644 --- a/src/mstrap/templates/Brewfile.cr +++ b/src/mstrap/templates/Brewfile.cr @@ -3,6 +3,11 @@ module MStrap module Templates class Brewfile ECR.def_to_s "#{__DIR__}/Brewfile.ecr" + + getter :runtime_manager + + def initialize(@runtime_manager : RuntimeManager) + end end end end diff --git a/src/mstrap/templates/Brewfile.ecr b/src/mstrap/templates/Brewfile.ecr index 1f11ada..ee883f0 100644 --- a/src/mstrap/templates/Brewfile.ecr +++ b/src/mstrap/templates/Brewfile.ecr @@ -9,7 +9,11 @@ brew 'pkg-config' brew 'zlib' # Language runtime managers +<% if runtime_manager.name == "asdf" %> brew 'asdf' +<% elsif runtime_manager.name == "mise" %> +brew 'mise' +<% end %> if /darwin/ =~ RUBY_PLATFORM brew 'autoconf' diff --git a/src/mstrap/templates/env.sh.ecr b/src/mstrap/templates/env.sh.ecr index 1e8e4c4..89faf3c 100644 --- a/src/mstrap/templates/env.sh.ecr +++ b/src/mstrap/templates/env.sh.ecr @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env <%= shell_name %> export MSTRAP=true export MSTRAP_PROJECT_SOCKETS="<%= MStrap::Paths::PROJECT_SOCKETS %>" @@ -6,9 +6,12 @@ export MSTRAP_SRC_DIR="<%= MStrap::Paths::SRC_DIR %>" export MSTRAP_RC_DIR="<%= MStrap::Paths::RC_DIR %>" <%- if needs_homebrew_shellenv? %> +# Load Homebrew test -d <%= MStrap::Paths::HOMEBREW_PREFIX %> && eval $(<%= MStrap::Paths::HOMEBREW_PREFIX %>/bin/brew shellenv) <% end -%> +<% if runtime_manager.name == "asdf" %> +# Activate asdf for language runtime version management if [ -d "$(brew --prefix asdf)" ]; then source "$(brew --prefix asdf)/libexec/asdf.sh" @@ -16,3 +19,10 @@ if [ -d "$(brew --prefix asdf)" ]; then echo "legacy_version_file = yes\n" > "$HOME/.asdfrc" fi fi +<% elsif runtime_manager.name == "mise" %> +# Activate mise for language runtime version management +if command -v mise 2>&1 > /dev/null; then + export MISE_ASDF_COMPAT=1 + eval "$(mise activate <%= shell_name %>)" +fi +<% end %> diff --git a/src/mstrap/templates/env_sh.cr b/src/mstrap/templates/env_sh.cr index a47fae4..158b16e 100644 --- a/src/mstrap/templates/env_sh.cr +++ b/src/mstrap/templates/env_sh.cr @@ -4,6 +4,12 @@ module MStrap class EnvSh ECR.def_to_s "#{__DIR__}/env.sh.ecr" + getter :shell_name + getter :runtime_manager + + def initialize(@shell_name : String, @runtime_manager : RuntimeManager) + end + def needs_homebrew_shellenv? {{ flag?(:linux) || (flag?(:aarch64) && flag?(:darwin)) }} end From 73755230d4410f40a1dfb2b4d394ce747075399f Mon Sep 17 00:00:00 2001 From: Max Fierke Date: Sat, 27 Jan 2024 18:13:50 -0600 Subject: [PATCH 03/11] Run runtime commands via runtime_exec This is functionally the same as cmd for now in these cases, but might not always be --- src/mstrap/runtime.cr | 2 +- src/mstrap/runtimes/crystal.cr | 2 +- src/mstrap/runtimes/node.cr | 6 +++--- src/mstrap/runtimes/php.cr | 2 +- src/mstrap/runtimes/python.cr | 2 +- src/mstrap/runtimes/rust.cr | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/mstrap/runtime.cr b/src/mstrap/runtime.cr index 93aa958..6071a81 100644 --- a/src/mstrap/runtime.cr +++ b/src/mstrap/runtime.cr @@ -9,7 +9,7 @@ module MStrap end # Execute a command using a specific language runtime version - def runtime_exec(command : String, args : Array(String), runtime_version : String? = nil) + def runtime_exec(command : String, args : Array(String)? = nil, runtime_version : String? = nil) if runtime_version version_env_var = runtime_manager.version_env_var(language_name) env = {version_env_var => runtime_version} diff --git a/src/mstrap/runtimes/crystal.cr b/src/mstrap/runtimes/crystal.cr index 4f3c2ed..4e77a33 100644 --- a/src/mstrap/runtimes/crystal.cr +++ b/src/mstrap/runtimes/crystal.cr @@ -21,7 +21,7 @@ module MStrap def bootstrap if File.exists?("shard.lock") - cmd "shards check || shards install", quiet: true + runtime_exec "shards check || shards install" end end diff --git a/src/mstrap/runtimes/node.cr b/src/mstrap/runtimes/node.cr index 3642c72..dbaa1e2 100644 --- a/src/mstrap/runtimes/node.cr +++ b/src/mstrap/runtimes/node.cr @@ -10,10 +10,10 @@ module MStrap def bootstrap if File.exists?("yarn.lock") - cmd "brew install yarn", quiet: true - skip_reshim { cmd "yarn install", quiet: true } + cmd "brew install yarn", quiet: true && + skip_reshim { runtime_exec "yarn install" } elsif File.exists?("package.json") - skip_reshim { cmd "npm install", quiet: true } + skip_reshim { runtime_exec "npm install" } end end diff --git a/src/mstrap/runtimes/php.cr b/src/mstrap/runtimes/php.cr index 4e473ef..3da82bd 100644 --- a/src/mstrap/runtimes/php.cr +++ b/src/mstrap/runtimes/php.cr @@ -11,7 +11,7 @@ module MStrap def bootstrap if File.exists?("composer.json") cmd "brew install composer", quiet: true - cmd "composer install", quiet: true + runtime_exec "composer install" end end diff --git a/src/mstrap/runtimes/python.cr b/src/mstrap/runtimes/python.cr index 2cb7e40..04456e3 100644 --- a/src/mstrap/runtimes/python.cr +++ b/src/mstrap/runtimes/python.cr @@ -12,7 +12,7 @@ module MStrap def bootstrap if File.exists?("requirements.txt") - cmd "pip install -r requirements.txt", quiet: true + runtime_exec "pip install -r requirements.txt" end end diff --git a/src/mstrap/runtimes/rust.cr b/src/mstrap/runtimes/rust.cr index b981f5a..ba749ae 100644 --- a/src/mstrap/runtimes/rust.cr +++ b/src/mstrap/runtimes/rust.cr @@ -15,7 +15,7 @@ module MStrap def bootstrap if File.exists?("Cargo.toml") - cmd "cargo fetch", quiet: true + runtime_exec "cargo fetch" end end From a523d6c74da11349c6472d7eaafc17634bee256c Mon Sep 17 00:00:00 2001 From: Max Fierke Date: Sat, 27 Jan 2024 18:14:41 -0600 Subject: [PATCH 04/11] rust: fallback to latest version rather than "stable" stable is specific to a particular asdf rust plugin --- src/mstrap/runtimes/rust.cr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mstrap/runtimes/rust.cr b/src/mstrap/runtimes/rust.cr index ba749ae..306f515 100644 --- a/src/mstrap/runtimes/rust.cr +++ b/src/mstrap/runtimes/rust.cr @@ -9,8 +9,8 @@ module MStrap end def current_version - # Falling back to stable is probably fairly safe - super || "stable" + # Falling back to latest is _usually_ safe + super || latest_version end def bootstrap From a4e15605acb7887e98ae11c0ecc97a279e7c84b7 Mon Sep 17 00:00:00 2001 From: Max Fierke Date: Sat, 27 Jan 2024 18:15:46 -0600 Subject: [PATCH 05/11] go: use go install for binary package installation on Go 1.16+ --- src/mstrap/runtimes/go.cr | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/mstrap/runtimes/go.cr b/src/mstrap/runtimes/go.cr index 719ce9f..f88c809 100644 --- a/src/mstrap/runtimes/go.cr +++ b/src/mstrap/runtimes/go.cr @@ -4,19 +4,26 @@ module MStrap # with Go via the chosen runtime manager and bootstrapping a Go project # based on conventions. class Go < Runtime + # :nodoc: + GO_INSTALL_MIN_VERSION = SemanticVersion.new(1, 16, 0) + def language_name : String "go" end def bootstrap if File.exists?("go.mod") - cmd "go mod download", quiet: true + runtime_exec "go mod download" end end def install_packages(packages : Array(Defs::PkgDef), runtime_version : String? = nil) : Bool packages.all? do |pkg| - cmd_args = ["get", "-u"] + cmd_args = if SemanticVersion.parse(runtime_version) >= GO_INSTALL_MIN_VERSION + ["install"] + else + ["get", "-u"] + end if version = pkg.version cmd_args << "#{pkg.name}@#{version}" From 09c398db575b3a41754e01c0d4d6da397179de7d Mon Sep 17 00:00:00 2001 From: Max Fierke Date: Sat, 27 Jan 2024 18:15:59 -0600 Subject: [PATCH 06/11] Skip asdf reshim on mise --- src/mstrap/runtimes/node.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mstrap/runtimes/node.cr b/src/mstrap/runtimes/node.cr index dbaa1e2..220c645 100644 --- a/src/mstrap/runtimes/node.cr +++ b/src/mstrap/runtimes/node.cr @@ -46,7 +46,7 @@ module MStrap yield ensure ENV.delete("ASDF_SKIP_RESHIM") - runtime_exec "asdf", ["reshim", "nodejs"] + runtime_exec "asdf", ["reshim", "nodejs"] if runtime_manager.name == "asdf" end end end From d15944fcf5184e08d6fce12e71f274374f991a96 Mon Sep 17 00:00:00 2001 From: Max Fierke Date: Sat, 27 Jan 2024 18:17:03 -0600 Subject: [PATCH 07/11] ruby: Skip bundler install All supported rubies ship with bundler at this point, and those that don't probably don't build on modern systems via asdf or mise at this point --- src/mstrap/runtime_managers/mise.cr | 2 +- src/mstrap/runtimes/go.cr | 11 ++++++----- src/mstrap/runtimes/node.cr | 3 +-- src/mstrap/runtimes/ruby.cr | 3 +-- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/mstrap/runtime_managers/mise.cr b/src/mstrap/runtime_managers/mise.cr index 6d7a919..49fe5fb 100644 --- a/src/mstrap/runtime_managers/mise.cr +++ b/src/mstrap/runtime_managers/mise.cr @@ -30,7 +30,7 @@ module MStrap mise_installed = JSON.parse(mise_json_output) if installed = mise_installed.as_a? - installed.map { |version| version["version"].as_s } + installed.map(&.["version"].as_s) else Array(String).new end diff --git a/src/mstrap/runtimes/go.cr b/src/mstrap/runtimes/go.cr index f88c809..144278a 100644 --- a/src/mstrap/runtimes/go.cr +++ b/src/mstrap/runtimes/go.cr @@ -19,11 +19,12 @@ module MStrap def install_packages(packages : Array(Defs::PkgDef), runtime_version : String? = nil) : Bool packages.all? do |pkg| - cmd_args = if SemanticVersion.parse(runtime_version) >= GO_INSTALL_MIN_VERSION - ["install"] - else - ["get", "-u"] - end + cmd_args = + if SemanticVersion.parse(runtime_version) >= GO_INSTALL_MIN_VERSION + ["install"] + else + ["get", "-u"] + end if version = pkg.version cmd_args << "#{pkg.name}@#{version}" diff --git a/src/mstrap/runtimes/node.cr b/src/mstrap/runtimes/node.cr index 220c645..5f0c3db 100644 --- a/src/mstrap/runtimes/node.cr +++ b/src/mstrap/runtimes/node.cr @@ -10,8 +10,7 @@ module MStrap def bootstrap if File.exists?("yarn.lock") - cmd "brew install yarn", quiet: true && - skip_reshim { runtime_exec "yarn install" } + cmd "brew install yarn", quiet: true && skip_reshim { runtime_exec "yarn install" } elsif File.exists?("package.json") skip_reshim { runtime_exec "npm install" } end diff --git a/src/mstrap/runtimes/ruby.cr b/src/mstrap/runtimes/ruby.cr index 640bc84..7b8c72e 100644 --- a/src/mstrap/runtimes/ruby.cr +++ b/src/mstrap/runtimes/ruby.cr @@ -10,8 +10,7 @@ module MStrap def bootstrap if File.exists?("gems.rb") || File.exists?("Gemfile") - cmd "gem install bundler", quiet: true - cmd "bundle check || bundle install", quiet: true + runtime_exec "bundle check || bundle install" end end From 0bbb8822c94b64efd230d5476458aa47740eddca Mon Sep 17 00:00:00 2001 From: Max Fierke Date: Sat, 27 Jan 2024 20:41:26 -0600 Subject: [PATCH 08/11] Abstract away language runtime execution via runtime manager --- src/mstrap/runtime.cr | 19 +++++++---------- src/mstrap/runtime_manager.cr | 4 ++-- src/mstrap/runtime_managers/asdf.cr | 29 ++++++++++++++++++------- src/mstrap/runtime_managers/mise.cr | 33 ++++++++++++++++++++--------- 4 files changed, 54 insertions(+), 31 deletions(-) diff --git a/src/mstrap/runtime.cr b/src/mstrap/runtime.cr index 6071a81..938d040 100644 --- a/src/mstrap/runtime.cr +++ b/src/mstrap/runtime.cr @@ -10,13 +10,7 @@ module MStrap # Execute a command using a specific language runtime version def runtime_exec(command : String, args : Array(String)? = nil, runtime_version : String? = nil) - if runtime_version - version_env_var = runtime_manager.version_env_var(language_name) - env = {version_env_var => runtime_version} - cmd env, command, args, quiet: true - else - cmd command, args, quiet: true - end + runtime_manager.runtime_exec(language_name, command, args, runtime_version) end # Bootstrap the current directory for the runtime @@ -101,15 +95,18 @@ module MStrap # # NOTE: This will not traverse parent directories to find versions files. def with_dir_version(dir, &) - version_env_var = runtime_manager.version_env_var(language_name) - env_version = ENV[version_env_var]? + org_version = current_version begin Dir.cd(dir) do - ENV[version_env_var] = current_version + unless runtime_manager.set_version(language_name, current_version) + raise_setup_error!("Unable to switch version to #{current_version}") + end yield end ensure - ENV[version_env_var] = env_version + unless runtime_manager.set_version(language_name, org_version) + raise_setup_error!("Unable to set version back to #{current_version}") + end end end diff --git a/src/mstrap/runtime_manager.cr b/src/mstrap/runtime_manager.cr index 43c1c3a..4924673 100644 --- a/src/mstrap/runtime_manager.cr +++ b/src/mstrap/runtime_manager.cr @@ -22,9 +22,9 @@ module MStrap abstract def install_version(language_name : String, version : String) : Bool abstract def installed_versions(language_name : String) : Array(String) abstract def latest_version(language_name : String) : String - abstract def plugin_name(language_name : String) : String? + abstract def runtime_exec(language_name : String, command : String, args : Array(String)? = nil, runtime_version : String? = nil) + abstract def set_version(language_name : String, version : String?) : Bool abstract def set_global_version(language_name : String, version : String) : Bool - abstract def version_env_var(language_name : String) : String macro finished # :nodoc: diff --git a/src/mstrap/runtime_managers/asdf.cr b/src/mstrap/runtime_managers/asdf.cr index 2af6365..3675904 100644 --- a/src/mstrap/runtime_managers/asdf.cr +++ b/src/mstrap/runtime_managers/asdf.cr @@ -62,12 +62,28 @@ module MStrap end end + # Execute a command using a specific language runtime version + def runtime_exec(language_name : String, command : String, args : Array(String)? = nil, runtime_version : String? = nil) + if runtime_version + version_env_var = version_env_var(language_name) + env = {version_env_var => runtime_version} + cmd env, command, args, quiet: true + else + cmd command, args, quiet: true + end + end + + def set_version(language_name : String, version : String?) : Bool + version_env_var = version_env_var(language_name) + ENV[version_env_var] = version + true + end + def set_global_version(language_name, version : String) : Bool cmd "asdf global #{plugin_name(language_name)} #{version}", quiet: true end - # :nodoc: - def version_env_var(language_name) : String + private def version_env_var(language_name) : String if asdf_plugin_name = plugin_name(language_name) "ASDF_#{asdf_plugin_name.upcase}_VERSION" else @@ -75,14 +91,12 @@ module MStrap end end - # :nodoc: - def version_from_env(language_name) + private def version_from_env(language_name) env_var_name = version_env_var(language_name) ENV[env_var_name]? end - # :nodoc: - def version_from_tool_versions(language_name) + private def version_from_tool_versions(language_name) tool_versions_path = File.join(Dir.current, ".tool-versions") return nil unless File.exists?(tool_versions_path) @@ -95,8 +109,7 @@ module MStrap end end - # :nodoc: - def version_from_legacy_version_file(language_name) + private def version_from_legacy_version_file(language_name) version_path = File.join(Dir.current, ".#{language_name}-version") return nil unless File.exists?(version_path) File.read(version_path).strip diff --git a/src/mstrap/runtime_managers/mise.cr b/src/mstrap/runtime_managers/mise.cr index 49fe5fb..fe2c2b0 100644 --- a/src/mstrap/runtime_managers/mise.cr +++ b/src/mstrap/runtime_managers/mise.cr @@ -6,7 +6,25 @@ module MStrap end def current_version(language_name : String) : String? - `mise current #{language_name}`.chomp + `mise current #{plugin_name(language_name)}`.chomp + end + + # Execute a command using a specific language runtime version + def runtime_exec(language_name : String, command : String, args : Array(String)? = nil, runtime_version : String? = nil) + exec_args = [] of String + + if runtime_version + exec_args << "#{plugin_name(language_name)}@#{runtime_version}" + end + + cmd_args = ["exec"] + exec_args + ["--", command] + cmd_args += args unless !args + + if command && (!args || args.empty?) + cmd "mise #{cmd_args.join(' ')}", quiet: true + else + cmd "mise", cmd_args, quiet: true + end end # Returns whether the mise plugin has been installed for a language runtime @@ -45,17 +63,12 @@ module MStrap language_name end - def set_global_version(language_name, version : String) : Bool - cmd "mise use -g #{plugin_name(language_name)}@#{version}", quiet: true + def set_version(language_name : String, version : String?) : Bool + true end - # :nodoc: - def version_env_var(language_name) : String - if mise_plugin_name = plugin_name(language_name) - "MISE_#{mise_plugin_name.upcase}_VERSION" - else - "MISE_#{language_name.upcase}_VERSION" - end + def set_global_version(language_name, version : String) : Bool + cmd "mise use -g #{plugin_name(language_name)}@#{version}", quiet: true end end end From 25d0c236ec6854addd893bba1c76643684f3d6be Mon Sep 17 00:00:00 2001 From: Max Fierke Date: Sat, 27 Jan 2024 21:11:09 -0600 Subject: [PATCH 09/11] Cleanup some runtime setup error handling --- src/mstrap/cli.cr | 2 ++ src/mstrap/runtime.cr | 8 ++++---- src/mstrap/runtimes/crystal.cr | 6 ------ 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/mstrap/cli.cr b/src/mstrap/cli.cr index 9756ca9..7430e13 100644 --- a/src/mstrap/cli.cr +++ b/src/mstrap/cli.cr @@ -235,6 +235,8 @@ DESC Signal::TERM.trap { exit 1 } Commander.run(cli, options.argv) + rescue e : MStrapError + logc e.message rescue e {% if flag?(:debug) %} raise e diff --git a/src/mstrap/runtime.cr b/src/mstrap/runtime.cr index 938d040..596286b 100644 --- a/src/mstrap/runtime.cr +++ b/src/mstrap/runtime.cr @@ -65,13 +65,13 @@ module MStrap # Returns whether the project uses the runtime abstract def matches? : Bool - # Installs asdf plugin for the language runtime and installs any of the - # language runtime dependencies for the project. + # Installs runtime manager plugin for the language runtime and installs any + # of the language runtime dependencies for the project. def setup unless runtime_manager.has_plugin?(language_name) log "--> Installing #{language_name} plugin to #{runtime_manager.name}: " unless runtime_manager.install_plugin(language_name) - logc "There was an error adding the #{language_name} plugin to #{runtime_manager.name}. Check #{MStrap::Paths::LOG_FILE} or run again with --debug" + raise_setup_error!("There was an unexpected error adding the #{language_name} plugin to #{runtime_manager.name}") end success "OK" end @@ -82,7 +82,7 @@ module MStrap if current_version && current_version != "" && !has_version?(current_version) log "--> Installing #{language_name} #{current_version} via #{runtime_manager.name}: " unless runtime_manager.install_version(language_name, current_version) - logc "There was an error installing #{language_name} #{current_version} via #{runtime_manager.name}. Check #{MStrap::Paths::LOG_FILE} or run again with --debug" + raise_setup_error!("There was an unexpected error installing #{current_version} via #{runtime_manager.name}") end success "OK" end diff --git a/src/mstrap/runtimes/crystal.cr b/src/mstrap/runtimes/crystal.cr index 4e77a33..6099610 100644 --- a/src/mstrap/runtimes/crystal.cr +++ b/src/mstrap/runtimes/crystal.cr @@ -4,12 +4,6 @@ module MStrap # with Crystal via the chosen runtime manager and bootstrapping a Crystal # project based on conventions. class Crystal < Runtime - class SetupError < RuntimeSetupError - def initialize(message) - super("crystal", message) - end - end - def language_name : String "crystal" end From 2b8571a3a32d9885314e0c288c424be7ffc5c502 Mon Sep 17 00:00:00 2001 From: Max Fierke Date: Sat, 27 Jan 2024 22:14:13 -0600 Subject: [PATCH 10/11] Create runtimes config section for default_manager --- spec/mstrap/configuration_spec.cr | 42 +++++++++++++++++++++++++- src/mstrap/configuration.cr | 2 +- src/mstrap/defs/config_def.cr | 12 +++++--- src/mstrap/defs/runtimes_config_def.cr | 15 +++++++++ src/mstrap/runtime_managers/mise.cr | 2 +- 5 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 src/mstrap/defs/runtimes_config_def.cr diff --git a/spec/mstrap/configuration_spec.cr b/spec/mstrap/configuration_spec.cr index 2bebec7..58b2f09 100644 --- a/spec/mstrap/configuration_spec.cr +++ b/spec/mstrap/configuration_spec.cr @@ -34,7 +34,7 @@ def delete_profile(name) end Spectator.describe MStrap::Configuration do - let(config_def) do + let(config_def_v1_0) do MStrap::Defs::ConfigDef.from_hcl(<<-HCL) version = "1.0" @@ -50,6 +50,8 @@ Spectator.describe MStrap::Configuration do HCL end + let(config_def) { config_def_v1_0 } + let(personal_profile_def) do MStrap::Defs::ProfileDef.from_hcl(<<-HCL) version = "1.0" @@ -315,6 +317,44 @@ Spectator.describe MStrap::Configuration do end end + describe "#runtime_manager" do + context "for v1.0 configs" do + subject { MStrap::Configuration.new(config_def_v1_0) } + + it "defaults to asdf" do + expect(subject.runtime_manager).to be_a(MStrap::RuntimeManagers::ASDF) + end + end + + context "for v1.1 configs" do + let(config_def_v1_1) do + MStrap::Defs::ConfigDef.from_hcl(<<-HCL) + version = "1.1" + + runtimes { + default_manager = "mise" + } + + user { + name = "Reginald Testington" + email = "reginald@testington.biz" + } + + profile "personal" { + url = "ssh://git@gitprovider.biz/reggiemctest/mstrap-personal.git" + } + + HCL + end + + subject { MStrap::Configuration.new(config_def_v1_1) } + + it "can be set through runtimes.default_manager" do + expect(subject.runtime_manager).to be_a(MStrap::RuntimeManagers::Mise) + end + end + end + describe "#save!" do it "saves the loaded configuration back to disk" do subject = MStrap::Configuration.new(config_def) diff --git a/src/mstrap/configuration.cr b/src/mstrap/configuration.cr index fdc22bc..b5e276b 100644 --- a/src/mstrap/configuration.cr +++ b/src/mstrap/configuration.cr @@ -44,7 +44,7 @@ module MStrap @loaded_profiles = [] of Defs::ProfileDef @known_profile_configs = config.profiles + [DEFAULT_PROFILE_CONFIG_DEF] @resolved_profile = Defs::ProfileDef.new - @runtime_manager = RuntimeManager.for(config.runtime_manager) + @runtime_manager = RuntimeManager.for(config.runtimes.default_manager) @user = User.new(user: config.user) end diff --git a/src/mstrap/defs/config_def.cr b/src/mstrap/defs/config_def.cr index 01cc90c..e473808 100644 --- a/src/mstrap/defs/config_def.cr +++ b/src/mstrap/defs/config_def.cr @@ -9,13 +9,13 @@ module MStrap @[HCL::Block(key: "profile")] property profiles = [] of ::MStrap::Defs::ProfileConfigDef - @[HCL::Attribute] - property runtime_manager = "asdf" + @[HCL::Block] + property runtimes = ::MStrap::Defs::RuntimesConfigDef.new @[HCL::Block] property user = ::MStrap::Defs::UserDef.new - def_equals_and_hash @version, @profiles, @user + def_equals_and_hash @version, @profiles, @runtimes, @user def self.from_url(url : String) HTTP::Client.get(url, tls: MStrap.tls_client) do |response| @@ -23,7 +23,11 @@ module MStrap end end - def initialize(@user = UserDef.new, @profiles = Array(ProfileConfigDef).new) + def initialize( + @user = UserDef.new, + @profiles = Array(ProfileConfigDef).new, + @runtimes = RuntimesConfigDef.new + ) end end end diff --git a/src/mstrap/defs/runtimes_config_def.cr b/src/mstrap/defs/runtimes_config_def.cr new file mode 100644 index 0000000..0df0c6e --- /dev/null +++ b/src/mstrap/defs/runtimes_config_def.cr @@ -0,0 +1,15 @@ +module MStrap + module Defs + class RuntimesConfigDef + include HCL::Serializable + + @[HCL::Attribute] + property default_manager = "asdf" + + def_equals_and_hash @default_manager + + def initialize + end + end + end +end diff --git a/src/mstrap/runtime_managers/mise.cr b/src/mstrap/runtime_managers/mise.cr index fe2c2b0..a94f5f2 100644 --- a/src/mstrap/runtime_managers/mise.cr +++ b/src/mstrap/runtime_managers/mise.cr @@ -18,7 +18,7 @@ module MStrap end cmd_args = ["exec"] + exec_args + ["--", command] - cmd_args += args unless !args + cmd_args += args if args if command && (!args || args.empty?) cmd "mise #{cmd_args.join(' ')}", quiet: true From f66c11c548c846deb9215c836bed111d50ec58d5 Mon Sep 17 00:00:00 2001 From: Max Fierke Date: Sat, 27 Jan 2024 23:39:27 -0600 Subject: [PATCH 11/11] Start the 0.7.0 cycle w/ 0.7.0.dev --- CHANGELOG.md | 15 +++++++++++++++ meson.build | 2 +- shard.yml | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7297b69..2e488d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Support for using [mise](https://mise.jdx.dev) to manage language runtime versions (#50). `config.hcl` can be configured as such to enable it: + ``` + version = "1.1" + + # ... + + runtimes { + default_manager = "mise" + } + ``` + + To switch, you'll need to re-run `mstrap` and restart your terminal windows. + Then, you can run `brew uninstall asdf --force` to uninstall asdf (`mstrap` + will have removed `asdf`'s activation from mstrap's `env.sh` already) + ### Changed ### Bugfixes diff --git a/meson.build b/meson.build index 3e204a6..e4a7556 100644 --- a/meson.build +++ b/meson.build @@ -2,7 +2,7 @@ project('mstrap', 'c', meson_version : '>= 0.60.0', license : 'MIT', - version : '0.6.0', + version : '0.7.0.dev', default_options : [ 'buildtype=debugoptimized', 'default_library=static' diff --git a/shard.yml b/shard.yml index fe591da..343b8c2 100644 --- a/shard.yml +++ b/shard.yml @@ -1,5 +1,5 @@ name: mstrap -version: 0.6.0 +version: 0.7.0.dev authors: - Max Fierke