Skip to content
Draft
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
/target
dist/
.hasty/
73 changes: 73 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ clap = { version = "4.0.26", features = ["derive"] }
daggy = "0.8.0"
futures = "0.3.25"
glob = "0.3.0"
hex = "0.4.3"
log = "0.4.17"
serde = { version = "1.0.147", features = ["derive"] }
serde_json = "1.0.88"
sha2 = "0.10.6"
tokio = { version = "1.22.0", features = ["full", "process"] }
urlencoding = "2.1.2"
46 changes: 46 additions & 0 deletions src/cache.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use hex::encode;
use log::{debug, info};
use std::path::PathBuf;

use crate::Script;

static CACHE_DIR: &str = ".hasty/cache";

pub trait Cache {
fn new(working_dir: &PathBuf) -> Self;
fn exists(&self, script: &Script) -> bool;
// fn get(&self, script: &Script)
// fn set(&self, script: &Script)
}

pub struct LocalCache {
working_dir: PathBuf,
}

impl Cache for LocalCache {
fn new(working_dir: &PathBuf) -> Self {
LocalCache {
working_dir: working_dir.into(),
}
}

fn exists(&self, script: &Script) -> bool {
let cache_dir = get_cache_dir(&self.working_dir);
let script_cache_dir = cache_dir.join(format!("{}", encode(script.id())));
let fingerprint_cache_dir =
script_cache_dir.join(format!("{}", script.fingerprint.clone().unwrap()));

let exists = fingerprint_cache_dir.exists();

info!(
target: &format!("{}:{}", "cache", script.id()),
"dir: {:?}, exists: {}", fingerprint_cache_dir, exists
);

exists
}
}

fn get_cache_dir(working_dir: &PathBuf) -> PathBuf {
working_dir.join(CACHE_DIR)
}
58 changes: 58 additions & 0 deletions src/fingerprint.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use std::{fs::File, io};

use crate::Script;
use log::info;
use sha2::{Digest, Sha256};

#[derive(Debug, Clone)]
pub struct Fingerprint {
hashes: Vec<String>,
}

impl Fingerprint {
pub fn new() -> Self {
Fingerprint { hashes: vec![] }
}

// TODO: include system info, dependency info, and global config?
pub fn compute(&mut self, script: &Script) -> &mut Self {
if self.hashes.len() > 0 {
return self;
}

if let Some(files) = &script.config.files {
for script_file in files {
let mut sha256 = Sha256::new();
let mut file: Option<File> = None;

let file_result = File::open(script.dir.join(script_file));

match file_result {
Ok(x) => file = Some(x),
Err(error) => {
info!("error loading script file: {:?}", error);
}
}

if let Some(mut f) = file {
io::copy(&mut f, &mut sha256);
let result = sha256.finalize();

self.hashes.push(format!("{:x}", result));
}
}
}

return self;
}

pub fn string(&self) -> String {
self.hashes.join(",")
}
}

impl PartialEq for Fingerprint {
fn eq(&self, other: &Self) -> bool {
self.string() == other.string()
}
}
40 changes: 31 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mod cache;
mod fingerprint;
pub mod logger;
pub mod options;
pub mod package_json;
Expand All @@ -6,6 +8,7 @@ use daggy::{
petgraph::visit::{IntoNodeIdentifiers, Topo},
Dag, NodeIndex, Walker,
};
use fingerprint::Fingerprint;
use futures::future::join_all;
use log::{error, info};
use package_json::{find_workspaces, PackageJSON};
Expand All @@ -19,6 +22,8 @@ use tokio::{
};
use urlencoding::encode;

use crate::cache::{Cache, LocalCache};

static CONFIG_FILE_NAME: &str = "hasty.json";
pub static TOPOLOGICAL_DEP_PREFIX: &str = "^";

Expand Down Expand Up @@ -51,6 +56,7 @@ pub struct Script {
pub package_name: String,
config: CommandConfig,
pub dir: PathBuf,
pub fingerprint: Option<String>,
}

impl Script {
Expand All @@ -68,10 +74,17 @@ impl Script {
dir: dir.into(),
command: name.to_string(),
status,
fingerprint: None,
}
}

pub fn execute(&mut self) -> Child {
let mut fp = Fingerprint::new();

self.fingerprint = Some(fp.compute(self).string());

info!(target: &self.id(), "fingerprint: {}", self.fingerprint.as_ref().unwrap());

self.status = ScriptStatus::Running;

let mut command = Command::new("npm");
Expand Down Expand Up @@ -241,6 +254,8 @@ impl Engine {
}
}

let working_dir = self.dir.clone();

// add a task that we can await later to ensure things get cleaned up correctly
tasks.push(tokio::spawn(async move {
let mut deps_remaining = deps_channels.len();
Expand All @@ -256,18 +271,25 @@ impl Engine {
}
}

let cache = LocalCache::new(&working_dir);

let mut fp = Fingerprint::new();
script.fingerprint = Some(fp.compute(&script).string());

if dry_run {
info!("execute: {}", script.id());
} else {
let mut child = script.execute();

let status = match child.wait().await {
Ok(status) => Some(status),
Err(err) => {
error!("Error running script: {:?}", err);
None
}
};
if !cache.exists(&script) {
let mut child = script.execute();

let status = match child.wait().await {
Ok(status) => Some(status),
Err(err) => {
error!("Error running script: {:?}", err);
None
}
};
}
}

script_watcher.send_replace(ScriptStatus::Finished);
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ async fn main() {
.unwrap_or(std::env::current_dir().unwrap());

let config = hasty::load_config_file(&options);

let mut tasks_to_execute = vec![];

match options.script {
Expand Down
2 changes: 2 additions & 0 deletions test/basic/hasty.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
"pipeline": {
"build": {
"command": "build",
"files": ["index.js"],
"output": ["dist/**"],
"dependencies": ["format", "^build"]
},
"lint": {
Expand Down
1 change: 0 additions & 1 deletion test/basic/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"packages/*"
],
"scripts": {
"build": "echo \"build\"",
"lint": "echo \"lint\"",
"test": "echo \"test\"",
"format": "echo \"format:start\"; sleep 1; echo \"format:end\"" }
Expand Down
1 change: 1 addition & 0 deletions test/basic/packages/a/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('hello world a');
2 changes: 1 addition & 1 deletion test/basic/packages/a/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "a",
"scripts": {
"build": "echo \"build:a:start\"; sleep 1; echo \"build:a:end\"",
"build": "mkdir -p ./dist && cp ./index.js ./dist",
"format": "echo \"format:a:start\"; sleep 1; echo \"format:a:end\"; exit 1"
},
"dependencies": {
Expand Down
1 change: 1 addition & 0 deletions test/basic/packages/b/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('hello world b')