Skip to content

Commit

Permalink
new: Add Bun tier 2 and 3 support. (#1164)
Browse files Browse the repository at this point in the history
* Update tool.

* Support override.

* Update docker.

* Add platform.

* Copy node into bun.

* Start on tests.

* Test overrides.

* Update docs.

* Add handbook.

* Update linux.
  • Loading branch information
milesj authored Nov 2, 2023
1 parent ce99d2f commit c6bef93
Show file tree
Hide file tree
Showing 93 changed files with 2,333 additions and 95 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ jobs:
bins: cargo-make, cargo-nextest, cargo-llvm-cov
components: llvm-tools-preview
cache: false
# Required for "globals" tests
- uses: moonrepo/setup-toolchain@v0
if: ${{ runner.os == 'Linux' }}
- run: proto install bun 1.0.0
if: ${{ runner.os == 'Linux' }}
- uses: mozilla-actions/[email protected]
- name: Run tests
if: ${{ env.WITH_COVERAGE == 'false' }}
Expand Down
39 changes: 39 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion crates/bun/lang/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod bun_lockb;
use moon_lang::{DependencyManager, Language};

pub use bun_lockb::*;
pub use moon_lang::LockfileDependencyVersions;

pub const BUN: Language = Language {
binary: "bun",
Expand All @@ -13,7 +14,7 @@ pub const BUN: Language = Language {

// Dependency managers

pub const BUN_INSTALL: DependencyManager = DependencyManager {
pub const BUNPM: DependencyManager = DependencyManager {
binary: "bun install",
config_files: &["bunfig.toml"],
lockfile: "bun.lockb",
Expand Down
37 changes: 37 additions & 0 deletions crates/bun/platform/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[package]
name = "moon_bun_platform"
version = "0.1.0"
edition = "2021"
publish = false

[dependencies]
moon_action_context = { path = "../../core/action-context" }
moon_bun_lang = { path = "../lang" }
moon_bun_tool = { path = "../tool" }
moon_common = { path = "../../../nextgen/common" }
moon_config = { path = "../../../nextgen/config" }
moon_hash = { path = "../../../nextgen/hash" }
moon_lang = { path = "../../core/lang" }
moon_logger = { path = "../../core/logger" }
moon_node_lang = { path = "../../node/lang" }
moon_platform = { path = "../../core/platform" }
moon_process = { path = "../../../nextgen/process" }
moon_project = { path = "../../../nextgen/project" }
moon_task = { path = "../../../nextgen/task" }
moon_terminal = { path = "../../core/terminal" }
moon_tool = { path = "../../core/tool" }
moon_typescript_platform = { path = "../../typescript/platform" }
moon_utils = { path = "../../core/utils" }
miette = { workspace = true }
proto_core = { workspace = true }
rustc-hash = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
starbase_styles = { workspace = true }
starbase_utils = { workspace = true }
tokio = { workspace = true }

[dev-dependencies]
moon = { path = "../../core/moon" }
moon_project_graph = { path = "../../../nextgen/project-graph" }
moon_test_utils = { path = "../../core/test-utils" }
32 changes: 32 additions & 0 deletions crates/bun/platform/src/actions/install_deps.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use moon_bun_lang::BUN;
use moon_bun_tool::BunTool;
use moon_lang::has_vendor_installed_dependencies;
use moon_logger::{debug, info};
use moon_terminal::{print_checkpoint, Checkpoint};
use moon_tool::DependencyManager;
use moon_utils::{is_ci, is_test_env};
use std::path::Path;

const LOG_TARGET: &str = "moon:bun-platform:install-deps";

pub async fn install_deps(bun: &BunTool, working_dir: &Path) -> miette::Result<()> {
// When in CI, we can avoid installing dependencies because
// we can assume they've already been installed before moon runs!
if is_ci() && has_vendor_installed_dependencies(working_dir, &BUN) {
info!(
target: LOG_TARGET,
"In a CI environment and dependencies already exist, skipping install"
);

return Ok(());
}

debug!(target: LOG_TARGET, "Installing dependencies");

print_checkpoint("bun install", Checkpoint::Setup);

bun.install_dependencies(&(), working_dir, !is_test_env())
.await?;

Ok(())
}
5 changes: 5 additions & 0 deletions crates/bun/platform/src/actions/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
mod install_deps;
mod run_target;

pub use install_deps::*;
pub use run_target::*;
145 changes: 145 additions & 0 deletions crates/bun/platform/src/actions/run_target.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
use crate::target_hash::BunTargetHash;
use moon_bun_tool::BunTool;
use moon_config::{HasherConfig, HasherOptimization};
use moon_node_lang::{
node::{self, BinFile},
PackageJson,
};
use moon_process::Command;
use moon_project::Project;
use moon_task::Task;
use moon_tool::{prepend_path_env_var, DependencyManager, Tool, ToolError};
use moon_utils::path;
use rustc_hash::FxHashMap;
use std::path::Path;

fn find_package_bin(
command: &mut Command,
starting_dir: &Path,
working_dir: &Path,
bin_name: &str,
) -> miette::Result<Option<Command>> {
let possible_bin_path = match node::find_package_bin(starting_dir, bin_name)? {
Some(bin) => bin,
None => {
// moon isn't installed as a node module, but probably
// exists globally, so let's go with that instead of failing
if bin_name == "moon" {
return Ok(Some(Command::new(bin_name)));
}

return Err(ToolError::MissingBinary("node module".into(), bin_name.to_owned()).into());
}
};

match possible_bin_path {
// Rust, Go
BinFile::Binary(bin_path) => {
return Ok(Some(Command::new(bin_path)));
}
// JavaScript
BinFile::Script(bin_path) => {
command.arg(path::to_string(
path::relative_from(bin_path, working_dir).unwrap(),
)?);
}
// Other (Bash)
BinFile::Other(bin_path, parent_cmd) => {
let mut cmd = Command::new(parent_cmd);
cmd.arg(bin_path);

return Ok(Some(cmd));
}
};

Ok(None)
}

pub fn create_target_command(
bun: &BunTool,
project: &Project,
task: &Task,
working_dir: &Path,
) -> miette::Result<Command> {
let mut command = Command::new(bun.get_bin_path()?);

match task.command.as_str() {
"bun" | "bunx" => {
if task.command == "bunx" {
command.arg("x");
}
}
bin => {
if let Some(new_command) =
find_package_bin(&mut command, &project.root, working_dir, bin)?
{
command = new_command;
}
}
};

if !bun.global {
command.env(
"PATH",
prepend_path_env_var([bun.tool.get_bin_path()?.parent().unwrap()]),
);
}

command.args(&task.args).envs(&task.env);

Ok(command)
}

// This is like the function above, but is for situations where the tool
// has not been configured, and should default to the global "bun" found
// in the user's shell.
pub fn create_target_command_without_tool(
project: &Project,
task: &Task,
working_dir: &Path,
) -> miette::Result<Command> {
let mut command = Command::new(&task.command);

if task.command != "bun" && task.command != "bunx" {
if let Some(new_command) =
find_package_bin(&mut command, &project.root, working_dir, &task.command)?
{
command = new_command;
}
}

command.args(&task.args).envs(&task.env);

Ok(command)
}

pub async fn create_target_hasher(
bun: Option<&BunTool>,
project: &Project,
workspace_root: &Path,
hasher_config: &HasherConfig,
) -> miette::Result<BunTargetHash> {
let mut hasher = BunTargetHash::new(
bun.map(|n| n.config.version.as_ref().map(|v| v.to_string()))
.unwrap_or_default(),
);

let resolved_dependencies =
if matches!(hasher_config.optimization, HasherOptimization::Accuracy) && bun.is_some() {
bun.unwrap()
.get_resolved_dependencies(&project.root)
.await?
} else {
FxHashMap::default()
};

if let Some(root_package) = PackageJson::read(workspace_root)? {
hasher.hash_package_json(&root_package, &resolved_dependencies);
}

if let Some(package) = PackageJson::read(&project.root)? {
hasher.hash_package_json(&package, &resolved_dependencies);
}

Ok(hasher)
}
Loading

0 comments on commit c6bef93

Please sign in to comment.