Skip to content

Commit

Permalink
new: Use resolved versions of dependencies when hashing. (#290)
Browse files Browse the repository at this point in the history
* Add npm/yarn1.

* Add pnpm and yarn.

* Use object form for yarn 2.

* Add hasher config.

* Add blog.

* Polish stuff.

* Add yarn caching.

* Add pnpm.

* Add npm.

* Rework yarn1.

* Yarn changes.

* Fix some issues.

* Add yarn test.

* Add npm tests.

* Add pnpm tests.

* Support number deps.

* Test yarn 1.

* Polish stuff.
  • Loading branch information
milesj committed Aug 31, 2022
1 parent 694db07 commit 95d24b7
Show file tree
Hide file tree
Showing 28 changed files with 1,185 additions and 115 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion crates/action-runner/src/actions/run_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@ pub async fn run_target(
runner
.is_cached(
common_hasher,
node_actions::create_target_hasher(&workspace, &project)?,
node_actions::create_target_hasher(&workspace, &project).await?,
)
.await?
}
Expand Down
4 changes: 4 additions & 0 deletions crates/config/src/workspace/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::providers::url::Url;
use crate::types::{FileGlob, FilePath};
use crate::validators::{validate_child_relative_path, validate_extends, validate_id};
use crate::workspace::action_runner::ActionRunnerConfig;
use crate::workspace::hasher::HasherConfig;
use crate::workspace::node::NodeConfig;
use crate::workspace::typescript::TypeScriptConfig;
use crate::workspace::vcs::VcsConfig;
Expand Down Expand Up @@ -68,6 +69,9 @@ pub struct WorkspaceConfig {
#[validate(custom = "validate_extends")]
pub extends: Option<String>,

#[validate]
pub hasher: HasherConfig,

#[validate]
pub node: Option<NodeConfig>,

Expand Down
18 changes: 18 additions & 0 deletions crates/config/src/workspace/hasher.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use validator::Validate;

#[derive(Clone, Debug, Default, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum HasherOptimization {
#[default]
Accuracy,
Performance,
}

#[derive(Clone, Debug, Default, Deserialize, Eq, JsonSchema, PartialEq, Serialize, Validate)]
#[schemars(default)]
#[serde(rename_all = "camelCase")]
pub struct HasherConfig {
pub optimization: HasherOptimization,
}
2 changes: 2 additions & 0 deletions crates/config/src/workspace/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
mod action_runner;
mod config;
mod hasher;
mod node;
mod typescript;
mod vcs;

pub use action_runner::*;
pub use config::*;
pub use hasher::*;
pub use node::*;
pub use typescript::*;
pub use vcs::*;
7 changes: 5 additions & 2 deletions crates/config/tests/workspace_test.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use moon_config::{
ActionRunnerConfig, ConfigError, NodeConfig, VcsConfig, VcsManager, WorkspaceConfig,
WorkspaceProjects,
ActionRunnerConfig, ConfigError, HasherConfig, NodeConfig, VcsConfig, VcsManager,
WorkspaceConfig, WorkspaceProjects,
};
use moon_constants::CONFIG_WORKSPACE_FILENAME;
use moon_utils::test::get_fixtures_dir;
Expand Down Expand Up @@ -29,6 +29,7 @@ fn loads_defaults() {
WorkspaceConfig {
action_runner: ActionRunnerConfig::default(),
extends: None,
hasher: HasherConfig::default(),
node: None,
projects: WorkspaceProjects::default(),
typescript: None,
Expand Down Expand Up @@ -287,6 +288,7 @@ node:
WorkspaceConfig {
action_runner: ActionRunnerConfig::default(),
extends: None,
hasher: HasherConfig::default(),
node: Some(NodeConfig {
package_manager: NodePackageManager::Yarn,
..NodeConfig::default()
Expand Down Expand Up @@ -786,6 +788,7 @@ vcs:
WorkspaceConfig {
action_runner: ActionRunnerConfig::default(),
extends: None,
hasher: HasherConfig::default(),
node: None, // NodeConfig::default(),
projects: WorkspaceProjects::default(),
typescript: None,
Expand Down
1 change: 1 addition & 0 deletions crates/error/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ edition = "2021"
[dependencies]
regex = "1.6.0"
serde_json = { version = "1.0.82", default-features = false }
serde_yaml = { version = "0.9.4", default-features = false }
thiserror = "1.0.31"
4 changes: 4 additions & 0 deletions crates/error/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use regex::Error as RegexError;
use serde_json::Error as JsonError;
use serde_yaml::Error as YamlError;
use std::io::{Error as IoError, ErrorKind as IoErrorKind};
use std::path::PathBuf;
use thiserror::Error;
Expand Down Expand Up @@ -40,6 +41,9 @@ pub enum MoonError {
#[error("Process <shell>{0}</shell> failed with a <symbol>{1}</symbol> exit code.\n<muted>{2}</muted>")]
ProcessNonZeroWithOutput(String, i32, String),

#[error("Failed to parse <path>{0}</path>: {1}")]
Yaml(PathBuf, #[source] YamlError),

#[error(transparent)]
Io(#[from] IoError),

Expand Down
2 changes: 2 additions & 0 deletions crates/lang-node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ lazy_static = "1.4.0"
regex = "1.6.0"
serde = { version = "1.0.140", features = ["derive"] }
serde_json = { version = "1.0.82", features = ["preserve_order"] }
serde_yaml = "0.9.4"

[dev-dependencies]
assert_fs = "1.0.7"
pretty_assertions = "1.2.1"
serial_test = "0.8.0"
tokio = { version = "1.20.0", features = ["test-util"] }
4 changes: 4 additions & 0 deletions crates/lang-node/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
pub mod node;
pub mod npm;
pub mod package;
pub mod pnpm;
pub mod tsconfig;
pub mod yarn;
pub mod yarn_classic;

use moon_lang::{Language, PackageManager, VersionManager};

Expand Down
167 changes: 167 additions & 0 deletions crates/lang-node/src/npm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
use cached::proc_macro::cached;
use moon_error::MoonError;
use moon_lang::config_cache;
use moon_lang::LockfileDependencyVersions;
use moon_utils::fs::sync_read_json;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
use std::path::{Path, PathBuf};

config_cache!(
PackageLock,
"package-lock.json",
sync_read_json,
write_lockfile
);

#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PackageLockDependency {
pub dependencies: Option<HashMap<String, PackageLockDependency>>,
pub dev: Option<bool>,
pub integrity: Option<String>,
pub requires: Option<HashMap<String, String>>,
pub resolved: Option<String>,
pub version: String,

#[serde(flatten)]
pub unknown: HashMap<String, Value>,
}

#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PackageLock {
pub lockfile_version: Value,
pub name: String,
pub dependencies: Option<HashMap<String, PackageLockDependency>>,
pub packages: Option<HashMap<String, Value>>,
pub requires: Option<bool>,

#[serde(flatten)]
pub unknown: HashMap<String, Value>,

#[serde(skip)]
pub path: PathBuf,
}

fn write_lockfile(_path: &Path, _lockfile: &PackageLock) -> Result<(), MoonError> {
Ok(()) // Do nothing
}

#[cached(result)]
pub fn load_lockfile_dependencies(path: PathBuf) -> Result<LockfileDependencyVersions, MoonError> {
let mut deps: LockfileDependencyVersions = HashMap::new();

if let Some(lockfile) = PackageLock::read(path)? {
// TODO: This isn't entirely accurate as npm does not hoist all dependencies
// to the root of the lockfile. We'd need to recursively extract everything,
// but for now, this will get us most of the way.
for (name, dep) in lockfile.dependencies.unwrap_or_default() {
if let Some(versions) = deps.get_mut(&name) {
versions.push(dep.version.clone());
} else {
deps.insert(name, vec![dep.version.clone()]);
}
}
}

Ok(deps)
}

#[cfg(test)]
mod tests {
use super::*;
use assert_fs::prelude::*;
use moon_utils::string_vec;
use pretty_assertions::assert_eq;
use serde_json::Number;

#[test]
fn parses_lockfile() {
let temp = assert_fs::TempDir::new().unwrap();

temp.child("package-lock.json")
.write_str(r#"
{
"name": "moon-examples",
"lockfileVersion": 2,
"requires": true,
"dependencies": {
"@babel/helper-function-name": {
"version": "7.18.9",
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz",
"integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==",
"requires": {
"@babel/template": "^7.18.6",
"@babel/types": "^7.18.9"
}
},
"rollup-plugin-polyfill-node": {
"version": "0.10.2",
"resolved": "https://registry.npmjs.org/rollup-plugin-polyfill-node/-/rollup-plugin-polyfill-node-0.10.2.tgz",
"integrity": "sha512-5GMywXiLiuQP6ZzED/LO/Q0HyDi2W6b8VN+Zd3oB0opIjyRs494Me2ZMaqKWDNbGiW4jvvzl6L2n4zRgxS9cSQ==",
"dev": true,
"requires": {
"@rollup/plugin-inject": "^4.0.0"
}
}
}
}"#,
)
.unwrap();

let lockfile: PackageLock = sync_read_json(temp.path().join("package-lock.json")).unwrap();

assert_eq!(
lockfile,
PackageLock {
lockfile_version: Value::Number(Number::from(2)),
name: "moon-examples".into(),
requires: Some(true),
dependencies: Some(HashMap::from([(
"@babel/helper-function-name".to_owned(),
PackageLockDependency {
integrity: Some("sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==".into()),
requires: Some(HashMap::from([
("@babel/template".to_owned(), "^7.18.6".to_owned()),
("@babel/types".to_owned(), "^7.18.9".to_owned())
])),
resolved: Some("https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz".into()),
version: "7.18.9".into(),
..PackageLockDependency::default()
}
), (
"rollup-plugin-polyfill-node".to_owned(),
PackageLockDependency {
dev: Some(true),
integrity: Some("sha512-5GMywXiLiuQP6ZzED/LO/Q0HyDi2W6b8VN+Zd3oB0opIjyRs494Me2ZMaqKWDNbGiW4jvvzl6L2n4zRgxS9cSQ==".into()),
requires: Some(HashMap::from([
("@rollup/plugin-inject".to_owned(), "^4.0.0".to_owned())
])),
resolved: Some("https://registry.npmjs.org/rollup-plugin-polyfill-node/-/rollup-plugin-polyfill-node-0.10.2.tgz".into()),
version: "0.10.2".into(),
..PackageLockDependency::default()
}
)])),
..PackageLock::default()
}
);

assert_eq!(
load_lockfile_dependencies(temp.path().join("package-lock.json")).unwrap(),
HashMap::from([
(
"@babel/helper-function-name".to_owned(),
string_vec!["7.18.9"]
),
(
"rollup-plugin-polyfill-node".to_owned(),
string_vec!["0.10.2"]
),
])
);

temp.close().unwrap();
}
}
Loading

0 comments on commit 95d24b7

Please sign in to comment.