Skip to content

Commit

Permalink
Merge pull request #54 from maxfierke/mf-rustup
Browse files Browse the repository at this point in the history
Implement rustup as a possible runtime manager
  • Loading branch information
maxfierke authored Apr 15, 2024
2 parents 71cb979 + 776384d commit 1b803b9
Show file tree
Hide file tree
Showing 38 changed files with 533 additions and 173 deletions.
119 changes: 116 additions & 3 deletions spec/mstrap/configuration_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -317,12 +317,12 @@ Spectator.describe MStrap::Configuration do
end
end

describe "#runtime_manager" do
describe "#default_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)
expect(subject.default_runtime_manager).to be_a(MStrap::RuntimeManagers::ASDF)
end
end

Expand Down Expand Up @@ -350,7 +350,120 @@ Spectator.describe MStrap::Configuration do
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)
expect(subject.default_runtime_manager).to be_a(MStrap::RuntimeManagers::Mise)
end
end
end

describe "#runtime_managers" do
context "for v1.0 configs" do
subject { MStrap::Configuration.new(config_def_v1_0) }

it "defaults to [asdf]" do
expect(subject.runtime_managers).to eq([MStrap::RuntimeManager.for("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"
runtime "rust" {
manager = "rustup"
}
}
user {
name = "Reginald Testington"
email = "[email protected]"
}
profile "personal" {
url = "ssh://[email protected]/reggiemctest/mstrap-personal.git"
}
HCL
end

subject { MStrap::Configuration.new(config_def_v1_1) }

it "it derived through runtimes.default_manager and runtimes.runtime[*].manager" do
expect(subject.runtime_managers).to eq([
MStrap::RuntimeManager.for("mise"),
MStrap::RuntimeManager.for("rustup"),
])
end
end
end

describe "#runtimes" do
context "for v1.0 configs" do
subject { MStrap::Configuration.new(config_def_v1_0) }

it "defaults all to ASDF-provided runtimes" do
%w(crystal go node php python ruby rust).each do |language_name|
expect(subject.runtimes[language_name].runtime_manager).to be_a(MStrap::RuntimeManagers::ASDF)
end
end

it "returns the expected language runtimes" do
expect(subject.runtimes["crystal"]).to be_a(MStrap::Runtimes::Crystal)
expect(subject.runtimes["go"]).to be_a(MStrap::Runtimes::Go)
expect(subject.runtimes["node"]).to be_a(MStrap::Runtimes::Node)
expect(subject.runtimes["php"]).to be_a(MStrap::Runtimes::Php)
expect(subject.runtimes["python"]).to be_a(MStrap::Runtimes::Python)
expect(subject.runtimes["ruby"]).to be_a(MStrap::Runtimes::Ruby)
expect(subject.runtimes["rust"]).to be_a(MStrap::Runtimes::Rust)
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"
runtime "rust" {
manager = "rustup"
}
}
user {
name = "Reginald Testington"
email = "[email protected]"
}
profile "personal" {
url = "ssh://[email protected]/reggiemctest/mstrap-personal.git"
}
HCL
end

subject { MStrap::Configuration.new(config_def_v1_1) }

it "allows overriding language manager for specific languages" do
%w(crystal go node php python ruby).each do |language_name|
expect(subject.runtimes[language_name].runtime_manager).to be_a(MStrap::RuntimeManagers::Mise)
end

expect(subject.runtimes["rust"].runtime_manager).to be_a(MStrap::RuntimeManagers::Rustup)
end

it "returns the expected language runtimes" do
expect(subject.runtimes["crystal"]).to be_a(MStrap::Runtimes::Crystal)
expect(subject.runtimes["go"]).to be_a(MStrap::Runtimes::Go)
expect(subject.runtimes["node"]).to be_a(MStrap::Runtimes::Node)
expect(subject.runtimes["php"]).to be_a(MStrap::Runtimes::Php)
expect(subject.runtimes["python"]).to be_a(MStrap::Runtimes::Python)
expect(subject.runtimes["ruby"]).to be_a(MStrap::Runtimes::Ruby)
expect(subject.runtimes["rust"]).to be_a(MStrap::Runtimes::Rust)
end
end
end
Expand Down
3 changes: 2 additions & 1 deletion src/mstrap.cr
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ require "./mstrap/profile_fetcher"
require "./mstrap/user"
require "./mstrap/configuration"
require "./mstrap/supports/**"
require "./mstrap/web_bootstrapper"
require "./mstrap/bootstrapper"
require "./mstrap/bootstrappers/**"
require "./mstrap/runtime_manager"
require "./mstrap/runtime_managers/**"
require "./mstrap/runtime"
Expand Down
28 changes: 28 additions & 0 deletions src/mstrap/bootstrapper.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module MStrap
abstract class Bootstrapper
include DSL

def initialize(@config : Configuration)
end

def self.for(config : Configuration, project : Project) : Array(Bootstrapper)
bootstrappers = Array(Bootstrapper).new

if project.run_scripts? && Bootstrappers::ScriptBootstrapper.has_scripts?(project)
bootstrappers << Bootstrappers::ScriptBootstrapper.new(config)
else
bootstrappers << Bootstrappers::DefaultBootstrapper.new(config)

if project.web?
bootstrappers << Bootstrappers::WebBootstrapper.new(config)
end
end

bootstrappers
end

abstract def bootstrap(project : Project) : Bool

protected getter :config
end
end
35 changes: 35 additions & 0 deletions src/mstrap/bootstrappers/default_bootstrapper.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module MStrap
module Bootstrappers
class DefaultBootstrapper < Bootstrapper
# Conventional bootstrapping from mstrap. This will auto-detect the runtimes
# used by the project and run the standard bootstrapping for each runtime.
# 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`.
def bootstrap(project : Project) : Bool
logd "Bootstrapping '#{project.name}' with runtime defaults."

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"
runtime.setup
end
end
end

true
end

def runtime_impls(project)
if project.runtimes.empty?
config.runtimes
else
config.runtimes.select do |_, runtime|
project.runtimes.includes?(runtime.language_name)
end
end
end
end
end
end
38 changes: 38 additions & 0 deletions src/mstrap/bootstrappers/script_bootstrapper.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
module MStrap
module Bootstrappers
class ScriptBootstrapper < Bootstrapper
# :nodoc:
BOOTSTRAP_SCRIPT = File.join("script", "bootstrap")

# :nodoc:
SETUP_SCRIPT = File.join("script", "setup")

# Executes `script/bootstrap` and `script/setup` (if either exists and are
# configured to run)
def bootstrap(project : Project) : Bool
logd "Found bootstrapping scripts, executing instead of using defaults."

begin
ENV["__MSTRAP_EXEC_SCRIPTS"] = "true"

Dir.cd(project.path) do
cmd BOOTSTRAP_SCRIPT if File.exists?(BOOTSTRAP_SCRIPT)
cmd SETUP_SCRIPT if File.exists?(SETUP_SCRIPT)
end
ensure
ENV.delete("__MSTRAP_EXEC_SCRIPTS")
end

true
end

# Whether project has any bootstrapping/setup scripts a-la
# [`scripts-to-rule-them-all`](https://github.com/github/scripts-to-rule-them-all)
def self.has_scripts?(project)
[BOOTSTRAP_SCRIPT, SETUP_SCRIPT].any? do |script_path|
File.exists?(File.join(project.path, script_path))
end
end
end
end
end
33 changes: 33 additions & 0 deletions src/mstrap/bootstrappers/web_bootstrapper.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
module MStrap
module Bootstrappers
# The `WebBootstrapper` is responsible for bootstrapping web-based projects.
# Currently, this is just setting up an NGINX configuration for the project.
class WebBootstrapper < Bootstrapper
include DSL

def initialize(@config : Configuration)
super
@mkcert = Mkcert.new
end

# Executes the bootstrapper
def bootstrap(project : Project) : Bool
logd "'#{project.name}' is a web project. Running web bootstrapper."

if mkcert.installed?
Dir.cd(Paths::PROJECT_CERTS) do
mkcert.install!
mkcert.install_cert!(project.hostname)
end
else
logw "mkcert not found. Skipping cert setup."
end

Templates::NginxConf.new(project).write_to_config!
true
end

private getter :mkcert
end
end
end
2 changes: 1 addition & 1 deletion src/mstrap/cli.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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(config.runtime_manager)
Bootstrapper.for(config, project).each(&.bootstrap(project))
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
Loading

0 comments on commit 1b803b9

Please sign in to comment.