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

Introduce conflicts to moss and boulder #248

Merged
merged 3 commits into from
Jun 8, 2024
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
8 changes: 7 additions & 1 deletion boulder/src/package/emit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::{
};

use itertools::Itertools;
use moss::{package::Meta, Dependency};
use moss::{package::Meta, Dependency, Provider};
use thiserror::Error;
use tui::{ProgressBar, ProgressStyle, Styled};

Expand Down Expand Up @@ -82,6 +82,12 @@ impl<'a> Package<'a> {
)
.collect(),
providers: self.analysis.providers().cloned().collect(),
conflicts: self
.definition
.conflicts
.iter()
.filter_map(|name| Provider::from_name(name).ok())
.collect(),
uri: None,
hash: None,
download_size: None,
Expand Down
2 changes: 2 additions & 0 deletions crates/stone_recipe/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ pub struct Package {
pub run_deps: Vec<String>,
#[serde(default)]
pub paths: Vec<Path>,
#[serde(default)]
pub conflicts: Vec<String>,
}

#[derive(Debug, Clone)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ DROP TABLE IF EXISTS meta;
DROP TABLE IF EXISTS meta_licenses;
DROP TABLE IF EXISTS meta_dependencies;
DROP TABLE IF EXISTS meta_providers;
DROP TABLE IF EXISTS meta_conflicts;
7 changes: 7 additions & 0 deletions moss/src/db/meta/migrations/2024-03-03-165811_init/up.sql
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,10 @@ CREATE TABLE IF NOT EXISTS meta_providers (
PRIMARY KEY (package, provider),
FOREIGN KEY (package) REFERENCES meta(package) ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS meta_conflicts (
package TEXT NOT NULL,
conflict TEXT NOT NULL,
PRIMARY KEY (package, conflict),
FOREIGN KEY (package) REFERENCES meta(package) ON DELETE CASCADE
);
87 changes: 86 additions & 1 deletion moss/src/db/meta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ impl Database {
.load_iter(conn)?
.map(|p| Ok(p?.provider))
.collect::<Result<_, Error>>()?;
let conflicts = model::Conflict::belonging_to(&meta)
.select(model::Conflict::as_select())
.load_iter(conn)?
.map(|p| Ok(p?.conflict))
.collect::<Result<_, Error>>()?;

Ok(Meta {
name: meta.name,
Expand All @@ -84,6 +89,7 @@ impl Database {
licenses,
dependencies,
providers,
conflicts,
uri: meta.uri,
hash: meta.hash,
download_size: meta.download_size.map(|size| size as u64),
Expand Down Expand Up @@ -126,6 +132,7 @@ impl Database {
licenses: Default::default(),
dependencies: Default::default(),
providers: Default::default(),
conflicts: Default::default(),
uri: meta.uri,
hash: meta.hash,
download_size: meta.download_size.map(|size| size as u64),
Expand Down Expand Up @@ -206,6 +213,17 @@ impl Database {
}
Ok(())
})?;

// Add conflicts
model::Conflict::belonging_to(chunk)
.load_iter::<model::Conflict, _>(conn)?
.try_for_each::<_, Result<_, Error>>(|result| {
let row = result?;
if let Some(meta) = entries.get_mut(&row.package.into()) {
meta.conflicts.insert(row.conflict);
}
Ok(())
})?;
}

Ok(entries.into_iter().collect())
Expand Down Expand Up @@ -281,6 +299,17 @@ impl Database {
})
})
.collect::<Vec<_>>();
let conflicts = packages
.iter()
.flat_map(|(package, meta)| {
meta.conflicts.iter().map(|conflict| {
(
model::meta_conflicts::package.eq(<package::Id as AsRef<str>>::as_ref(package)),
model::meta_conflicts::conflict.eq(conflict.to_string()),
)
})
})
.collect::<Vec<_>>();

conn.transaction(|conn| {
batch_remove_impl(&ids, conn)?;
Expand All @@ -295,6 +324,9 @@ impl Database {
diesel::insert_into(model::meta_providers::table)
.values(providers)
.execute(conn)?;
diesel::insert_into(model::meta_conflicts::table)
.values(conflicts)
.execute(conn)?;
Ok(())
})
})
Expand Down Expand Up @@ -329,7 +361,7 @@ mod model {
Selectable,
};

pub use crate::db::meta::schema::{meta, meta_dependencies, meta_licenses, meta_providers};
pub use crate::db::meta::schema::{meta, meta_conflicts, meta_dependencies, meta_licenses, meta_providers};
use crate::package;

#[derive(Queryable, Selectable, Identifiable)]
Expand Down Expand Up @@ -392,6 +424,17 @@ mod model {
pub provider: crate::Provider,
}

#[derive(Queryable, Selectable, Identifiable, Associations)]
#[diesel(table_name = meta_conflicts)]
#[diesel(primary_key(package, conflict))]
#[diesel(belongs_to(Meta, foreign_key = package))]
#[diesel(belongs_to(PackageId, foreign_key = package))]
pub struct Conflict {
pub package: String,
#[diesel(deserialize_as = String)]
pub conflict: crate::Provider,
}

#[derive(Insertable)]
#[diesel(table_name = meta)]
pub struct NewMeta<'a> {
Expand Down Expand Up @@ -457,4 +500,46 @@ mod test {
let result = db.get(&id);
assert!(result.is_err());
}

#[test]
fn test_conflict_is_recognized() {
let db = Database::new(":memory:").unwrap();

// See `test/conflicts/italian-pizza.yml` for the recipe file that produced this stone.
// It should be obvious that this package conflicts with `name(pineapple)`.
let italian_pizza = include_bytes!("../../../../test/conflicts/italian-pizza-1-1-1-x86_64.stone");
let pineapple_provider = Provider {
kind: Kind::PackageName,
name: "pineapple".to_string(),
};

let mut stone = stone::read_bytes(italian_pizza).unwrap();

let payloads = stone.payloads().unwrap().collect::<Result<Vec<_>, _>>().unwrap();
let meta_payload = payloads.iter().find_map(PayloadKind::meta).unwrap();
let meta = Meta::from_stone_payload(&meta_payload.body).unwrap();
db.add(package::Id::from(meta.id()), meta.clone()).unwrap();

// Ensure we're parsing the correct package!
assert_eq!(&meta.name, &"italian-pizza".to_string().into());
// Ensure that the conflict info already exists in the binary package.
assert_eq!(
meta.conflicts.iter().collect::<Vec<&Provider>>(),
vec![&pineapple_provider]
);

// Now retrieve by provider.
let lookup = Filter::Provider(Provider {
kind: Kind::PackageName,
name: "italian-pizza".to_string(),
});
let fetched = db.query(Some(lookup)).unwrap();
assert_eq!(fetched.len(), 1);

let (_, retrieved_pkg) = fetched.first().unwrap();
let retrieved_conflicts: Vec<&Provider> = retrieved_pkg.conflicts.iter().collect();
// Ensure that the conflicts field is inserted into and can be queried from our database
// correctly.
assert_eq!(retrieved_conflicts, vec![&pineapple_provider]);
}
}
10 changes: 9 additions & 1 deletion moss/src/db/meta/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ diesel::table! {
}
}

diesel::table! {
meta_conflicts (package, conflict) {
package -> Text,
conflict -> Text,
}
}

diesel::table! {
meta_dependencies (package, dependency) {
package -> Text,
Expand All @@ -39,8 +46,9 @@ diesel::table! {
}
}

diesel::joinable!(meta_conflicts -> meta (package));
diesel::joinable!(meta_dependencies -> meta (package));
diesel::joinable!(meta_licenses -> meta (package));
diesel::joinable!(meta_providers -> meta (package));

diesel::allow_tables_to_appear_in_same_query!(meta, meta_dependencies, meta_licenses, meta_providers,);
diesel::allow_tables_to_appear_in_same_query!(meta, meta_conflicts, meta_dependencies, meta_licenses, meta_providers,);
31 changes: 25 additions & 6 deletions moss/src/package/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ pub struct Meta {
pub dependencies: BTreeSet<Dependency>,
/// All providers, including name()
pub providers: BTreeSet<Provider>,
/// All providers that conflict with this package
pub conflicts: BTreeSet<Provider>,
/// If relevant: uri to fetch from
pub uri: Option<String>,
/// If relevant: hash for the download
Expand Down Expand Up @@ -88,6 +90,7 @@ impl Meta {
name: name.clone(),
}))
.collect();
let conflicts = payload.iter().filter_map(meta_conflict).collect();

Ok(Meta {
name: Name::from(name),
Expand All @@ -102,6 +105,7 @@ impl Meta {
licenses,
dependencies,
providers,
conflicts,
uri,
hash,
download_size,
Expand Down Expand Up @@ -143,6 +147,12 @@ impl Meta {
.filter(|provider| provider.kind != dependency::Kind::PackageName)
.map(|provider| (Tag::Provides, Kind::Provider(provider.kind.into(), provider.name))),
)
.chain(
self.conflicts
.into_iter()
// We re-add this on ingestion / it's implied
.map(|conflict| (Tag::Conflicts, Kind::Provider(conflict.kind.into(), conflict.name))),
)
.map(|(tag, kind)| payload::Meta { tag, kind })
.collect()
}
Expand Down Expand Up @@ -205,13 +215,22 @@ fn meta_dependency(meta: &payload::Meta) -> Option<Dependency> {
}

fn meta_provider(meta: &payload::Meta) -> Option<Provider> {
if let payload::meta::Kind::Provider(kind, name) = meta.kind.clone() {
Some(Provider {
match (meta.tag, meta.kind.clone()) {
(payload::meta::Tag::Provides, payload::meta::Kind::Provider(kind, name)) => Some(Provider {
kind: dependency::Kind::from(kind),
name,
})
} else {
None
name: name.clone(),
}),
_ => None,
}
}

fn meta_conflict(meta: &payload::Meta) -> Option<Provider> {
match (meta.tag, meta.kind.clone()) {
(payload::meta::Tag::Conflicts, payload::meta::Kind::Provider(kind, name)) => Some(Provider {
kind: dependency::Kind::from(kind),
name: name.clone(),
}),
_ => None,
}
}

Expand Down
2 changes: 2 additions & 0 deletions moss/src/registry/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ mod test {
licenses: Default::default(),
dependencies: Default::default(),
providers: Default::default(),
conflicts: Default::default(),
uri: Default::default(),
hash: Default::default(),
download_size: Default::default(),
Expand Down Expand Up @@ -186,6 +187,7 @@ mod test {
licenses: Default::default(),
dependencies: Default::default(),
providers: Default::default(),
conflicts: Default::default(),
uri: Default::default(),
hash: Default::default(),
download_size: Default::default(),
Expand Down
Binary file added test/conflicts/italian-pizza-1-1-1-x86_64.stone
Binary file not shown.
17 changes: 17 additions & 0 deletions test/conflicts/italian-pizza.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#
# SPDX-FileCopyrightText: © 2020-2022 Serpent OS Developers
#
# SPDX-License-Identifier: Zlib
#
name : italian-pizza
version : "1"
release : 1
summary : A certain Italian pizza that hates pineapples
description : Me no like pineapples.
license : Zlib
homepage : https://github.com/serpent-os
conflicts :
- pineapple
install : |
mkdir -p %(installroot)/%(vendordir)
touch %(installroot)/%(vendordir)/italian-pizza
Binary file added test/conflicts/pineapple-1-1-1-x86_64.stone
Binary file not shown.
15 changes: 15 additions & 0 deletions test/conflicts/pineapple.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#
# SPDX-FileCopyrightText: © 2020-2022 Serpent OS Developers
#
# SPDX-License-Identifier: Zlib
#
name : pineapple
version : "1"
release : 1
summary : Delicious slices of pineapples
description : Can I be on your pizza?
license : Zlib
homepage : https://github.com/serpent-os
install : |
mkdir -p %(installroot)/%(vendordir)
touch %(installroot)/%(vendordir)/pienapple
Loading