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
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
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;
Expand All @@ -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;
Expand Down Expand Up @@ -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)]
Expand All @@ -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,
Expand Down Expand Up @@ -375,6 +454,8 @@ struct ConservativeRootLockfileChanged {
struct LockfileChanged {
/// This is a nothing field
empty: bool,
removed: Array<ExternalPackage>,
added: Array<ExternalPackage>,
}

#[derive(SimpleObject)]
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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>| {
Expand Down
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
Expand Up @@ -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},
};
Expand Down Expand Up @@ -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()
Expand All @@ -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))
}
}

Expand Down
8 changes: 8 additions & 0 deletions crates/turborepo-lockfiles/src/berry/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
7 changes: 7 additions & 0 deletions crates/turborepo-lockfiles/src/bun/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
9 changes: 9 additions & 0 deletions crates/turborepo-lockfiles/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
7 changes: 7 additions & 0 deletions crates/turborepo-lockfiles/src/npm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
10 changes: 10 additions & 0 deletions crates/turborepo-lockfiles/src/pnpm/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
7 changes: 7 additions & 0 deletions crates/turborepo-lockfiles/src/yarn1/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down
Loading
Loading