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

feat(query): add lockfile package changes to affectedPackage #9639

Merged
merged 3 commits into from
Jan 3, 2025
Merged
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
33 changes: 33 additions & 0 deletions crates/turborepo-lib/src/query/external_package.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use std::sync::Arc;

use async_graphql::Object;

use crate::run::Run;

#[derive(Clone)]
pub struct ExternalPackage {
run: Arc<Run>,
package: turborepo_lockfiles::Package,
}

impl ExternalPackage {
pub fn new(run: Arc<Run>, package: turborepo_lockfiles::Package) -> Self {
Self { run, package }
}

/// Converts the lockfile key to a human friendly name
pub fn human_name(&self) -> String {
self.run
.pkg_dep_graph()
.lockfile()
.and_then(|lockfile| lockfile.human_name(&self.package))
.unwrap_or_else(|| self.package.key.clone())
}
}

#[Object]
impl ExternalPackage {
async fn name(&self) -> String {
self.human_name().to_string()
}
}
156 changes: 82 additions & 74 deletions crates/turborepo-lib/src/query/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod external_package;
mod file;
mod package;
mod server;
@@ -11,6 +12,7 @@ use std::{

use async_graphql::{http::GraphiQLSource, *};
use axum::{response, response::IntoResponse};
use external_package::ExternalPackage;
use miette::Diagnostic;
use package::Package;
pub use server::run_server;
@@ -64,6 +66,82 @@ impl RepositoryQuery {
pub fn new(run: Arc<Run>) -> Self {
Self { run }
}

fn convert_change_reason(
&self,
reason: turborepo_repository::change_mapper::PackageInclusionReason,
) -> PackageChangeReason {
match reason {
turborepo_repository::change_mapper::PackageInclusionReason::All(
AllPackageChangeReason::GlobalDepsChanged { file },
) => PackageChangeReason::GlobalDepsChanged(GlobalDepsChanged {
file_path: file.to_string(),
}),
turborepo_repository::change_mapper::PackageInclusionReason::All(
AllPackageChangeReason::DefaultGlobalFileChanged { file },
) => PackageChangeReason::DefaultGlobalFileChanged(DefaultGlobalFileChanged {
file_path: file.to_string(),
}),
turborepo_repository::change_mapper::PackageInclusionReason::All(
AllPackageChangeReason::LockfileChangeDetectionFailed,
) => {
PackageChangeReason::LockfileChangeDetectionFailed(LockfileChangeDetectionFailed {
empty: false,
})
}
turborepo_repository::change_mapper::PackageInclusionReason::All(
AllPackageChangeReason::GitRefNotFound { from_ref, to_ref },
) => PackageChangeReason::GitRefNotFound(GitRefNotFound { from_ref, to_ref }),
turborepo_repository::change_mapper::PackageInclusionReason::All(
AllPackageChangeReason::LockfileChangedWithoutDetails,
) => {
PackageChangeReason::LockfileChangedWithoutDetails(LockfileChangedWithoutDetails {
empty: false,
})
}
turborepo_repository::change_mapper::PackageInclusionReason::All(
AllPackageChangeReason::RootInternalDepChanged { root_internal_dep },
) => PackageChangeReason::RootInternalDepChanged(RootInternalDepChanged {
root_internal_dep: root_internal_dep.to_string(),
}),
turborepo_repository::change_mapper::PackageInclusionReason::RootTask { task } => {
PackageChangeReason::RootTask(RootTask {
task_name: task.to_string(),
})
}
turborepo_repository::change_mapper::PackageInclusionReason::ConservativeRootLockfileChanged => {
PackageChangeReason::ConservativeRootLockfileChanged(ConservativeRootLockfileChanged { empty: false })
}
turborepo_repository::change_mapper::PackageInclusionReason::LockfileChanged { removed, added } => {
let removed = removed.into_iter().map(|package| ExternalPackage::new(self.run.clone(), package)).collect::<Array<_>>();
let added = added.into_iter().map(|package| ExternalPackage::new(self.run.clone(), package)).collect::<Array<_>>();
PackageChangeReason::LockfileChanged(LockfileChanged { empty: false, removed, added })
}
turborepo_repository::change_mapper::PackageInclusionReason::DependencyChanged {
dependency,
} => PackageChangeReason::DependencyChanged(DependencyChanged {
dependency_name: dependency.to_string(),
}),
turborepo_repository::change_mapper::PackageInclusionReason::DependentChanged {
dependent,
} => PackageChangeReason::DependentChanged(DependentChanged {
dependent_name: dependent.to_string(),
}),
turborepo_repository::change_mapper::PackageInclusionReason::FileChanged { file } => {
PackageChangeReason::FileChanged(FileChanged {
file_path: file.to_string(),
})
}
turborepo_repository::change_mapper::PackageInclusionReason::InFilteredDirectory {
directory,
} => PackageChangeReason::InFilteredDirectory(InFilteredDirectory {
directory_path: directory.to_string(),
}),
turborepo_repository::change_mapper::PackageInclusionReason::IncludedByFilter {
filters,
} => PackageChangeReason::IncludedByFilter(IncludedByFilter { filters }),
}
}
}

#[derive(Debug, SimpleObject)]
@@ -72,6 +150,7 @@ impl RepositoryQuery {
#[graphql(concrete(name = "ChangedPackages", params(ChangedPackage)))]
#[graphql(concrete(name = "Files", params(File)))]
#[graphql(concrete(name = "TraceErrors", params(file::TraceError)))]
#[graphql(concrete(name = "ExternalPackages", params(ExternalPackage)))]
pub struct Array<T: OutputType> {
items: Vec<T>,
length: usize,
@@ -375,6 +454,8 @@ struct ConservativeRootLockfileChanged {
struct LockfileChanged {
/// This is a nothing field
empty: bool,
removed: Array<ExternalPackage>,
added: Array<ExternalPackage>,
}

#[derive(SimpleObject)]
@@ -416,79 +497,6 @@ enum PackageChangeReason {
InFilteredDirectory(InFilteredDirectory),
}

impl From<turborepo_repository::change_mapper::PackageInclusionReason> for PackageChangeReason {
fn from(value: turborepo_repository::change_mapper::PackageInclusionReason) -> Self {
match value {
turborepo_repository::change_mapper::PackageInclusionReason::All(
AllPackageChangeReason::GlobalDepsChanged { file },
) => PackageChangeReason::GlobalDepsChanged(GlobalDepsChanged {
file_path: file.to_string(),
}),
turborepo_repository::change_mapper::PackageInclusionReason::All(
AllPackageChangeReason::DefaultGlobalFileChanged { file },
) => PackageChangeReason::DefaultGlobalFileChanged(DefaultGlobalFileChanged {
file_path: file.to_string(),
}),
turborepo_repository::change_mapper::PackageInclusionReason::All(
AllPackageChangeReason::LockfileChangeDetectionFailed,
) => {
PackageChangeReason::LockfileChangeDetectionFailed(LockfileChangeDetectionFailed {
empty: false,
})
}
turborepo_repository::change_mapper::PackageInclusionReason::All(
AllPackageChangeReason::GitRefNotFound { from_ref, to_ref },
) => PackageChangeReason::GitRefNotFound(GitRefNotFound { from_ref, to_ref }),
turborepo_repository::change_mapper::PackageInclusionReason::All(
AllPackageChangeReason::LockfileChangedWithoutDetails,
) => {
PackageChangeReason::LockfileChangedWithoutDetails(LockfileChangedWithoutDetails {
empty: false,
})
}
turborepo_repository::change_mapper::PackageInclusionReason::All(
AllPackageChangeReason::RootInternalDepChanged { root_internal_dep },
) => PackageChangeReason::RootInternalDepChanged(RootInternalDepChanged {
root_internal_dep: root_internal_dep.to_string(),
}),
turborepo_repository::change_mapper::PackageInclusionReason::RootTask { task } => {
PackageChangeReason::RootTask(RootTask {
task_name: task.to_string(),
})
}
turborepo_repository::change_mapper::PackageInclusionReason::ConservativeRootLockfileChanged => {
PackageChangeReason::ConservativeRootLockfileChanged(ConservativeRootLockfileChanged { empty: false })
}
turborepo_repository::change_mapper::PackageInclusionReason::LockfileChanged => {
PackageChangeReason::LockfileChanged(LockfileChanged { empty: false })
}
turborepo_repository::change_mapper::PackageInclusionReason::DependencyChanged {
dependency,
} => PackageChangeReason::DependencyChanged(DependencyChanged {
dependency_name: dependency.to_string(),
}),
turborepo_repository::change_mapper::PackageInclusionReason::DependentChanged {
dependent,
} => PackageChangeReason::DependentChanged(DependentChanged {
dependent_name: dependent.to_string(),
}),
turborepo_repository::change_mapper::PackageInclusionReason::FileChanged { file } => {
PackageChangeReason::FileChanged(FileChanged {
file_path: file.to_string(),
})
}
turborepo_repository::change_mapper::PackageInclusionReason::InFilteredDirectory {
directory,
} => PackageChangeReason::InFilteredDirectory(InFilteredDirectory {
directory_path: directory.to_string(),
}),
turborepo_repository::change_mapper::PackageInclusionReason::IncludedByFilter {
filters,
} => PackageChangeReason::IncludedByFilter(IncludedByFilter { filters }),
}
}
}

#[derive(SimpleObject)]
struct ChangedPackage {
reason: PackageChangeReason,
@@ -518,7 +526,7 @@ impl RepositoryQuery {
.map(|(package, reason)| {
Ok(ChangedPackage {
package: Package::new(self.run.clone(), package)?,
reason: reason.into(),
reason: self.convert_change_reason(reason),
})
})
.filter(|package: &Result<ChangedPackage, Error>| {
10 changes: 5 additions & 5 deletions crates/turborepo-lib/src/run/scope/change_detector.rs
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ use turbopath::{AbsoluteSystemPath, AnchoredSystemPathBuf};
use turborepo_repository::{
change_mapper::{
AllPackageChangeReason, ChangeMapper, DefaultPackageChangeMapper, Error,
GlobalDepsPackageChangeMapper, LockfileChange, PackageChanges, PackageInclusionReason,
GlobalDepsPackageChangeMapper, PackageChanges, PackageInclusionReason,
},
package_graph::{PackageGraph, PackageName},
};
@@ -53,12 +53,12 @@ impl<'a> ScopeChangeDetector<'a> {

/// Gets the lockfile content from SCM if it has changed.
/// Does *not* error if cannot get content, instead just
/// returns an empty lockfile change
/// returns a Some(None)
fn get_lockfile_contents(
&self,
from_ref: Option<&str>,
changed_files: &HashSet<AnchoredSystemPathBuf>,
) -> Option<LockfileChange> {
) -> Option<Option<Vec<u8>>> {
let lockfile_path = self
.pkg_graph
.package_manager()
@@ -79,10 +79,10 @@ impl<'a> ScopeChangeDetector<'a> {
.lockfile_path(self.turbo_root);

let Ok(content) = self.scm.previous_content(from_ref, &lockfile_path) else {
return Some(LockfileChange::Empty);
return Some(None);
};

Some(LockfileChange::WithContent(content))
Some(Some(content))
}
}

8 changes: 8 additions & 0 deletions crates/turborepo-lockfiles/src/berry/mod.rs
Original file line number Diff line number Diff line change
@@ -531,6 +531,14 @@ impl Lockfile for BerryLockfile {
let entry = self.locator_package.get(key)?;
Some(entry.version.clone())
}

fn human_name(&self, package: &crate::Package) -> Option<String> {
let locator = Locator::try_from(package.key.as_str()).ok()?;
let berry_package = self.locator_package.get(&locator)?;
let name = locator.ident.to_string();
let version = &berry_package.version;
Some(format!("{name}@{version}"))
}
}

impl LockfileData {
7 changes: 7 additions & 0 deletions crates/turborepo-lockfiles/src/bun/mod.rs
Original file line number Diff line number Diff line change
@@ -122,6 +122,13 @@ impl Lockfile for BunLockfile {
fn turbo_version(&self) -> Option<String> {
None
}

fn human_name(&self, package: &crate::Package) -> Option<String> {
let entry = self.inner.get(&package.key)?;
let name = entry.name.as_deref()?;
let version = &entry.version;
Some(format!("{name}@{version}"))
}
}

impl Entry {
9 changes: 9 additions & 0 deletions crates/turborepo-lockfiles/src/lib.rs
Original file line number Diff line number Diff line change
@@ -65,6 +65,15 @@ pub trait Lockfile: Send + Sync + Any + std::fmt::Debug {

/// Return any turbo version found in the lockfile
fn turbo_version(&self) -> Option<String>;

/// A human friendly version of a lockfile key.
/// Usually of the form `package@version`, but version might include
/// additional information to convey difference from other packages in
/// the lockfile e.g. differing peer dependencies.
#[allow(unused)]
fn human_name(&self, package: &Package) -> Option<String> {
None
}
}

/// Takes a lockfile, and a map of workspace directory paths -> (package name,
7 changes: 7 additions & 0 deletions crates/turborepo-lockfiles/src/npm.rs
Original file line number Diff line number Diff line change
@@ -152,6 +152,13 @@ impl Lockfile for NpmLockfile {
let turbo_entry = self.packages.get("node_modules/turbo")?;
turbo_entry.version.clone()
}

fn human_name(&self, package: &Package) -> Option<String> {
let npm_package = self.packages.get(&package.key)?;
let version = npm_package.version.as_deref()?;
let name = package.key.split("node_modules/").last()?;
Some(format!("{name}@{version}"))
}
}

impl NpmLockfile {
10 changes: 10 additions & 0 deletions crates/turborepo-lockfiles/src/pnpm/data.rs
Original file line number Diff line number Diff line change
@@ -524,6 +524,16 @@ impl crate::Lockfile for PnpmLockfile {
.find_map(|project| project.dependencies.turbo_version())?;
Some(turbo_version.to_owned())
}

fn human_name(&self, package: &crate::Package) -> Option<String> {
if matches!(self.version(), SupportedLockfileVersion::V7AndV9) {
Some(package.key.clone())
} else {
// TODO: this is really hacky and doesn't properly handle v5 as it uses `/` as
// the delimiter between name and version
Some(package.key.strip_prefix('/')?.to_owned())
}
}
}

impl DependencyInfo {
7 changes: 7 additions & 0 deletions crates/turborepo-lockfiles/src/yarn1/mod.rs
Original file line number Diff line number Diff line change
@@ -127,6 +127,13 @@ impl Lockfile for Yarn1Lockfile {
let entry = self.inner.get(key)?;
Some(entry.version.clone())
}

fn human_name(&self, package: &crate::Package) -> Option<String> {
let entry = self.inner.get(&package.key)?;
let name = entry.name.as_deref()?;
let version = &entry.version;
Some(format!("{name}@{version}"))
}
}

pub fn yarn_subgraph(contents: &[u8], packages: &[String]) -> Result<Vec<u8>, crate::Error> {
Loading
Loading