Skip to content

Commit 1951f0a

Browse files
committed
breakit: init
This is just yet another PKGBREAK generator, providing better control over the package selecting process to user. This supports a PKGREBUILD syntax, for example: PKGREBUILD__ABI="sodep revdep \ path::usr/lib/python.*/.* \ path::.*\\.pyc \ section::lang-python" Will include: - All shared-library dependents. - All PKGDEP dependents. - All packages providing files under /usr/lib/pythonX.X - All packages under lang-python section. The third is important for upgrading Python versions. This also allows user to choose to bump REL of all broken packages but not committing, in order to avoid flashing the blinking firmware to their GPG keys. Another improvement is the ability to parse APML files more robustly. For example, breaker often generates PKGBREAK relationships like "XXX<=1:"${_UPSTREAM_VER}"", while BreakIt will print the evaluated version string in these cases. The two existing APML parsing libraries are both written in Rust and it seems to be a bit hard to invoke them in Python. Another feature is being capable to write the generated PKGBREAK back into defines files. With the power of LST, this will not break the existing code style of defines files. However, this is not an alternative to breaker, for that not all features in breaker are implemented here. I am not sure about what I missed but there must be something I missed. So use this tool at your own risk.
1 parent c37c960 commit 1951f0a

File tree

8 files changed

+3129
-0
lines changed

8 files changed

+3129
-0
lines changed

Diff for: breakit/Cargo.lock

+1,890
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: breakit/Cargo.toml

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
[package]
2+
name = "breakit"
3+
version = "0.1.0"
4+
edition = "2024"
5+
description = "BreakIt: AOSC OS package rebuild collector"
6+
authors = ["xtex <[email protected]>"]
7+
license = "GPL-3.0-or-later"
8+
homepage = "https://github.com/xtexChooser/pfu"
9+
repository = "https://github.com/xtexChooser/pfu.git"
10+
11+
[dependencies]
12+
anyhow = "1.0.95"
13+
bytes = "1.9.0"
14+
clap = { version = "4.5.27", features = ["env", "derive"] }
15+
console = "0.15.10"
16+
git2 = { version = "0.20.0", default-features = false, features = ["vendored-libgit2"] }
17+
libabbs = "0.1.0"
18+
regex = { version = "1.11.1", default-features = false, features = ["std", "perf"] }
19+
reqwest = { version = "0.12.12", features = ["json"] }
20+
serde = { version = "1.0.217", features = ["derive"] }
21+
tokio = { version = "1.43.0", features = ["full"] }
22+
zstd = "0.13.2"
23+
24+
[package.metadata.release]
25+
pre-release-commit-message = "{{crate_name}}: release {{version}}"
26+
consolidate-commits = false
27+
allow-branch = ["main"]
28+
sign-commit = true

Diff for: breakit/LICENSE

+674
Large diffs are not rendered by default.

Diff for: breakit/rustfmt.toml

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
hard_tabs = true
2+
max_width = 80

Diff for: breakit/src/lib.rs

+187
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
use std::{collections::HashSet, fs, str::FromStr, sync::Arc};
2+
3+
use anyhow::{Result, anyhow, bail};
4+
use libabbs::{
5+
apml::{
6+
ApmlContext,
7+
value::{array::StringArray, union::Union},
8+
},
9+
tree::{AbbsSourcePackage, AbbsTree},
10+
};
11+
use regex::Regex;
12+
use tokio::task::JoinSet;
13+
14+
mod pkgcontents;
15+
mod pkgsite;
16+
17+
#[derive(Debug)]
18+
pub struct PkgBreakContext {
19+
pub abbs: AbbsTree,
20+
pub http_client: reqwest::Client,
21+
}
22+
23+
impl PkgBreakContext {
24+
/// Selects a set of packages to be rebuilt.
25+
///
26+
/// Note that the produced list are not filtered
27+
/// and may include packages that have been dropped
28+
/// from the repository. However, it must never include
29+
/// the trigger package itself.
30+
pub async fn select(
31+
self: &Arc<Self>,
32+
package: &AbbsSourcePackage,
33+
kind: &str,
34+
) -> Result<HashSet<String>> {
35+
let kind = kind.to_ascii_uppercase();
36+
37+
let mut result = HashSet::new();
38+
let mut jobs = JoinSet::new();
39+
40+
let spec = fs::read_to_string(package.join("spec"))?;
41+
let spec_ctx = ApmlContext::eval_source(&spec)?;
42+
let pkgrebuild = spec_ctx
43+
.get(&format!("PKGREBUILD__{}", kind))
44+
.or_else(|| spec_ctx.get("PKGREBUILD"))
45+
.map(|val| StringArray::from(val.as_string()))
46+
.map(|val| {
47+
val.unwrap()
48+
.into_iter()
49+
.map(|dir| Directive::from_str(&dir))
50+
.collect()
51+
})
52+
.unwrap_or_else(|| {
53+
if kind == "ABI" {
54+
Ok(vec![
55+
Directive::PackageDependents(None),
56+
Directive::LibraryDependents(None),
57+
])
58+
} else {
59+
Ok(vec![])
60+
}
61+
})?;
62+
63+
for directive in pkgrebuild {
64+
// fast-path for pkg directives
65+
if let Directive::Package(pkg) = &directive {
66+
result.insert(pkg.to_string());
67+
continue;
68+
}
69+
70+
let ctx = self.clone();
71+
let package = package.clone();
72+
jobs.spawn(async move {
73+
ctx.select_directive(&package, &directive).await
74+
});
75+
}
76+
77+
while let Some(part) = jobs.join_next().await {
78+
let part = part?;
79+
let part = part?;
80+
result.extend(part);
81+
}
82+
83+
result.remove(package.name());
84+
Ok(result)
85+
}
86+
87+
/// Selects a set of packages to be rebuilt.
88+
pub async fn select_directive(
89+
&self,
90+
package: &AbbsSourcePackage,
91+
directive: &Directive,
92+
) -> Result<HashSet<String>> {
93+
match directive {
94+
Directive::LibraryDependents(pkg) => {
95+
let deps = pkgsite::find_deps(
96+
&self.http_client,
97+
&pkg.to_owned()
98+
.unwrap_or_else(|| package.name().to_string()),
99+
false,
100+
)
101+
.await?;
102+
Ok(deps)
103+
}
104+
Directive::PackageDependents(pkg) => {
105+
let deps = pkgsite::find_deps(
106+
&self.http_client,
107+
&pkg.to_owned()
108+
.unwrap_or_else(|| package.name().to_string()),
109+
true,
110+
)
111+
.await?;
112+
Ok(deps)
113+
}
114+
Directive::PathPattern(regex) => {
115+
pkgcontents::find_deps(&self.http_client, regex).await
116+
}
117+
Directive::Section(section) => {
118+
let mut result = HashSet::new();
119+
for package in self.abbs.section_packages(&section.into())? {
120+
for package in package.subpackages()? {
121+
result.insert(package.name()?);
122+
}
123+
}
124+
Ok(result)
125+
}
126+
Directive::Package(pkg) => Ok(HashSet::from([pkg.clone()])),
127+
Directive::PackagePattern(regex) => {
128+
let mut result = HashSet::new();
129+
for package in self.abbs.all_packages()? {
130+
for package in package.subpackages()? {
131+
let name = package.name()?;
132+
if regex.is_match(&name) {
133+
result.insert(name);
134+
}
135+
}
136+
}
137+
Ok(result)
138+
}
139+
}
140+
}
141+
}
142+
143+
/// A PKGREBUILD selector directive.
144+
#[derive(Debug, Clone)]
145+
pub enum Directive {
146+
/// Shared-library dependents.
147+
LibraryDependents(Option<String>),
148+
/// Reverse dependents.
149+
PackageDependents(Option<String>),
150+
/// Packages providing files matching the pattern.
151+
PathPattern(Regex),
152+
/// Packages in a certain section.
153+
Section(String),
154+
/// A certain package.
155+
Package(String),
156+
/// Packages matching the pattern.
157+
PackagePattern(Regex),
158+
}
159+
160+
impl FromStr for Directive {
161+
type Err = anyhow::Error;
162+
163+
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
164+
let un = Union::try_from(s)?;
165+
match un.tag.as_str() {
166+
"sodep" => Ok(Self::LibraryDependents(un.argument)),
167+
"revdep" => Ok(Self::PackageDependents(un.argument)),
168+
"path" => {
169+
Ok(Self::PathPattern(Regex::new(&un.argument.ok_or_else(
170+
|| anyhow!("path directive must have an argument"),
171+
)?)?))
172+
}
173+
"section" => Ok(Self::Section(un.argument.ok_or_else(|| {
174+
anyhow!("section directive must have an argument")
175+
})?)),
176+
"pkg" => Ok(Self::Package(un.argument.ok_or_else(|| {
177+
anyhow!("pkg directive must have an argument")
178+
})?)),
179+
"pkgpattern" => {
180+
Ok(Self::PackagePattern(Regex::new(&un.argument.ok_or_else(
181+
|| anyhow!("pkgpattern directive must have an argument"),
182+
)?)?))
183+
}
184+
_ => bail!("unsupported tag in PKGREBUILD directive"),
185+
}
186+
}
187+
}

0 commit comments

Comments
 (0)