Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement rustup as a possible runtime manager #54

Merged
merged 6 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading