Skip to content

Commit e257604

Browse files
authored
Add prettier and improve CI (#13)
* Add prettier and improve CI To keep a consistent style with multiple editors, and to avoid manual reflowing of text, we add prettier as formatter for markdown and yml files. I've intentionally left everything at default settings to rely on the work defining a good standard done by prettier's team and to avoid bikeshedding over the style. The only non-default option is prose wrapping for markdown files, which adds the equivalent of a line length limit as we have it in rust and yaml to markdown, too. I find too long lines hard to read and soft-wrap isn't consistently available (e.g. github PR diffs become hard to read), though we could remove that option, too. Prettier is enforced by CI. On CI, we also build the book for all PRs with an updated mdbook version and check compatibility with the latest mdbook version (non-blocking). * Adds names to jobs * Don't run CI twice for PRs
1 parent bc0e91e commit e257604

31 files changed

+1144
-1016
lines changed

.github/workflows/ci.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: CI
2+
on:
3+
push:
4+
branches: [main]
5+
pull_request:
6+
workflow_dispatch:
7+
8+
jobs:
9+
build:
10+
name: Build
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Checkout
14+
uses: actions/checkout@v3
15+
16+
- name: Setup mdBook
17+
uses: peaceiris/actions-mdbook@v2
18+
with:
19+
mdbook-version: "0.4.43"
20+
21+
- run: mdbook build
22+
23+
# Report when we become incompatible with latest mdbook, but don't fail the workflow
24+
build-mdbook-latest:
25+
name: Build (mdbook latest)
26+
runs-on: ubuntu-latest
27+
continue-on-error: true
28+
steps:
29+
- name: Checkout
30+
uses: actions/checkout@v3
31+
32+
- name: Setup mdBook
33+
uses: peaceiris/actions-mdbook@v2
34+
with:
35+
mdbook-version: "latest"
36+
37+
- run: mdbook build
38+
39+
lint:
40+
name: Lint
41+
runs-on: ubuntu-latest
42+
steps:
43+
- name: Checkout
44+
uses: actions/checkout@v3
45+
46+
- name: Prettier
47+
run: npx prettier --check .

.github/workflows/cloudflare.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
name: Cloudflare
12
on: [push]
23

34
jobs:
@@ -13,10 +14,9 @@ jobs:
1314

1415
# Build the guide to ./book
1516
- name: Setup mdBook
16-
uses: peaceiris/actions-mdbook@v1
17+
uses: peaceiris/actions-mdbook@v2
1718
with:
18-
mdbook-version: '0.4.37'
19-
# mdbook-version: 'latest'
19+
mdbook-version: "0.4.43"
2020
- run: mdbook build
2121

2222
- name: Publish to Cloudflare Pages

.prettierrc.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
proseWrap = "always"

README.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
# PubGrub guide
22

3-
Source for the PubGrub guide.
4-
The published version is available at https://pubgrub-rs-guide.pages.dev.
5-
This guide is made with [mdBook][mdbook].
6-
To compile it locally, install mdBook and run
3+
Source for the PubGrub guide. The published version is available at
4+
https://pubgrub-rs-guide.pages.dev. This guide is made with [mdBook][mdbook]. To
5+
compile it locally, install mdBook and run
76

87
```sh
98
mdbook serve
109
```
1110

11+
We use prettier for consistent formatting. The easiest way to run it is through
12+
npx:
13+
14+
```sh
15+
npx prettier --write --print-width 80 --prose-wrap always "**/*.md"
16+
npx prettier --write "**/*.yml"
17+
```
18+
1219
[mdbook]: https://github.com/rust-lang/mdBook

book.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
[book]
22
authors = ["Matthieu Pizenberg"]
33
language = "en"
4-
multilingual = false
5-
src = "src"
64
title = "PubGrub Guide"
75

86
[output.html]

src/contributing.md

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
# How can I contribute? Here are some ideas
22

3-
- Use it!
4-
Indeed there is quite some work left for custom
5-
dependency providers. So just using the crate, building
6-
you own dependency provider for your favorite programming language
7-
and letting us know how it turned out
8-
would be amazing feedback already!
3+
- Use it! Indeed there is quite some work left for custom dependency providers.
4+
So just using the crate, building you own dependency provider for your
5+
favorite programming language and letting us know how it turned out would be
6+
amazing feedback already!
97

10-
- Non failing extension for multiple versions.
11-
Currently, the solver works by allowing only one version per package.
12-
In some contexts however, we may want to not fail if multiple versions are required,
13-
and return instead multiple versions per package.
14-
Such situations may be for example allowing multiple major versions of the same crate.
15-
But it could be more general than that exact scenario.
8+
- Non failing extension for multiple versions. Currently, the solver works by
9+
allowing only one version per package. In some contexts however, we may want
10+
to not fail if multiple versions are required, and return instead multiple
11+
versions per package. Such situations may be for example allowing multiple
12+
major versions of the same crate. But it could be more general than that exact
13+
scenario.

src/internals/conflict_resolution.md

Lines changed: 33 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,41 @@
11
# Conflict resolution
22

3-
As stated before, a conflict is a satisfied incompatibility
4-
that we detected in the unit propagation loop.
5-
The goal of conflict resolution is to backtrack the partial solution
6-
such that we have the following guarantees:
3+
As stated before, a conflict is a satisfied incompatibility that we detected in
4+
the unit propagation loop. The goal of conflict resolution is to backtrack the
5+
partial solution such that we have the following guarantees:
76

8-
1. The root cause incompatibility of the conflict is almost satisfied
9-
(such that we can continue unit propagation).
7+
1. The root cause incompatibility of the conflict is almost satisfied (such that
8+
we can continue unit propagation).
109
2. The following derivations will be different than before conflict resolution.
1110

12-
Let the "satisfier" be the earliest assignment in the partial solution
13-
making the incompatibility fully satisfied by the partial solution up to that point.
14-
We know that we want to backtrack the partial solution at least previous to that assignment.
15-
Backtracking only makes sense if done at decision levels frontiers.
16-
As such the conflictual incompatibility can only become "almost satisfied"
17-
if there is only one package term related to incompatibility satisfaction
18-
at the decision level of that satisfier.
19-
When the satisfier is a decision this is trivial since all previous assignments
20-
are of lower decision levels.
21-
When the satisfier is a derivation however we need to check that property.
22-
We do that by computing the "previous satisfier" decision level.
23-
The previous satisfier is (if it exists) the earliest assignment
24-
previous to the satisfier such that the partial solution up to that point,
25-
plus the satisfier, makes the incompatibility satisfied.
26-
Once we found it, we know that property (1) is guaranteed as long as
27-
we backtrack to a decision level between the one of the previous satisfier
28-
and the one of the satisfier, as long as these are different.
11+
Let the "satisfier" be the earliest assignment in the partial solution making
12+
the incompatibility fully satisfied by the partial solution up to that point. We
13+
know that we want to backtrack the partial solution at least previous to that
14+
assignment. Backtracking only makes sense if done at decision levels frontiers.
15+
As such the conflictual incompatibility can only become "almost satisfied" if
16+
there is only one package term related to incompatibility satisfaction at the
17+
decision level of that satisfier. When the satisfier is a decision this is
18+
trivial since all previous assignments are of lower decision levels. When the
19+
satisfier is a derivation however we need to check that property. We do that by
20+
computing the "previous satisfier" decision level. The previous satisfier is (if
21+
it exists) the earliest assignment previous to the satisfier such that the
22+
partial solution up to that point, plus the satisfier, makes the incompatibility
23+
satisfied. Once we found it, we know that property (1) is guaranteed as long as
24+
we backtrack to a decision level between the one of the previous satisfier and
25+
the one of the satisfier, as long as these are different.
2926

30-
If the satisfier and previous satisfier decisions levels are the same,
31-
we cannot guarantee (1) for that incompatibility after backtracking.
32-
Therefore, the key of conflict resolution is to derive a new incompatibility
33-
for which we will be able to guarantee (1).
34-
And we have seen how to do that with the
35-
[rule of resolution](incompatibilities.md#rule-of-resolution).
36-
We will derive a new incompatibility called the "prior cause"
37-
as the resolvent of the current incompatibility and
38-
the incompatibility which is the cause of the satisfier.
39-
If necessary, we repeat that procedure until finding an incompatibility,
40-
called the "root cause" for which we can guarantee that it will
41-
be almost satisfied after backtracking (1).
27+
If the satisfier and previous satisfier decisions levels are the same, we cannot
28+
guarantee (1) for that incompatibility after backtracking. Therefore, the key of
29+
conflict resolution is to derive a new incompatibility for which we will be able
30+
to guarantee (1). And we have seen how to do that with the
31+
[rule of resolution](incompatibilities.md#rule-of-resolution). We will derive a
32+
new incompatibility called the "prior cause" as the resolvent of the current
33+
incompatibility and the incompatibility which is the cause of the satisfier. If
34+
necessary, we repeat that procedure until finding an incompatibility, called the
35+
"root cause" for which we can guarantee that it will be almost satisfied after
36+
backtracking (1).
4237

43-
Now the question is where do we cut?
44-
Is there a reason we cut at the previous satisfier decision level?
45-
Is it to guarantee (2)? Would that not be guaranteed if we picked
46-
another decision level? Is it because backtracking further back
38+
Now the question is where do we cut? Is there a reason we cut at the previous
39+
satisfier decision level? Is it to guarantee (2)? Would that not be guaranteed
40+
if we picked another decision level? Is it because backtracking further back
4741
will reduce the number of potential conflicts?

src/internals/incompatibilities.md

Lines changed: 62 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,25 @@
11
# Incompatibilities
22

3-
43
## Definition
54

6-
Incompatibilities are called "nogoods" in [CDNL-ASP][ass] terminology.
7-
**An incompatibility is a [conjunction][conjunction] of package terms that must
8-
be evaluated false**, meaning at least one package term must be evaluated false.
9-
Otherwise we say that the incompatibility has been "satisfied".
10-
Satisfied incompatibilities represent conflicts and thus
11-
the goal of the PubGrub algorithm is to build a solution
12-
such that none of the produced incompatibilities are ever satisfied.
13-
If one incompatibility becomes satisfied at some point,
14-
the algorithm finds the root cause of it and backtracks the partial solution
15-
before the decision at the origin of that root cause.
16-
17-
> Remark: incompatibilities (nogoods) are the opposite of clauses
18-
> in traditional conflict-driven clause learning ([CDCL][cdcl])
19-
> which are disjunctions of literals that must be evaluated true,
20-
> so have at least one literal evaluated true.
5+
Incompatibilities are called "nogoods" in [CDNL-ASP][ass] terminology. **An
6+
incompatibility is a [conjunction][conjunction] of package terms that must be
7+
evaluated false**, meaning at least one package term must be evaluated false.
8+
Otherwise we say that the incompatibility has been "satisfied". Satisfied
9+
incompatibilities represent conflicts and thus the goal of the PubGrub algorithm
10+
is to build a solution such that none of the produced incompatibilities are ever
11+
satisfied. If one incompatibility becomes satisfied at some point, the algorithm
12+
finds the root cause of it and backtracks the partial solution before the
13+
decision at the origin of that root cause.
14+
15+
> Remark: incompatibilities (nogoods) are the opposite of clauses in traditional
16+
> conflict-driven clause learning ([CDCL][cdcl]) which are disjunctions of
17+
> literals that must be evaluated true, so have at least one literal evaluated
18+
> true.
2119
>
22-
> The gist of CDCL is that it builds a solution to satisfy a
23-
> [conjunctive normal form][cnf] (conjunction of clauses) while
24-
> CDNL builds a solution to unsatisfy a [disjunctive normal form][dnf]
25-
> (disjunction of nogoods).
20+
> The gist of CDCL is that it builds a solution to satisfy a [conjunctive normal
21+
> form][cnf] (conjunction of clauses) while CDNL builds a solution to unsatisfy
22+
> a [disjunctive normal form][dnf] (disjunction of nogoods).
2623
>
2724
> In addition, PubGrub is a lazy CDNL algorithm since the disjunction of nogoods
2825
> (incompatibilities) is built on the fly with the solution.
@@ -33,84 +30,77 @@ before the decision at the origin of that root cause.
3330
[cnf]: https://en.wikipedia.org/wiki/Conjunctive_normal_form
3431
[dnf]: https://en.wikipedia.org/wiki/Disjunctive_normal_form
3532

36-
In this guide, we will note incompatibilities with curly braces.
37-
An incompatibility containing one term \\(T_a\\) for package \\(a\\)
38-
and one term \\(T_b\\) for package \\(b\\) will be noted
33+
In this guide, we will note incompatibilities with curly braces. An
34+
incompatibility containing one term \\(T_a\\) for package \\(a\\) and one term
35+
\\(T_b\\) for package \\(b\\) will be noted
3936

4037
\\[ \\{ a: T_a, b: T_b \\}. \\]
4138

42-
> Remark: in a more "mathematical" setting, we would probably have noted
43-
> \\( T_a \land T_b \\), but the chosen notation maps well
44-
> with the representation of incompatibilities as hash maps.
45-
39+
> Remark: in a more "mathematical" setting, we would probably have noted \\( T_a
40+
> \land T_b \\), but the chosen notation maps well with the representation of
41+
> incompatibilities as hash maps.
4642
4743
## Properties
4844

49-
**Packages only appear once in an incompatibility**.
50-
Since an incompatibility is a conjunction,
51-
multiple terms for the same package are merged with the intersection of those terms.
45+
**Packages only appear once in an incompatibility**. Since an incompatibility is
46+
a conjunction, multiple terms for the same package are merged with the
47+
intersection of those terms.
5248

53-
**Terms that are always satisfied can be removed from an incompatibility**.
54-
We previously explained that the term \\( \neg [\varnothing] \\) is always evaluated true.
55-
As a consequence, it can safely be removed from the conjunction of terms that is the incompatibility.
49+
**Terms that are always satisfied can be removed from an incompatibility**. We
50+
previously explained that the term \\( \neg [\varnothing] \\) is always
51+
evaluated true. As a consequence, it can safely be removed from the conjunction
52+
of terms that is the incompatibility.
5653

5754
\\[ \\{ a: T_a, b: T_b, c: \neg [\varnothing] \\} = \\{ a: T_a, b: T_b \\} \\]
5855

59-
**Dependencies can be expressed as incompatibilities**.
60-
Saying that versions in range \\( r_a \\) of package \\( a \\)
61-
depend on versions in range \\( r_b \\) of package \\( b \\)
62-
can be expressed by the incompatibility
56+
**Dependencies can be expressed as incompatibilities**. Saying that versions in
57+
range \\( r_a \\) of package \\( a \\) depend on versions in range \\( r_b \\)
58+
of package \\( b \\) can be expressed by the incompatibility
6359

6460
\\[ \\{ a: [r_a], b: \neg [r_b] \\}. \\]
6561

66-
6762
## Unit propagation
6863

6964
If all terms but one of an incompatibility are satisfied by a partial solution,
70-
we can deduce that the remaining unsatisfied term must be evaluated false.
71-
We can thus derive a new unit term for the partial solution
72-
which is the negation of the remaining unsatisfied term of the incompatibility.
73-
For example, if we have the incompatibility
74-
\\( \\{ a: T_a, b: T_b, c: T_c \\} \\)
75-
and if \\( T_a \\) and \\( T_b \\) are satisfied by terms in the partial solution
76-
then we can derive that the term \\( \overline{T_c} \\) can be added for package \\( c \\)
65+
we can deduce that the remaining unsatisfied term must be evaluated false. We
66+
can thus derive a new unit term for the partial solution which is the negation
67+
of the remaining unsatisfied term of the incompatibility. For example, if we
68+
have the incompatibility \\( \\{ a: T_a, b: T_b, c: T_c \\} \\) and if \\( T_a
69+
\\) and \\( T_b \\) are satisfied by terms in the partial solution then we can
70+
derive that the term \\( \overline{T_c} \\) can be added for package \\( c \\)
7771
in the partial solution.
7872

79-
8073
## Rule of resolution
8174

82-
Intuitively, we are able to deduce things such as if package \\( a \\)
83-
depends and package \\( b \\) which depends on package \\( c \\),
84-
then \\( a \\) depends on \\( c \\).
85-
With incompatibilities, we would note
75+
Intuitively, we are able to deduce things such as if package \\( a \\) depends
76+
and package \\( b \\) which depends on package \\( c \\), then \\( a \\) depends
77+
on \\( c \\). With incompatibilities, we would note
8678

87-
\\[ \\{ a: T_a, b: \overline{T_b} \\}, \quad
88-
\\{ b: T_b, c: \overline{T_c} \\} \quad
89-
\Rightarrow \quad \\{ a: T_a, c: \overline{T_c} \\}. \\]
79+
\\[ \\{ a: T_a, b: \overline{T_b} \\}, \quad \\{ b: T_b, c: \overline{T_c} \\}
80+
\quad \Rightarrow \quad \\{ a: T_a, c: \overline{T_c} \\}. \\]
9081

91-
This is the simplified version of the rule of resolution.
92-
For the generalization, let's reuse the "more mathematical" notation of conjunctions
93-
for incompatibilities \\( T_a \land T_b \\) and the above rule would be
82+
This is the simplified version of the rule of resolution. For the
83+
generalization, let's reuse the "more mathematical" notation of conjunctions for
84+
incompatibilities \\( T_a \land T_b \\) and the above rule would be
9485

95-
\\[ T_a \land \overline{T_b}, \quad
96-
T_b \land \overline{T_c} \quad
97-
\Rightarrow \quad T_a \land \overline{T_c}. \\]
86+
\\[ T_a \land \overline{T_b}, \quad T_b \land \overline{T_c} \quad \Rightarrow
87+
\quad T_a \land \overline{T_c}. \\]
9888

9989
In fact, the above rule can also be expressed as follows
10090

101-
\\[ T_a \land \overline{T_b}, \quad
102-
T_b \land \overline{T_c} \quad
103-
\Rightarrow \quad T_a \land (\overline{T_b} \lor T_b) \land \overline{T_c} \\]
91+
\\[ T_a \land \overline{T_b}, \quad T_b \land \overline{T_c} \quad \Rightarrow
92+
\quad T_a \land (\overline{T_b} \lor T_b) \land \overline{T_c} \\]
10493

105-
since for any term \\( T \\), the disjunction \\( T \lor \overline{T} \\) is always true.
106-
In general, for any two incompatibilities \\( T_a^1 \land T_b^1 \land \cdots \land T_z^1 \\)
107-
and \\( T_a^2 \land T_b^2 \land \cdots \land T_z^2 \\) we can deduce a third,
108-
called the resolvent whose expression is
94+
since for any term \\( T \\), the disjunction \\( T \lor \overline{T} \\) is
95+
always true. In general, for any two incompatibilities \\( T_a^1 \land T_b^1
96+
\land \cdots \land T_z^1 \\) and \\( T_a^2 \land T_b^2 \land \cdots \land T_z^2
97+
\\) we can deduce a third, called the resolvent whose expression is
10998

110-
\\[ (T_a^1 \lor T_a^2) \land (T_b^1 \land T_b^2) \land \cdots \land (T_z^1 \land T_z^2). \\]
99+
\\[ (T_a^1 \lor T_a^2) \land (T_b^1 \land T_b^2) \land \cdots \land (T_z^1 \land
100+
T_z^2). \\]
111101

112-
In that expression, only one pair of package terms is regrouped as a union (a disjunction),
113-
the others are all intersected (conjunction).
114-
If a term for a package does not exist in one incompatibility,
115-
it can safely be replaced by the term \\( \neg [\varnothing] \\) in the expression above
116-
as we have already explained before.
102+
In that expression, only one pair of package terms is regrouped as a union (a
103+
disjunction), the others are all intersected (conjunction). If a term for a
104+
package does not exist in one incompatibility, it can safely be replaced by the
105+
term \\( \neg [\varnothing] \\) in the expression above as we have already
106+
explained before.

0 commit comments

Comments
 (0)