diff --git a/Cargo.lock b/Cargo.lock index 8dd037cedb45f..baa28ee0fd166 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6569,6 +6569,7 @@ dependencies = [ "napi", "napi-build", "napi-derive", + "pretty_assertions", "thiserror", "tokio", "turbopath", diff --git a/crates/turborepo-paths/src/absolute_system_path.rs b/crates/turborepo-paths/src/absolute_system_path.rs index 2393a758b3336..2c82e8b26e2c0 100644 --- a/crates/turborepo-paths/src/absolute_system_path.rs +++ b/crates/turborepo-paths/src/absolute_system_path.rs @@ -248,6 +248,8 @@ impl AbsoluteSystemPath { ) } + /// Note: This does not handle resolutions, so `../` in a path won't + /// resolve. pub fn anchor(&self, path: &AbsoluteSystemPath) -> Result { AnchoredSystemPathBuf::new(self, path) } diff --git a/crates/turborepo-paths/src/anchored_system_path_buf.rs b/crates/turborepo-paths/src/anchored_system_path_buf.rs index 6147be0461ac2..359edd219fd83 100644 --- a/crates/turborepo-paths/src/anchored_system_path_buf.rs +++ b/crates/turborepo-paths/src/anchored_system_path_buf.rs @@ -73,6 +73,8 @@ impl<'a> From<&'a AnchoredSystemPathBuf> for wax::CandidatePath<'a> { } impl AnchoredSystemPathBuf { + /// Note: This does not handle resolutions, so `../` in a path won't + /// resolve. pub fn new( root: impl AsRef, path: impl AsRef, diff --git a/crates/turborepo-repository/src/change_mapper/mod.rs b/crates/turborepo-repository/src/change_mapper/mod.rs index 0138a7a503328..dce6b0325d323 100644 --- a/crates/turborepo-repository/src/change_mapper/mod.rs +++ b/crates/turborepo-repository/src/change_mapper/mod.rs @@ -122,6 +122,12 @@ impl<'a, PD: PackageChangeMapper> ChangeMapper<'a, PD> { pub fn changed_packages( &self, changed_files: HashSet, + // None - we don't know if the lockfile changed + // + // Some(None) - we know the lockfile changed, but don't know exactly why (i.e. `git status` + // and the lockfile is there) + // + // Some(Some(content)) - we know the lockfile changed and have the contents lockfile_change: Option>>, ) -> Result { if let Some(file) = Self::default_global_file_changed(&changed_files) { diff --git a/packages/turbo-repository/README.md b/packages/turbo-repository/README.md index e0ecf00dd3d9a..94fc90378ebfd 100644 --- a/packages/turbo-repository/README.md +++ b/packages/turbo-repository/README.md @@ -11,6 +11,34 @@ for the JS API. This package contains scripts to build dev and release versions. `pnpm build && pnpm package` will build and package a dev version of the native library for `darwin-arm64`, or you can pass an additional argument for a specific target. `pnpm build:release` will build a release version of the library -# Publishing +## Setup + +Install JS dependencies: + +```sh +pnpm install +``` + +## Build + +Build native library and TypeScript wrapper: + +```sh +pnpm build +cargo build +``` + +## Test + +```sh +pnpm test +``` + +## Example Usage + +You can see examples in the `__tests__` directory, or see a simple script in `node scripts/test.mjs`. +Note that this may fall out of date over time, but it's meant to be used during the early iterations. + +## Publishing There is now a version bump script in [bump-version.sh](./scripts/bump-version.sh). Passing it the new version will bump the meta package version, as well as the optional dependencies list and native packages. diff --git a/packages/turbo-repository/__tests__/affected-packages.test.ts b/packages/turbo-repository/__tests__/affected-packages.test.ts index 9885078fd6ecb..4cb62e0b50f6b 100644 --- a/packages/turbo-repository/__tests__/affected-packages.test.ts +++ b/packages/turbo-repository/__tests__/affected-packages.test.ts @@ -6,9 +6,10 @@ import { Workspace, Package, PackageManager } from "../js/dist/index.js"; type PackageReduced = Pick; interface AffectedPackagesTestParams { + description: string; files: string[]; + changedLockfile?: string | undefined | null; expected: PackageReduced[]; - description: string; } describe("affectedPackages", () => { @@ -33,22 +34,48 @@ describe("affectedPackages", () => { }, { description: - "global change should be irrelevant but still triggers all packages", - files: ["README.md"], - expected: [ - { name: "app-a", relativePath: "apps/app" }, - { name: "ui", relativePath: "packages/ui" }, - ], + "a lockfile change will only affect packages impacted by the change", + files: [], + changedLockfile: `lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: {} + + apps/app: + dependencies: + microdiff: + specifier: ^1.4.0 + version: 1.5.0 + ui: + specifier: workspace:* + version: link:../../packages/ui + + packages/blank: {} + + packages/ui: {} + +packages: + + /microdiff@1.5.0: + resolution: {integrity: sha512-Drq+/THMvDdzRYrK0oxJmOKiC24ayUV8ahrt8l3oRK51PWt6gdtrIGrlIH3pT/lFh1z93FbAcidtsHcWbnRz8Q==} + dev: false +`, + expected: [{ name: "app-a", relativePath: "apps/app" }], }, ]; - for (const { description, files, expected } of tests) { + for (const { description, files, expected, changedLockfile } of tests) { it(description, async () => { const dir = path.resolve(__dirname, "./fixtures/monorepo"); const workspace = await Workspace.find(dir); const reduced: PackageReduced[] = ( - await workspace.affectedPackages(files) + await workspace.affectedPackages(files, changedLockfile) ).map((pkg) => { return { name: pkg.name, diff --git a/packages/turbo-repository/__tests__/fixtures/monorepo/apps/app/package.json b/packages/turbo-repository/__tests__/fixtures/monorepo/apps/app/package.json index 18fa556034020..6b7095cdbdb27 100644 --- a/packages/turbo-repository/__tests__/fixtures/monorepo/apps/app/package.json +++ b/packages/turbo-repository/__tests__/fixtures/monorepo/apps/app/package.json @@ -1,6 +1,7 @@ { "name": "app-a", "dependencies": { - "ui": "*" + "microdiff": "^1.4.0", + "ui": "workspace:*" } } diff --git a/packages/turbo-repository/__tests__/fixtures/monorepo/package.json b/packages/turbo-repository/__tests__/fixtures/monorepo/package.json index 6d79e3c7f8588..409bceb9ac7dd 100644 --- a/packages/turbo-repository/__tests__/fixtures/monorepo/package.json +++ b/packages/turbo-repository/__tests__/fixtures/monorepo/package.json @@ -1,4 +1,4 @@ { "name": "basic", - "packageManager": "pnpm@8.1.0" + "packageManager": "pnpm@8.14.0" } diff --git a/packages/turbo-repository/__tests__/fixtures/monorepo/pnpm-lock.yaml b/packages/turbo-repository/__tests__/fixtures/monorepo/pnpm-lock.yaml index d39016eb31aed..c7d50d6e90e75 100644 --- a/packages/turbo-repository/__tests__/fixtures/monorepo/pnpm-lock.yaml +++ b/packages/turbo-repository/__tests__/fixtures/monorepo/pnpm-lock.yaml @@ -1,13 +1,28 @@ lockfileVersion: '6.0' +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + importers: .: {} - app: + apps/app: dependencies: + microdiff: + specifier: ^1.4.0 + version: 1.4.0 ui: - specifier: '*' - version: link:../ui + specifier: workspace:* + version: link:../../packages/ui + + packages/blank: {} + + packages/ui: {} + +packages: - ui: {} + /microdiff@1.4.0: + resolution: {integrity: sha512-OBKBOa1VBznvLPb/3ljeJaENVe0fO0lnWl77lR4vhPlQD71UpjEoRV5P0KdQkcjbFlBu1Oy2mEUBMU3wxcBAGg==} + dev: false diff --git a/packages/turbo-repository/js/index.d.ts b/packages/turbo-repository/js/index.d.ts index dc781ee5d541e..26d252b6f8741 100644 --- a/packages/turbo-repository/js/index.d.ts +++ b/packages/turbo-repository/js/index.d.ts @@ -55,5 +55,8 @@ export class Workspace { * of strings relative to the monorepo root and use the current system's * path separator. */ - affectedPackages(files: Array): Promise>; + affectedPackages( + files: Array, + changedLockfile?: string | undefined | null + ): Promise>; } diff --git a/packages/turbo-repository/rust/Cargo.toml b/packages/turbo-repository/rust/Cargo.toml index d26c16f4718d3..2ba24ed040667 100644 --- a/packages/turbo-repository/rust/Cargo.toml +++ b/packages/turbo-repository/rust/Cargo.toml @@ -18,5 +18,8 @@ tokio = { workspace = true } turbopath = { workspace = true } turborepo-repository = { workspace = true } +[dev-dependencies] +pretty_assertions = { workspace = true } + [build-dependencies] napi-build = "2.0.1" diff --git a/packages/turbo-repository/rust/README.md b/packages/turbo-repository/rust/README.md index 2c86eb8b4b3b8..2a58ba84c2a81 100644 --- a/packages/turbo-repository/rust/README.md +++ b/packages/turbo-repository/rust/README.md @@ -1,20 +1,5 @@ -## Setup: +# `turbo-repository` -Install JS dependencies: +This sub-crate provides repository helpers which are written in Rust and translated to TypeScript with [NAPI-RS](https://napi.rs). -``` -pnpm i -``` - -## Build: - -Build native library and js wrapper - -```sh -pnpm build -``` - -## Example Usage - -You can see examples in the `__tests__` directory, or see a simple script in `node scripts/test.mjs`. -Note that this may fall out of date over time, but it's meant to be used during the early iterations. +See the [root `README.md` for instructions](../README.md). diff --git a/packages/turbo-repository/rust/src/lib.rs b/packages/turbo-repository/rust/src/lib.rs index 7687c8bd4d394..8460427b6c39f 100644 --- a/packages/turbo-repository/rust/src/lib.rs +++ b/packages/turbo-repository/rust/src/lib.rs @@ -7,14 +7,14 @@ use napi::Error; use napi_derive::napi; use turbopath::{AbsoluteSystemPath, AnchoredSystemPathBuf}; use turborepo_repository::{ - change_mapper::{ChangeMapper, DefaultPackageChangeMapper, PackageChanges}, + change_mapper::{ChangeMapper, GlobalDepsPackageChangeMapper, PackageChanges}, inference::RepoState as WorkspaceState, package_graph::{PackageGraph, PackageName, PackageNode, WorkspacePackage, ROOT_PKG_NAME}, }; mod internal; #[napi] -#[derive(PartialEq, Eq, Hash, Clone)] +#[derive(PartialEq, Eq, Hash, Clone, Debug)] pub struct Package { pub name: String, /// The absolute path to the package root. @@ -187,12 +187,15 @@ impl Workspace { /// of strings relative to the monorepo root and use the current system's /// path separator. #[napi] - pub async fn affected_packages(&self, files: Vec) -> Result, Error> { + pub async fn affected_packages( + &self, + files: Vec, + changed_lockfile: Option, + ) -> Result, Error> { let workspace_root = match AbsoluteSystemPath::new(&self.absolute_path) { Ok(path) => path, Err(e) => return Err(Error::from_reason(e.to_string())), }; - let hash_set_of_paths: HashSet = files .into_iter() .filter_map(|path| { @@ -203,9 +206,11 @@ impl Workspace { .collect(); // Create a ChangeMapper with no ignore patterns - let default_package_detector = DefaultPackageChangeMapper::new(&self.graph); - let mapper = ChangeMapper::new(&self.graph, vec![], default_package_detector); - let package_changes = match mapper.changed_packages(hash_set_of_paths, None) { + let global_deps_package_detector = + GlobalDepsPackageChangeMapper::new(&self.graph, std::iter::empty::<&str>()).unwrap(); + let mapper = ChangeMapper::new(&self.graph, vec![], global_deps_package_detector); + let lockfile_change = changed_lockfile.map(|s| Some(s.into_bytes())); + let package_changes = match mapper.changed_packages(hash_set_of_paths, lockfile_change) { Ok(changes) => changes, Err(e) => return Err(Error::from_reason(e.to_string())), };