fix(orphans): handle alternative providers to eliminate false positives #13031
No reviewers
releng/Owners
Labels
No labels
after freeze
automation
backlog
blocked
change-ack
change-nak
change-noreleng
changes
Closed As
Can't Fix
Closed As
Duplicate
Closed As
Fixed
Closed As
Fixed with Explanation
Closed As
Get back later
Closed As
Grooming
Closed As
Insufficient data
Closed As
Invalid
Closed As
It's all good
Closed As
taiga
Closed As
upstream
dev
docs
easyfix
epel
f26
f27
f28
f29
f30
f31
f32
f33
f34
f35
f36
f37
f38
f39
f40
f41
f42
f43
f44
fedora
groomed
high-gain
high-trouble
in-progress
in-review
investigation
legal
low-gain
low-trouble
mass rebuild
medium-gain
medium-trouble
meeting
mini-initiative
new_artifact
ops
pdc_retirement
rawhide
RCA
review
script
sidetarget
sprint-0
sprint-1
sprint-2
unfrozen
waiting on external
Backlog Status
Needs Review
Backlog Status
Ready
chore
documentation
points
01
points
02
points
03
points
05
points
08
points
13
Priority
High
Priority
Low
Priority
Medium
Sprint Status
Blocked
Sprint Status
Done
Sprint Status
In Progress
Sprint Status
Review
Sprint Status
To Do
Technical Debt
Work Item
Bug
Work Item
Epic
Work Item
Spike
Work Item
Task
Work Item
User Story
No milestone
No project
No assignees
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
releng/tooling!13031
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "jnsamyak/tooling:fix#11460"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The problem was that the dependency checker didn't account for alternative providers and rich dependencies.
This fix adds two helper methods to properly handle these cases:
is_requirement_satisfied_by_others() - checks if a requirement has
alternative providers that aren't being removed. This prevents
flagging dependencies when other valid providers exist.
has_all_requirements_satisfied() - verifies that a package's
complete set of requirements can be satisfied without the orphaned
packages. This properly handles rich dependencies with OR clauses.
Open to discussing this approach.
Fixes: releng/tickets#11460
Signed-off-by: Samyak Jain samyak.jn11@gmail.com
Could my rmdepcheck tool be useful here?
You can use it like this to determine the consequences of removing a package from an arbitrary (set of) repositor(y/ies):
it does this by diffing repoclosure output, which means it uses the "real" dnf/libsolv dependency resolution path, meaning its results should be consistent and correct and the tool doesn't have to laboriously re-invent dependency resolution logic.
@adamwill, yeah, I actually did want to talk to you about replacing this script's dep solving code with rmdepcheck. Currently, I don't think it'd work here because this script needs a structured mapping of which other packages would be broken by which orphaned packages so it can get a list of affected package maintainers. rmdepcheck just prints a textual summary of the resolution failures AFAICT.
Thanks for looking into to this! I was aware of the issue but never got around to looking into it because I was worried about performance impacts of additional queries and wanted to prioritize a complete rewrite to simplify the code or at least a libdnf5 port of the existing code. But hopefully, we can find a workable solution in the meantime.
I am concerned that this PR is not the correct solution and will create false negatives, though. I left comments explaining the issue in more detail.
In any case, it would be helpful if you ran the old and new versions of the script and compared execution time to see what kind of performance impact the changes would have.
@ -401,0 +423,4 @@"""# Check each requirement of the packagefor req in package.requires:req_str = str(req).split()[0] # Get base requirement nameThis logic does not seem quite right to me, and I believe it'll create false negatives. You can't just discard the version constraints and use the "base requirement name" and then assume that another package could satisfy the requirement. Let's say
python3-fooandpython3-foo2both Providepython3dist(foo), but the former ispython3dist(foo) = 3and the latter ispython3dist(foo) = 2. Let's say thatpython3-foo2is an orphan. Let's also say that packagebarRequirespython3dist(foo) < 3. That means that onlypython3-foo2(the provider ofpython3dist(foo) = 2) would be able to satisfybar's dependency. But this code just sees that a package other than the orphanedpython3-foo2can satisfy the base requirement name and removesbarfrom the affected packages list even though the version constraints in the full requirement string makes it so the other package cannot satisfy it.Also, the splitting on spaces like this will break rich dependencies with parentheses and create an invalid requirement string.
If this is changed to operate on the full requirement string and the
is_requirement_satisfied_by_others()check is completely removed, I think this would actually solve the issue.@ -468,0 +493,4 @@# Check if this provide is satisfied by other packages# (not the orphaned package or other packages being removed)if self.is_requirement_satisfied_by_others(base_provide, rpms):# This requirement has alternative providers, skip itEven if the
base_providedoes have alternative providers, we can't skip it here yet because different packages can have version constraints that disqualify the other providers. See the example case in my comment ondef has_all_requirements_satisfied().@gotmax23 wrote in #13031 (comment):
rmdepcheck has a JSON output mode:
The first element of each output dep tuple is a package NVR. Currently this is parsed out of
dnf repoclosuretext output, but I have a ticket to switch to usingdnf repoclosureJSON output once it's more widely available. I need to document the output format, but you'd always want thenewerrorslist for this purpose.It doesn't currently infer source packages from binary for you, but it could do that, and I've filed myself a ticket to add it as a convenience feature.
If you run
rmdepcheck --removesand specify multiple removes it can't really tell you which of them causes a given dep issue without a bunch of extra fancy stuff I don't really want to put in. But it runs very fast (~30 seconds per run, usually) so you can probably just run it on one package at a time. I could actually implement a sort of 'serial mode' where it would run on one package at a time internally so it could give you info per removal, I guess; that would be even faster as it wouldn't have to redo the metadata downloads which it does on each run.another option would be to just borrow the "repo modification logic" out of rmdepcheck and use it here, I guess; it's very simple these days. I do vaguely have the idea that I could make it exportable from rmdepcheck for other things to use also, I'd have to look at how that should work.
--removesmode useful forfind_unblocked_orphans.pyscript #35View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.Merge
Merge the changes and update on Forgejo.Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.