<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Things that I work on in Debian - pdo</title><link href="https://people.debian.org/~nthykier/blog/" rel="alternate"></link><link href="https://people.debian.org/~nthykier/blog/feeds/pdo.atom.xml" rel="self"></link><id>https://people.debian.org/~nthykier/blog/</id><updated>2025-03-09T12:05:00+00:00</updated><entry><title>Improving Debian packaging in Kate</title><link href="https://people.debian.org/~nthykier/blog/2025/improving-debian-packaging-in-kate.html" rel="alternate"></link><published>2025-03-09T12:05:00+00:00</published><updated>2025-03-09T12:05:00+00:00</updated><author><name>Niels Thykier</name></author><id>tag:people.debian.org,2025-03-09:/~nthykier/blog/2025/improving-debian-packaging-in-kate.html</id><content type="html">&lt;p&gt;The other day, I noted that the &lt;tt class="docutils literal"&gt;emacs&lt;/tt&gt; integration with &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; stopped working.
After debugging for a while, I realized that &lt;tt class="docutils literal"&gt;emacs&lt;/tt&gt; no longer sent the &lt;tt class="docutils literal"&gt;didOpen&lt;/tt&gt;
notification that is expected of it, which confused &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;. At this point, I was
already several hours into the debugging and I noted there was some discussions on
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;debian-devel&lt;/span&gt;&lt;/tt&gt; about &lt;tt class="docutils literal"&gt;emacs&lt;/tt&gt; and byte compilation not working. So I figured I would
shelve the &lt;tt class="docutils literal"&gt;emacs&lt;/tt&gt; problem for now.&lt;/p&gt;
&lt;p&gt;But I needed an LSP capable editor and with my &lt;tt class="docutils literal"&gt;vi&lt;/tt&gt; skills leaving much to be desired,
I skipped out on &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;vim-youcompleteme&lt;/span&gt;&lt;/tt&gt;. Instead, I pulled out &lt;tt class="docutils literal"&gt;kate&lt;/tt&gt;, which I had not
been using for years. It had LSP support, so it would fine, right?&lt;/p&gt;
&lt;p&gt;Well, no. Turns out that &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; LSP support had some assumptions that worked for
&lt;tt class="docutils literal"&gt;emacs&lt;/tt&gt; but not &lt;tt class="docutils literal"&gt;kate&lt;/tt&gt;. Plus once you start down the rabbit hole, you stumble on
things you missed previously.&lt;/p&gt;
&lt;div class="section" id="getting-started"&gt;
&lt;h2&gt;Getting started&lt;/h2&gt;
&lt;p&gt;First order of business was to tell &lt;tt class="docutils literal"&gt;kate&lt;/tt&gt; about &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;. Conveniently, &lt;tt class="docutils literal"&gt;kate&lt;/tt&gt; has
a configuration tab for adding language servers in a JSON format right next to the tab where
you can see its configuration for built-in LSP  (also in JSON format9. So a quick bit of
copy-paste magic and that was done.&lt;/p&gt;
&lt;p&gt;Yesterday, I opened an MR against upstream to have the configuration added
(&lt;a class="reference external" href="https://invent.kde.org/utilities/kate/-/merge_requests/1748"&gt;https://invent.kde.org/utilities/kate/-/merge_requests/1748&lt;/a&gt;) and they already merged it.
Today, I then filed a wishlist against &lt;tt class="docutils literal"&gt;kate&lt;/tt&gt; in Debian to have the Debian maintainers
cherry-pick it, so it works out of the box for Trixie (&lt;a class="reference external" href="https://bugs.debian.org/1099876"&gt;https://bugs.debian.org/1099876&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;So far so good.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="inlay-hint-woes"&gt;
&lt;h2&gt;Inlay hint woes&lt;/h2&gt;
&lt;p&gt;Since July (2024), &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; has support for &lt;tt class="docutils literal"&gt;Inlay hints&lt;/tt&gt;. They are basically small
bits of text that the LSP server can ask the editor to inject into the text to provide
hints to the reader.&lt;/p&gt;
&lt;p&gt;Typically, you see them used to provide typing hints, where the editor or the underlying
LSP server has figured out the type of a variable or expression that you did not
explicitly type. Another common use case is to inject the parameter name for positional
arguments when calling a function, so the user do not have to count the position to
figure out which value is passed as which parameter.&lt;/p&gt;
&lt;p&gt;In &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;, I have been using the &lt;tt class="docutils literal"&gt;Inlay hints&lt;/tt&gt; to show inherited fields in
&lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt;. As an example, if you have a definition like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;Source: foo-src
Section: devel
Priority: optional

Package: foo-bin
Architecture: any
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;foo-bin&lt;/span&gt;&lt;/tt&gt; inherits the &lt;tt class="docutils literal"&gt;Section&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;Priority&lt;/tt&gt; field since it does not supply
its own. Previously, &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; would that by injecting the fields themselves and their
value just below the &lt;tt class="docutils literal"&gt;Package&lt;/tt&gt; field as if you had typed them out directly. The editor
always renders &lt;tt class="docutils literal"&gt;Inlay hints&lt;/tt&gt; distinctly from regular text, so there was no risk of
confusion and it made the text look like a valid &lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt; file end to end. The
result looked something like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;Source: foo-src
Section: devel
Priority: optional

Package: foo-bin
Section: devel
Priority: optional
Architecture: any
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With the second instances of &lt;tt class="docutils literal"&gt;Section&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;Priority&lt;/tt&gt; being rendered differently than
its surrendering (usually faded or colorlessly).&lt;/p&gt;
&lt;p&gt;Unfortunately, &lt;tt class="docutils literal"&gt;kate&lt;/tt&gt; did not like injecting &lt;tt class="docutils literal"&gt;Inlay hints&lt;/tt&gt; with a newline in them,
which was needed for this trick. Reading into the LSP specs, it says nothing about
multi-line &lt;tt class="docutils literal"&gt;Inlay hints&lt;/tt&gt; being a thing and I figured I would see this problem again
with other editors if I left it be.&lt;/p&gt;
&lt;p&gt;I ended up changing the &lt;tt class="docutils literal"&gt;Inlay hints&lt;/tt&gt; to be placed at the end of the &lt;tt class="docutils literal"&gt;Package&lt;/tt&gt; field
and then included surrounding &lt;tt class="docutils literal"&gt;()&lt;/tt&gt; for better visuals. So now, it looks like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;Source: foo-src
Section: devel
Priority: optional

Package: foo-bin  (Section: devel)  (Priority: optional)
Architecture: any
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Unfortunately, it is no longer 1:1 with the underlying syntax which I liked about the
previous one. But it works in more editors and is still explicit. I also removed the
&lt;tt class="docutils literal"&gt;Inlay hint&lt;/tt&gt; for the &lt;tt class="docutils literal"&gt;Homepage&lt;/tt&gt; field. It takes too much space and I have yet to
meet someone missing it in the binary stanza.&lt;/p&gt;
&lt;p&gt;If you have any better ideas for how to render it, feel free to reach out to me.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="spurious-completion-and-hover"&gt;
&lt;h2&gt;Spurious completion and hover&lt;/h2&gt;
&lt;p&gt;As I was debugging the &lt;tt class="docutils literal"&gt;Inlay hints&lt;/tt&gt;, I wanted to do a quick restart of &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; after
each fix. Then I would trigger a small change to the document to ensure &lt;tt class="docutils literal"&gt;kate&lt;/tt&gt; would
request an update from &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; to render the &lt;tt class="docutils literal"&gt;Inlay hints&lt;/tt&gt; with the new code.&lt;/p&gt;
&lt;p&gt;The full outgoing payloads are sent via the logs to the client, so it was really about
minimizing which LSP requests are sent to &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;. Notably, two cases would flood the
log:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Completion requests. These are triggered by typing anything at all and since I wanted
to a change, I could not avoid this. So here it was about making sure there would be
nothing to complete, so the result was a small as possible.&lt;/li&gt;
&lt;li&gt;Hover doc requests. These are triggered by mouse hovering over field, so this was
mostly about ensuring my mouse movement did not linger over any field on the way
between restarting the LSP server and scrolling the log in &lt;tt class="docutils literal"&gt;kate&lt;/tt&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;In my infinite wisdom, I chose to make a comment line where I would do the change. I figured
it would neuter the completion requests completely and it should not matter if my cursor
landed on the comment as there would be no hover docs for comments either.&lt;/p&gt;
&lt;p&gt;Unfortunately for me, &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; would ignore the fact that it was on a comment line.
Instead, it would find the next field after the comment line and try to complete based on
that. Normally you do not see this, because the editor correctly identifies that none of
the completion suggestions start with a &lt;tt class="docutils literal"&gt;\#&lt;/tt&gt;, so they are all discarded.&lt;/p&gt;
&lt;p&gt;But it was pretty annoying for the debugging, so now &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; has been told to explicitly
stop these requests early on comment lines.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="hover-docs-for-packages"&gt;
&lt;h2&gt;Hover docs for packages&lt;/h2&gt;
&lt;p&gt;I added a feature in &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; where you can hover over package names in your relationship
fields (such as &lt;tt class="docutils literal"&gt;Depends&lt;/tt&gt;) and &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; will render a small snippet about it based on
data from your local APT cache.&lt;/p&gt;
&lt;p&gt;This doc is then handed to the editor and tagged as &lt;tt class="docutils literal"&gt;markdown&lt;/tt&gt; provided the editor supports
&lt;tt class="docutils literal"&gt;markdown&lt;/tt&gt; rendering. Both &lt;tt class="docutils literal"&gt;emacs&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;kate&lt;/tt&gt; support &lt;tt class="docutils literal"&gt;markdown&lt;/tt&gt;. However, not all
&lt;tt class="docutils literal"&gt;markdown&lt;/tt&gt; renderings are equal. Notably, &lt;tt class="docutils literal"&gt;emacs&lt;/tt&gt;'s rendering does not reformat the text
into paragraphs. In a sense, &lt;tt class="docutils literal"&gt;emacs&lt;/tt&gt; rendering works a bit like &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;&amp;lt;pre&amp;gt;...&amp;lt;/pre&amp;gt;&lt;/span&gt;&lt;/tt&gt; except
it does a bit of fancy rendering inside the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;&amp;lt;pre&amp;gt;...&amp;lt;/pre&amp;gt;&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;On the other hand, &lt;tt class="docutils literal"&gt;kate&lt;/tt&gt; seems to convert the &lt;tt class="docutils literal"&gt;markdown&lt;/tt&gt; to HTML and then throw the result
into an HTML render engine. Here it is important to remember that not all newlines are equal
in &lt;tt class="docutils literal"&gt;markdown&lt;/tt&gt;. A &lt;tt class="docutils literal"&gt;Foo&amp;lt;newline&amp;gt;Bar&lt;/tt&gt; is treated as one &amp;quot;paragraph&amp;quot; (&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;&amp;lt;p&amp;gt;...&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;/tt&gt;) and the HTML
render happily renders this as single line &lt;tt class="docutils literal"&gt;Foo Bar&lt;/tt&gt; provided there is sufficient width to
do so.&lt;/p&gt;
&lt;p&gt;A couple of extra newlines made wonders for the &lt;tt class="docutils literal"&gt;kate&lt;/tt&gt; rendering, but I have a feeling this
is not going to be the last time the hover docs will need some tweaking for prettification.
Feel free to reach out if you spot a weirdly rendered hover doc somewhere.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="making-quickfixes-available-in-kate"&gt;
&lt;h2&gt;Making quickfixes available in &lt;tt class="docutils literal"&gt;kate&lt;/tt&gt;&lt;/h2&gt;
&lt;p&gt;Quickfixes are treated as generic code actions in the LSP specs. Each code action has a &amp;quot;type&amp;quot;
(&lt;tt class="docutils literal"&gt;kind&lt;/tt&gt; in the LSP lingo), which enables the editor to group the actions accordingly or
filter by certain types of code actions.&lt;/p&gt;
&lt;p&gt;The design in the specs leads to the following flow:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;The LSP server provides the editor with diagnostics (there are multiple ways to trigger
this, so we will keep this part simple).&lt;/li&gt;
&lt;li&gt;The editor renders them to the user and the user chooses to interact with one of them.&lt;/li&gt;
&lt;li&gt;The interaction makes the editor asks the LSP server, which code actions are available
at that location (optionally with filter to only see quickfixes).&lt;/li&gt;
&lt;li&gt;The LSP server looks at the provided range and is expected to return the relevant
quickfixes here.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;This flow is really annoying from a LSP server writer point of view. When you do the diagnostics
(in step 1), you tend to already know what the possible quickfixes would be. The LSP spec
authors realized this at some point, so there are two features the editor provides to simplify
this.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;In the editor request for code actions, the editor is expected to provide the diagnostics
that they received from the server. Side note: I cannot quite tell if this is optional or
required from the spec.&lt;/li&gt;
&lt;li&gt;The editor can provide support for remembering a &lt;tt class="docutils literal"&gt;data&lt;/tt&gt; member in each diagnostic. The
server can then store arbitrary information in that member, which they will see again in
the code actions request. Again, provided that the editor supports this optional feature.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;All the quickfix logic in &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; so far has hinged on both of these two features.&lt;/p&gt;
&lt;p&gt;As life would have it, &lt;tt class="docutils literal"&gt;kate&lt;/tt&gt; provides &lt;strong&gt;neither&lt;/strong&gt; of them.&lt;/p&gt;
&lt;p&gt;Which meant I had to teach &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; to keep track of its diagnostics on its own. The plus side
is that makes it easier to support &amp;quot;pull diagnostics&amp;quot; down the line, since it requires a similar
feature. Additionally, it also means that quickfixes are now available in more editors. For
consistency, &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; logic is now always used rather than relying on the editor support
when present.&lt;/p&gt;
&lt;p&gt;The downside is that I had to spend hours coming up with and debugging a way to find the
diagnostics that overlap with the range provided by the editor. The most difficult part was keeping
the logic straight and getting the runes correct for it.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="making-the-quickfixes-actually-work"&gt;
&lt;h2&gt;Making the quickfixes actually work&lt;/h2&gt;
&lt;p&gt;With all of that, &lt;tt class="docutils literal"&gt;kate&lt;/tt&gt; would show the quickfixes for diagnostics from &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; and you could
use them too. However, they would always apply twice with suboptimal outcome as a result.&lt;/p&gt;
&lt;p&gt;The LSP spec has multiple ways of defining what need to be changed in response to activating a
code action.  In &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;, all edits are currently done via the &lt;tt class="docutils literal"&gt;WorkspaceEdit&lt;/tt&gt; type. It
has two ways of defining the changes. Either via &lt;tt class="docutils literal"&gt;changes&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;documentChanges&lt;/tt&gt; with
&lt;tt class="docutils literal"&gt;documentChanges&lt;/tt&gt; being the preferred one if both parties support this.&lt;/p&gt;
&lt;p&gt;I originally read that as I was allowed to provide both and the editor would pick the one it
preferred. However, after seeing &lt;tt class="docutils literal"&gt;kate&lt;/tt&gt; blindly use both when they are present, I reviewed
the spec and it does say &amp;quot;The edit should either provide &lt;tt class="docutils literal"&gt;changes&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;documentChanges&lt;/tt&gt;&amp;quot;,
so I think that one is on me.&lt;/p&gt;
&lt;p&gt;None of the changes in &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; currently require &lt;tt class="docutils literal"&gt;documentChanges&lt;/tt&gt;, so I went with just
using &lt;tt class="docutils literal"&gt;changes&lt;/tt&gt; for now despite it not being preferred.  I cannot figure
out the logic of whether an editor supports &lt;tt class="docutils literal"&gt;documentChanges&lt;/tt&gt;. As I read the notes for this
part of the spec, my understanding is that &lt;tt class="docutils literal"&gt;kate&lt;/tt&gt; does not announce its support for
&lt;tt class="docutils literal"&gt;documentChanges&lt;/tt&gt; but it clearly uses them when present. Therefore, I decided to keep it
simple for now until I have time to dig deeper.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="remaining-limitations-with-kate"&gt;
&lt;h2&gt;Remaining limitations with &lt;tt class="docutils literal"&gt;kate&lt;/tt&gt;&lt;/h2&gt;
&lt;p&gt;There is one remaining limitation with &lt;tt class="docutils literal"&gt;kate&lt;/tt&gt; that I have not yet solved. The &lt;tt class="docutils literal"&gt;kate&lt;/tt&gt;
program uses &lt;tt class="docutils literal"&gt;KSyntaxHighlighting&lt;/tt&gt; for its language detection, which in turn is the
basis for which LSP server is assigned to a given document.&lt;/p&gt;
&lt;p&gt;This engine does not seem to support as complex detection logic as I hoped from it. Concretely,
it either works on matching on an extension / a basename (same field for both cases) or
mime type. This combined with our habit in Debian to use extension less files like
&lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt; vs. &lt;tt class="docutils literal"&gt;debian/tests/control&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;debian/rules&lt;/tt&gt; or
&lt;tt class="docutils literal"&gt;debian/upstream/metadata&lt;/tt&gt; makes things awkward a best.&lt;/p&gt;
&lt;p&gt;Concretely, the syntax engine cannot tell &lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt; from &lt;tt class="docutils literal"&gt;debian/tests/control&lt;/tt&gt; as
they use the same basename. Fortunately, the syntax is close enough to work for both and
&lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; is set to use filename based lookups, so this case works well enough.&lt;/p&gt;
&lt;p&gt;However, for &lt;tt class="docutils literal"&gt;debian/rules&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;debian/upstream/metadata&lt;/tt&gt;, my understanding is that if
I assign these in the syntax engine as Debian files, these rules will also trigger for any
file named &lt;tt class="docutils literal"&gt;foo.rules&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;bar.metadata&lt;/tt&gt;. That seems a bit too broad for me, so I have
opted out of that for now. The down side is that these files will not work out of the box
with &lt;tt class="docutils literal"&gt;kate&lt;/tt&gt; for now.&lt;/p&gt;
&lt;p&gt;The current LSP configuration in &lt;tt class="docutils literal"&gt;kate&lt;/tt&gt; does not recognize makefiles or YAML either. Ideally,
we would assign custom languages for the affected Debian files, so we do not steal the ID
from other language servers. Notably, &lt;tt class="docutils literal"&gt;kate&lt;/tt&gt; has a built-in language server for YAML and
&lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; does nothing for a generic YAML document. However, adding YAML as a supported
language for &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; would cause conflict and regressions for users that are already
happy with their generic YAML language server from &lt;tt class="docutils literal"&gt;kate&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;So there are certainly still work to be done. If you are good with &lt;tt class="docutils literal"&gt;KSyntaxHighlighting&lt;/tt&gt;
and know how to solve some of this, I hope you will help me out.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="changes-unrelated-to-kate"&gt;
&lt;h2&gt;Changes unrelated to &lt;tt class="docutils literal"&gt;kate&lt;/tt&gt;&lt;/h2&gt;
&lt;p&gt;While I was working on &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;, I also added some other features that I want to mention.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;The &lt;tt class="docutils literal"&gt;debputy lint&lt;/tt&gt; command will now show related context to diagnostic in its terminal
report when such information is available and is from the same file as the diagnostic
itself (cross file cases are rendered without related information).&lt;/p&gt;
&lt;p&gt;The related information is typically used to highlight a source of a conflict. As an
example, if you use the same field twice in a stanza of &lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt;, then
&lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; will add a diagnostic to the second occurrence. The related information
for that diagnostic would provide the position of the first occurrence.&lt;/p&gt;
&lt;p&gt;This should make it easier to find the source of the conflict in the cases where
&lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; provides it. Let me know if you are missing it for certain diagnostics.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;The diagnostics analysis of &lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt; will now identify and flag simple
duplicated relations (complex ones like OR relations are ignored for now). Thanks
to Matthias Geiger for suggesting the feature and Otto Kekäläinen for reporting
a false positive that is now fixed.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div class="section" id="closing"&gt;
&lt;h2&gt;Closing&lt;/h2&gt;
&lt;p&gt;I am glad I tested with &lt;tt class="docutils literal"&gt;kate&lt;/tt&gt; to weed out most of these issues in time before
the freeze. The Debian freeze will start within a week from now. Since &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;
is a part of the toolchain packages it will be frozen from there except for
important bug fixes.&lt;/p&gt;
&lt;/div&gt;
</content><category term="Debian, debputy"></category><category term="debian"></category><category term="debputy"></category><category term="pdo"></category></entry><entry><title>Improving packaging file detection in Debian</title><link href="https://people.debian.org/~nthykier/blog/2024/improving-packaging-file-detection-in-debian.html" rel="alternate"></link><published>2024-07-07T13:50:00+00:00</published><updated>2024-07-07T13:50:00+00:00</updated><author><name>Niels Thykier</name></author><id>tag:people.debian.org,2024-07-07:/~nthykier/blog/2024/improving-packaging-file-detection-in-debian.html</id><content type="html">&lt;p&gt;Debian packaging consists of a directory (&lt;tt class="docutils literal"&gt;debian/&lt;/tt&gt;) containing
a number of &amp;quot;hard-coded&amp;quot; filenames such as &lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt;,
&lt;tt class="docutils literal"&gt;debian/changelog&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;debian/copyright&lt;/tt&gt;.  In addition to these,
many packages will also use a number of optional files that are
named via a pattern such as &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;debian/{{PACKAGE}}.install&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;At a high level the patterns looks deceptively simple. However,
if you start working on trying to automatically classify files
in &lt;tt class="docutils literal"&gt;debian/&lt;/tt&gt; (which could be helpful to tell the user they have
a typo in the filename), you will quickly realize these patterns
are not machine friendly at all for this purpose.&lt;/p&gt;
&lt;div class="section" id="the-patterns-deconstructed"&gt;
&lt;h2&gt;The patterns deconstructed&lt;/h2&gt;
&lt;p&gt;To appreciate the problem fully, here is a primer on the pattern
and all its issues. If you are already well-versed in these, you
might want to skip the section.&lt;/p&gt;
&lt;p&gt;The most common patterns are &lt;tt class="docutils literal"&gt;debian/package.stem&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;debian/stem&lt;/tt&gt;
and usually the go to example is the &lt;tt class="docutils literal"&gt;install&lt;/tt&gt; stem (
a concrete example being &lt;tt class="docutils literal"&gt;debian/debhelper.install&lt;/tt&gt;). However,
the full pattern consists of 4 parts where 3 of them are optional.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The package name followed by a period. Optional, but must be the
first if present.&lt;/li&gt;
&lt;li&gt;The name segment followed by a period. Optional, but must appear
between the package name (if present) and the stem. If the package
name is not present, then the name segment must be first.&lt;/li&gt;
&lt;li&gt;The stem. Mandatory.&lt;/li&gt;
&lt;li&gt;An architecture restriction prefixed by a period. Optional, must
appear after the stem if present.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;To visualize it with &lt;tt class="docutils literal"&gt;[foo]&lt;/tt&gt; to mark optional parts, it looks like
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;debian/[PACKAGE.][NAME.]STEM[.ARCH]&lt;/span&gt;&lt;/tt&gt;&lt;/p&gt;
&lt;p&gt;Detecting whether a given file is in fact a packaging file now boils
down to reverse engineering its name against this pattern. Again, so
far, it might still look manageable. One major complication is that
every part (except &lt;tt class="docutils literal"&gt;ARCH&lt;/tt&gt;) can contain periods. So a trivial
&amp;quot;split by period&amp;quot; is not going to cut it. As an example:&lt;/p&gt;
&lt;blockquote&gt;
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;debian/g++-3.0.user.service&lt;/span&gt;&lt;/tt&gt;&lt;/blockquote&gt;
&lt;p&gt;This example is deliberately crafted to be ambiguous and show this
problem in its full glory. This file name can be in multiple ways:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Is the stem &lt;tt class="docutils literal"&gt;service&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;user.service&lt;/tt&gt;?  (both are known
stems from &lt;tt class="docutils literal"&gt;dh_installsystemd&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;dh_installsystemduser&lt;/tt&gt;
respectively). In fact, it can be both at the same time with
&amp;quot;clever&amp;quot; usage of &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--name=user&lt;/span&gt;&lt;/tt&gt; passed to
&lt;tt class="docutils literal"&gt;dh_installsystemd&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;g++-3.0&lt;/span&gt;&lt;/tt&gt; can be a package prefix or part of the name
segment. Even if there is a &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;g++-3.0&lt;/span&gt;&lt;/tt&gt; package in
&lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt;, then &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; (until compat 15) will
still happily match this file for the main package if you pass
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--name=g++-3.0&lt;/span&gt;&lt;/tt&gt; to the helper. Side bar: Woe is you if there
is a &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;g++-3&lt;/span&gt;&lt;/tt&gt; &lt;strong&gt;and&lt;/strong&gt; a &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;g++-3.0&lt;/span&gt;&lt;/tt&gt; package in
&lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt;, then we have multiple options for the package
prefix! Though, I do not think that happens in practice.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;Therefore, there are a lot of possible ways to split this filename
that all matches the pattern but with vastly different meaning
and consequences.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="making-detection-practical"&gt;
&lt;h2&gt;Making detection practical&lt;/h2&gt;
&lt;p&gt;To make this detection practical, lets look at the first problems
that we need to solve.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;We need the possible stems up front to have a chance at all.
When multiple stems are an option, go for the longest match
(that is, the one with most periods) since &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--name&lt;/span&gt;&lt;/tt&gt; is
rare and &amp;quot;code golfing&amp;quot; is even rarer.&lt;/li&gt;
&lt;li&gt;We can make the package prefix mandatory for files with the
name segment. This way, the moment there is something before
the stem, we know the package prefix will be part of it and
can cut it. It does not solve the ambiguity if one package
name is a prefix of another package name (from the same
source), but it still a lot better.  This made its way
into &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; compat 15 and now it is &amp;quot;just&amp;quot; a slow
long way to a better future.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;A simple solution to the first problem could be to have a static
list of known stems. That will get you started but the &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt;
eco-system strive on decentralization, so this feels like a
mismatch.&lt;/p&gt;
&lt;p&gt;There is also a second problem with the static list. Namely,
a given stem is only &amp;quot;valid&amp;quot; if the command in question is
actually in use. Which means you now need to dumpster dive
into the mess that is Turning-complete &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt;
configuration file known as &lt;tt class="docutils literal"&gt;debian/rules&lt;/tt&gt; to fully solve
that. Thanks to the Turning-completeness, we will never
get a perfect solution for a static analysis.&lt;/p&gt;
&lt;p&gt;Instead, it is time to back out and instead apply some
simplifications. Here is a sample flow:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Check whether the &lt;tt class="docutils literal"&gt;dh&lt;/tt&gt; sequencer is used. If so, use
some heuristics to figure out which addons are used.&lt;/li&gt;
&lt;li&gt;Delegate to &lt;tt class="docutils literal"&gt;dh_assistant&lt;/tt&gt; to figure out which
commands will be used and which &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; config file
stems it knows about. Here we need to know which sequences
are in use from step one (if relevant). Combine this
with any other sources for stems you have.&lt;/li&gt;
&lt;li&gt;Deconstruct all files in &lt;tt class="docutils literal"&gt;debian/&lt;/tt&gt; against the stems
and known package names from &lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt;. In
theory, dumpster diving after &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--name&lt;/span&gt;&lt;/tt&gt; options would
be helpful here, but personally I skipped that part as
I want to keep my &lt;tt class="docutils literal"&gt;debian/rules&lt;/tt&gt; parsing to an
absolute minimum.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;With this logic, you can now:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Provide typo detection of the stem (&lt;tt class="docutils literal"&gt;debian/foo.intsall &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; debian/foo.install&lt;/tt&gt;)
provided to have adequate handling of the corner cases (such as &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;debian/*.conf&lt;/span&gt;&lt;/tt&gt;
not needing correction into &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;debian/*.config&lt;/span&gt;&lt;/tt&gt;)&lt;/li&gt;
&lt;li&gt;Detect possible invalid package prefix (&lt;tt class="docutils literal"&gt;debian/foo.install&lt;/tt&gt; without &lt;tt class="docutils literal"&gt;foo&lt;/tt&gt;
being a package). Note this has to be a weak warning unless the package is using
debhelper compat 15 or you dumpster dived to validate that &lt;tt class="docutils literal"&gt;dh_install&lt;/tt&gt; was not
passed &lt;tt class="docutils literal"&gt;dh_install &lt;span class="pre"&gt;--name&lt;/span&gt; foo&lt;/tt&gt;. Agreed, no one should do that, but they can and
false positives are the worst kind of positives for a linting tool.&lt;/li&gt;
&lt;li&gt;With some limitations, detect files used without the relevant command being
active.  As an example, the some integration modes of &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; removes
&lt;tt class="docutils literal"&gt;dh_install&lt;/tt&gt;, so a &lt;tt class="docutils literal"&gt;debian/foo.install&lt;/tt&gt; would not be used.&lt;/li&gt;
&lt;li&gt;Associate a given file with a given command to assist users with the
documentation look up. Like &lt;tt class="docutils literal"&gt;debian/foo.user.service&lt;/tt&gt; is related to
&lt;tt class="docutils literal"&gt;dh_installsystemduser&lt;/tt&gt;, so &lt;tt class="docutils literal"&gt;man dh_installsystemduser&lt;/tt&gt; is a natural
start for documentation.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;I have added the logic for all these features in &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; though the documentation
association is currently not in a user facing command. All the others are now
diagnostics emitted by &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; in its editor support mode (&lt;tt class="docutils literal"&gt;debputy lsp server&lt;/tt&gt;)
or via &lt;tt class="docutils literal"&gt;debputy lint&lt;/tt&gt;. In the editor mode, the diagnostics are currently associated
with the package name in &lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt; due to technical limitations of how the
editor integration works.&lt;/p&gt;
&lt;p&gt;Some of these features will the latest version of debhelper (moving target
at times). Check with &lt;tt class="docutils literal"&gt;debputy lsp features&lt;/tt&gt; for the &lt;tt class="docutils literal"&gt;Extra dh support&lt;/tt&gt; feature,
which will be &lt;tt class="docutils literal"&gt;enabled&lt;/tt&gt; if you got all you need.&lt;/p&gt;
&lt;p&gt;Note: The detection is currently (mostly) ignoring files with architecture
restrictions. That might be lifted in the future. However, architecture restricted
config files tend to be rare, so they were not a priority at this point. Additionally,
&lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; for technical reasons ignores stem typos with multiple matches. That
sadly means that typos of &lt;tt class="docutils literal"&gt;debian/docs&lt;/tt&gt; will often be unreported due to its
proximity to &lt;tt class="docutils literal"&gt;debian/dirs&lt;/tt&gt; and vice versa.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="diving-a-bit-deeper-on-getting-the-stems"&gt;
&lt;h2&gt;Diving a bit deeper on getting the stems&lt;/h2&gt;
&lt;p&gt;To get the stems, &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; has 3 primary sources:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Its own plugins can provide packager provided files. These are only relevant
if the package is using &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;It is als possible to provide a &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; plugin that identifies packaging
files (either static or named ones). Though in practice, we probably do not
want people to roll their own &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; plugin for this purpose, since the
detection only works if the plugin is installed. I have used this mechanism
to have &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; provide a &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;debhelper-documentation&lt;/span&gt;&lt;/tt&gt; plugin to enrich
the auto-detected data and we can assume most people interested in this
feature would have &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; installed.&lt;/li&gt;
&lt;li&gt;It asks &lt;tt class="docutils literal"&gt;dh_assistant &lt;span class="pre"&gt;list-guessed-dh-config-files&lt;/span&gt;&lt;/tt&gt; for config files, which
is covered below.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;dh_assistant&lt;/tt&gt; command uses the same logic as &lt;tt class="docutils literal"&gt;dh&lt;/tt&gt; to identify the active
add-ons and loads them. From there, it scans all commands mentioned in the sequence
for the &lt;tt class="docutils literal"&gt;PROMISE: DH NOOP WITHOUT ...&lt;/tt&gt;-hint and a new
&lt;tt class="docutils literal"&gt;INTROSPECTABLE: &lt;span class="pre"&gt;CONFIG-FILES&lt;/span&gt; ...&lt;/tt&gt;-hint. When these hints reference a packaging
file (as an example, via &lt;tt class="docutils literal"&gt;pkgfile(foo)&lt;/tt&gt;) then &lt;tt class="docutils literal"&gt;dh_assistant&lt;/tt&gt; records that
as a known packaging file for that helper.&lt;/p&gt;
&lt;p&gt;Additionally, &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; now also tracks commands that were removed from the
sequence. Several of the &lt;tt class="docutils literal"&gt;dh_assistant&lt;/tt&gt; subcommand now use this to enrich
their (JSON) output with notes about these commands being known but not active.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-end-result"&gt;
&lt;h2&gt;The end result&lt;/h2&gt;
&lt;p&gt;With all of this work, you now get:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;satisfy&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;dh-debputy (&amp;gt;= 0.1.43~), debhelper (&amp;gt;= 13.16~), python3-lsprotocol, python3-levenshtein&amp;#39;&lt;/span&gt;
&lt;span class="gp"&gt;# &lt;/span&gt;For&lt;span class="w"&gt; &lt;/span&gt;demo&lt;span class="w"&gt; &lt;/span&gt;purposes,&lt;span class="w"&gt; &lt;/span&gt;pull&lt;span class="w"&gt; &lt;/span&gt;two&lt;span class="w"&gt; &lt;/span&gt;known&lt;span class="w"&gt; &lt;/span&gt;repos&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;feel&lt;span class="w"&gt; &lt;/span&gt;free&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;use&lt;span class="w"&gt; &lt;/span&gt;your&lt;span class="w"&gt; &lt;/span&gt;own&lt;span class="w"&gt; &lt;/span&gt;packages&lt;span class="w"&gt; &lt;/span&gt;here&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;https://salsa.debian.org/debian/debhelper.git&lt;span class="w"&gt; &lt;/span&gt;-b&lt;span class="w"&gt; &lt;/span&gt;debian/13.16
&lt;span class="gp"&gt;$ &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;https://salsa.debian.org/debian/debputy.git&lt;span class="w"&gt; &lt;/span&gt;-b&lt;span class="w"&gt; &lt;/span&gt;debian/0.1.43
&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;debhelper
&lt;span class="gp"&gt;$ &lt;/span&gt;mv&lt;span class="w"&gt; &lt;/span&gt;debian/debhelper.install&lt;span class="w"&gt; &lt;/span&gt;debian/debhelper.intsall
&lt;span class="gp"&gt;$ &lt;/span&gt;debputy&lt;span class="w"&gt; &lt;/span&gt;lint
&lt;span class="go"&gt;warning: File: debian/debhelper.intsall:1:0:1:0: The file &amp;quot;debian/debhelper.intsall&amp;quot; is likely a typo of &amp;quot;debian/debhelper.install&amp;quot;&lt;/span&gt;
&lt;span class="go"&gt;    File-level diagnostic&lt;/span&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;mv&lt;span class="w"&gt; &lt;/span&gt;debian/debhelper.intsall&lt;span class="w"&gt; &lt;/span&gt;debian/debhleper.install
&lt;span class="gp"&gt;$ &lt;/span&gt;debputy&lt;span class="w"&gt; &lt;/span&gt;lint
&lt;span class="go"&gt;warning: File: debian/debhleper.install:1:0:1:0: Possible typo in &amp;quot;debian/debhleper.install&amp;quot;. Consider renaming the file to &amp;quot;debian/debhelper.debhleper.install&amp;quot; or &amp;quot;debian/debhelper.install&amp;quot; if it is intended for debhelper&lt;/span&gt;
&lt;span class="go"&gt;    File-level diagnostic&lt;/span&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;../debputy
&lt;span class="gp"&gt;$ &lt;/span&gt;touch&lt;span class="w"&gt; &lt;/span&gt;debian/install
&lt;span class="gp"&gt;$ &lt;/span&gt;debputy&lt;span class="w"&gt; &lt;/span&gt;lint&lt;span class="w"&gt; &lt;/span&gt;--no-warn-about-check-manifest
&lt;span class="go"&gt;warning: File: debian/install:1:0:1:0: The file debian/install is related to a command that is not active in the dh sequence with the current addons&lt;/span&gt;
&lt;span class="go"&gt;    File-level diagnostic&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As mentioned, you also get these diagnostics in the editor via the
&lt;tt class="docutils literal"&gt;debputy lsp server&lt;/tt&gt; feature. Here the diagnostics appear in
&lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt; over the package name for technical reasons.&lt;/p&gt;
&lt;p&gt;The editor side still needs a bit more work. Notably, changes to
the filename is not triggered automatically and will first be
caught on the next change to &lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt;. Likewise,
changes to &lt;tt class="docutils literal"&gt;debian/rules&lt;/tt&gt; to add &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--with&lt;/span&gt;&lt;/tt&gt; to &lt;tt class="docutils literal"&gt;dh&lt;/tt&gt; might
also have some limitations depending on the editor. Saving both
files and then triggering an edit of &lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt; seems
to work reliable but ideally it should not be that involved.&lt;/p&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; side could also do with some work to remove
the unnecessary support for the name segment with many file
stems that do not need them and announce that to
&lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Anyhow, it is still a vast improvement over the status quo
that was &amp;quot;Why is my file silently ignored!?&amp;quot;.&lt;/p&gt;
&lt;/div&gt;
</content><category term="Debian, debputy"></category><category term="debian"></category><category term="debputy"></category><category term="pdo"></category></entry><entry><title>Debian packaging with style black</title><link href="https://people.debian.org/~nthykier/blog/2024/debian-packaging-with-style-black.html" rel="alternate"></link><published>2024-07-01T10:15:00+00:00</published><updated>2024-07-01T10:15:00+00:00</updated><author><name>Niels Thykier</name></author><id>tag:people.debian.org,2024-07-01:/~nthykier/blog/2024/debian-packaging-with-style-black.html</id><content type="html">&lt;p&gt;When I started working on the language server for &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;,
one of several reasons was about automatic applying a formatting
style. Such that you would not have to remember to manually
reformat the file.&lt;/p&gt;
&lt;p&gt;One of the problems with supporting automatic formatting is that
no one agrees on the &amp;quot;one true style&amp;quot;. To make this concrete,
Johannes Schauer Marin Rodrigues did the numbers of which
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt; option that are most common in
&lt;a class="reference external" href="https://bugs.debian.org/895570#46"&gt;https://bugs.debian.org/895570#46&lt;/a&gt;.  Unsurprising, we end up
with 14-15 different styles with various degrees of popularity.
To make matters worse, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt; does not provide a way
to declare &amp;quot;this package uses options &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-sat&lt;/span&gt;&lt;/tt&gt;&amp;quot;.&lt;/p&gt;
&lt;p&gt;So that begged the question, how would &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; know which
style it should use when it was going to reformat file. After
a couple of false-starts, Christian Hofstaedtler mentioned that we
could just have a field in &lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt; for supporting
a &amp;quot;per-package&amp;quot; setting in responds to my concern about adding
a new &amp;quot;per-package&amp;quot; config file.&lt;/p&gt;
&lt;p&gt;At first, I was not happy with it, because how would you specify
all of these options in a field (in a decent manner)? But
then I realized that one I do not want all these styles and that
I could start simpler. The Python code formatter &lt;tt class="docutils literal"&gt;black&lt;/tt&gt; is
quite successful despite &lt;strong&gt;not&lt;/strong&gt; having a lot of personalized
style options. In fact, &lt;tt class="docutils literal"&gt;black&lt;/tt&gt; makes a statement out of
&lt;strong&gt;not&lt;/strong&gt; allowing a lot of different styles.&lt;/p&gt;
&lt;p&gt;Combing that, the result was &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;X-Style:&lt;/span&gt; black&lt;/tt&gt; (to be added
to the &lt;tt class="docutils literal"&gt;Source&lt;/tt&gt; stanza of &lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt;), which every
possible reference to the &lt;tt class="docutils literal"&gt;black&lt;/tt&gt; tool for how styling
would work. Namely, you outsource the style management to
the tool (&lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;) and then start using your focus on
something else than discussing styles.&lt;/p&gt;
&lt;p&gt;As with &lt;tt class="docutils literal"&gt;black&lt;/tt&gt;, this packaging formatting style is going
to be opinionated and it will evolve over time. At the starting
point, it is similar to &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt; &lt;span class="pre"&gt;-sat&lt;/span&gt;&lt;/tt&gt; for the deb822
files (&lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; does not reformat other files at the moment).
But as mentioned, it will likely evolve and possible diverge
from &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt; over time.&lt;/p&gt;
&lt;p&gt;The choice of the starting point was based on the numbers posted
by Johannes #895570. It was not my personal favorite but it seemed
to have a majority and is also close to the one suggested by salsa
pipeline maintainers. The delta being &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-kb&lt;/span&gt;&lt;/tt&gt; which I had
originally but removed in &lt;tt class="docutils literal"&gt;0.1.34&lt;/tt&gt; at request of Otto Kekäläinen
after reviewing the numbers from Johannes one more time.&lt;/p&gt;
&lt;p&gt;To facilitate this new change, I uploaded &lt;tt class="docutils literal"&gt;debputy/0.1.30&lt;/tt&gt;
(a while back) to Debian unstable with the following changes:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Support for the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;X-Style:&lt;/span&gt; black&lt;/tt&gt; header.&lt;/li&gt;
&lt;li&gt;When a style is defined, the &lt;tt class="docutils literal"&gt;debputy lsp server&lt;/tt&gt; command
will now automatically reformat deb822 files on save (if
the editor supports it) or on explicit &amp;quot;reformat file&amp;quot; request
from the editor (usually indirectly from the user).&lt;/li&gt;
&lt;li&gt;New subcommand &lt;tt class="docutils literal"&gt;debputy reformat&lt;/tt&gt; command that will reformat
the files, when a style is defined.&lt;/li&gt;
&lt;li&gt;A new &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;pre-commit&lt;/span&gt;&lt;/tt&gt; hook repo to run &lt;tt class="docutils literal"&gt;debputy lint&lt;/tt&gt; and
&lt;tt class="docutils literal"&gt;debputy reformat&lt;/tt&gt;. These hooks are available from
&lt;a class="reference external" href="https://salsa.debian.org/debian/debputy-pre-commit-hooks"&gt;https://salsa.debian.org/debian/debputy-pre-commit-hooks&lt;/a&gt;
version &lt;tt class="docutils literal"&gt;v0.1&lt;/tt&gt; and can be used with the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;pre-commit&lt;/span&gt;&lt;/tt&gt;
tool (from the package of same name).&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;The obvious omission is a salsa-pipeline feature for this. Otto
has put that on to his personal todo list and I am looking forward
to that.&lt;/p&gt;
&lt;div class="section" id="beyond-black"&gt;
&lt;h2&gt;Beyond black&lt;/h2&gt;
&lt;p&gt;Another thing I dislike about our existing style tooling is that if you
run &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt; without any arguments, you have a higher probability
of &amp;quot;trashing&amp;quot; the style of the current package than getting the desired
result. Part of this is because &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt;'s defaults are out
of sync with the usage (which is basically what
&lt;a class="reference external" href="https://bugs.debian.org/895570"&gt;https://bugs.debian.org/895570&lt;/a&gt; is about).&lt;/p&gt;
&lt;p&gt;But I see another problem. The &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt; tool explicitly defined
options to tweak the style but provided maintainers no way to record their
preference in any machine readable way. The net result is that we have
tons of diverging styles &lt;strong&gt;and&lt;/strong&gt; that you (as a user of &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt;)
have to manually tell &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt; which style you want every time you
run the tool.&lt;/p&gt;
&lt;p&gt;In my opinion that is not playing to the strengths of neither human nor
machine. Rather, it is playing to the weaknesses of the human if anything
at all.&lt;/p&gt;
&lt;p&gt;But the salsa-CI pipeline people also ran into this issue and decided to
work around this deficiency. To use &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt; in the salsa-CI
pipeline, you have to set a variable to activate the job and another
variable with the actual options you want.&lt;/p&gt;
&lt;p&gt;The salsa-CI pipeline is quite machine readable and &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt;
is widely used. I had &lt;tt class="docutils literal"&gt;debputy reformat&lt;/tt&gt; also check for the salsa-CI
variables as a fallback. This fallback also works for the editor mode
(&lt;tt class="docutils literal"&gt;debputy lsp server&lt;/tt&gt;), so you might not even have
to run &lt;tt class="docutils literal"&gt;debputy reformat&lt;/tt&gt;. :)&lt;/p&gt;
&lt;p&gt;This was a deliberate trade-off. While I do not want all us to have all
these options, I also want Debian packaging to be less painful and have
fewer paper cuts. Having &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; go extra lengths to meet
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt; users where they are came out as the better solution
for me.&lt;/p&gt;
&lt;p&gt;A nice side-effect of this trade-off is that &lt;tt class="docutils literal"&gt;debputy reformat&lt;/tt&gt; now
a good tool for drive-by contributors. You can safely run
&lt;tt class="docutils literal"&gt;debputy reformat&lt;/tt&gt; on any package and either it will apply the styling
or it will back out and inform you that no obvious style was detected.
In the latter case, you would have to fallback to manually deducing
the style and applying it.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="differences-to-wrap-and-sort"&gt;
&lt;h2&gt;Differences to &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;debputy reformat&lt;/tt&gt; has some limitations or known differences
to &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt;. Notably, &lt;tt class="docutils literal"&gt;debputy reformat&lt;/tt&gt; (nor
&lt;tt class="docutils literal"&gt;debputy lsp server&lt;/tt&gt;) will not invoke &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt;. Instead,
&lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; has its own reformatting engine that provides similar
features.&lt;/p&gt;
&lt;p&gt;One reason for not running &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt; is that I
want &lt;tt class="docutils literal"&gt;debputy reformat&lt;/tt&gt; to match the style that
&lt;tt class="docutils literal"&gt;debputy lsp server&lt;/tt&gt; will give you. That way, you get
consistent style across all &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; commands.&lt;/p&gt;
&lt;p&gt;Another reason is that it is important to me that reformatting is
safe and does not change semantics. This leads to two regrettable
known differences to the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt; behavior due to safety
in addition to one scope limitation in &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; will ignore requests to sort the stanzas
when the &amp;quot;keep first&amp;quot; option is disabled (&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-b&lt;/span&gt; &lt;span class="pre"&gt;--no-keep-first&lt;/span&gt;&lt;/tt&gt;).
This combination is unsafe reformatting. I feel it was a mistake for
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt; to ever allow this but at least it is no longer
the default (&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-b&lt;/span&gt;&lt;/tt&gt; is now &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-bk&lt;/span&gt;&lt;/tt&gt; by default). This will be less
of a problem in debhelper-compat 15, since the concept of
&amp;quot;main package&amp;quot; will disappear and all &lt;em&gt;multi-binary&lt;/em&gt; source packages
will be required to use &lt;tt class="docutils literal"&gt;debian/package.install&lt;/tt&gt; rather than
&lt;tt class="docutils literal"&gt;debian/install&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; will not reorder the contents of &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt;
packaging files such as &lt;tt class="docutils literal"&gt;debian/install&lt;/tt&gt;. This is also an
(theoretical) unsafe thing to do. While the average package
will not experience issues with this, there are rare corner
cases where the re-ordering can affect the end result. I
happen to know this, because I ran into issues when trying
to optimize &lt;tt class="docutils literal"&gt;dh_install&lt;/tt&gt; in a way that assumed the order
did not matter. Stuff broke and there is now special-case
code in &lt;tt class="docutils literal"&gt;dh_install&lt;/tt&gt; to back out of that optimization
when that happens.&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; has a limited list of &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt; options
it understands. Some options may cause &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; to back
out and disable reformatting entirely with a remark that
it cannot apply that style. If you run into a case of this,
feel free to file a feature request to support it. I will
not promise to support everything, but if it is safe and
trivially doable with the engine already, then I probably
will.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;As stated, where &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; cannot implement the
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt; styles fully, then it will currently implement
a subset that is safe if that can be identified or back out
entirely of the formatting when it cannot. In all cases,
&lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; will not break the formatting if it is correct.
It may just fail at correcting one aspect of the
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt; style if you happen to get it wrong.&lt;/p&gt;
&lt;p&gt;It is also important to remember that the prerequisite for
&lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; applying any &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt; style is that
you have set the salsa-CI pipeline variables to trigger
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt; with the salsa-CI pipeline. So there is
still a CI check before the merge that will run the
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt; in its full glory that provides the final
safety net for you.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="just-give-me-a-style"&gt;
&lt;h2&gt;Just give me a style&lt;/h2&gt;
&lt;p&gt;In conclusion, if you, like me, are more interested in getting a consistent
style rather than discussing what that style should be, now you can get that
with &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;X-Style:&lt;/span&gt; black&lt;/tt&gt;. You can also have your custom &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt;
style be picked up automatically for drive-by contributors.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;satisfy&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;dh-debputy (&amp;gt;= 0.1.30), python3-lsprotocol&amp;#39;&lt;/span&gt;

&lt;span class="gp"&gt;# &lt;/span&gt;Add&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sb"&gt;``&lt;/span&gt;X-Style:&lt;span class="w"&gt; &lt;/span&gt;black&lt;span class="sb"&gt;``&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sb"&gt;``&lt;/span&gt;debian/control&lt;span class="sb"&gt;``&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;just give me a style&amp;quot;&lt;/span&gt;
&lt;span class="gp"&gt;#&lt;/span&gt;
&lt;span class="gp"&gt;# &lt;/span&gt;OR,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;there&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;specific&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sb"&gt;``&lt;/span&gt;wrap-and-sort&lt;span class="sb"&gt;``&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;style&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;you&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;
&lt;span class="gp"&gt;# &lt;/span&gt;&lt;span class="nv"&gt;SALSA_CI_DISABLE_WRAP_AND_SORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;no&lt;span class="w"&gt; &lt;/span&gt;plus&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;relevant&lt;span class="w"&gt; &lt;/span&gt;options&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;
&lt;span class="gp"&gt;# &lt;/span&gt;SALSA_CI_WRAP_AND_SORT_ARGS&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;debian/salsa-ci.yml&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;or&lt;span class="w"&gt; &lt;/span&gt;.gitlab-ci.yml&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="gp"&gt;$ &lt;/span&gt;debputy&lt;span class="w"&gt; &lt;/span&gt;reformat
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It is sadly not yet in the salsa-ci pipeline. Otto is looking into that and
hopefully we will have it soon. :)&lt;/p&gt;
&lt;p&gt;And if you find yourself often doing archive-wide contributions and is tired
of having to reverse engineer package formatting styles, consider using
&lt;tt class="docutils literal"&gt;debputy reformat&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;debputy lsp server&lt;/tt&gt;. If you use &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; in
this way, please consider providing feedback on what would help you.&lt;/p&gt;
&lt;/div&gt;
</content><category term="Debian, debputy"></category><category term="debian"></category><category term="debputy"></category><category term="pdo"></category></entry><entry><title>debputy v0.1.21</title><link href="https://people.debian.org/~nthykier/blog/2024/debputy-v0-1-21.html" rel="alternate"></link><published>2024-03-24T15:20:00+00:00</published><updated>2024-03-24T15:20:00+00:00</updated><author><name>Niels Thykier</name></author><id>tag:people.debian.org,2024-03-24:/~nthykier/blog/2024/debputy-v0-1-21.html</id><content type="html">&lt;p&gt;Earlier today, I have just released &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; version 0.1.21
to Debian unstable. In the blog post, I will highlight some
of the new features.&lt;/p&gt;
&lt;div class="section" id="package-boilerplate-reduction-with-automatic-relationship-substvar"&gt;
&lt;h2&gt;Package boilerplate reduction with automatic relationship substvar&lt;/h2&gt;
&lt;p&gt;Last month, I started a discussion on rethinking how we do
relationship substvars such as the &lt;tt class="docutils literal"&gt;${misc:Depends}&lt;/tt&gt;. These
generally ends up being boilerplate runes in the form of
&lt;tt class="docutils literal"&gt;Depends: ${misc:Depends}, ${shlibs:Depends}&lt;/tt&gt; where you
as the packager has to remember exactly which runes apply
to your package.&lt;/p&gt;
&lt;p&gt;My proposed solution was to automatically apply these substvars
and this feature has now been implemented in &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;. It is
also combined with the feature where essential packages should
use &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;Pre-Depends&lt;/span&gt;&lt;/tt&gt; by default for &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;dpkg-shlibdeps&lt;/span&gt;&lt;/tt&gt; related
dependencies.&lt;/p&gt;
&lt;p&gt;I am quite excited about this feature, because I noticed with
&lt;tt class="docutils literal"&gt;libcleri&lt;/tt&gt; that we are now down to 3-5 fields for defining
a simple library package.  Especially since &lt;em&gt;most&lt;/em&gt; C library
packages are trivial enough that &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; can auto-derive
them to be &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;Multi-Arch:&lt;/span&gt; same&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;As an example, the &lt;tt class="docutils literal"&gt;libcleric1&lt;/tt&gt; package is down to 3
fields (&lt;tt class="docutils literal"&gt;Package&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;Architecture&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;Description&lt;/tt&gt;)
with &lt;tt class="docutils literal"&gt;Section&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;Priority&lt;/tt&gt; being inherited from the
&lt;tt class="docutils literal"&gt;Source&lt;/tt&gt; stanza. I have submitted a MR to show case the
boilerplate reduction at
&lt;a class="reference external" href="https://salsa.debian.org/siridb-team/libcleri/-/merge_requests/3"&gt;https://salsa.debian.org/siridb-team/libcleri/-/merge_requests/3&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The removal of &lt;tt class="docutils literal"&gt;libcleric1 (= ${binary:Version})&lt;/tt&gt; in that MR
relies on another existing feature where &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; can auto-derive
a dependency between an &lt;em&gt;arch:any&lt;/em&gt; &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-dev&lt;/span&gt;&lt;/tt&gt; package and the library
package based on the &lt;tt class="docutils literal"&gt;.so&lt;/tt&gt; symlink for the shared library.
The &lt;em&gt;arch:any&lt;/em&gt; restriction comes from the fact that &lt;em&gt;arch:all&lt;/em&gt; and
&lt;em&gt;arch:any&lt;/em&gt; packages are not built together, so &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; cannot
reliably see across the package boundaries during the build (and
therefore refuses to do so at all).&lt;/p&gt;
&lt;p&gt;Packages that have already migrated to &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; can use
&lt;tt class="docutils literal"&gt;debputy &lt;span class="pre"&gt;migrate-from-dh&lt;/span&gt;&lt;/tt&gt; to detect any unnecessary
relationship substitution variables in case you want to clean
up.  The removal of &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;Multi-Arch:&lt;/span&gt; same&lt;/tt&gt; and intra-source
dependencies must be done manually and so only be done so
when you have validated that it is safe and sane to do. I was
willing to do it for the show-case MR, but I am less confident
that would bother with these for existing packages in general.&lt;/p&gt;
&lt;p&gt;Note: I summarized the discussion of the automatic relationship
substvar feature earlier this month in
&lt;a class="reference external" href="https://lists.debian.org/debian-devel/2024/03/msg00030.html"&gt;https://lists.debian.org/debian-devel/2024/03/msg00030.html&lt;/a&gt;
for those who want more details.&lt;/p&gt;
&lt;p&gt;PS: The automatic relationship substvars feature will also
appear in &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; as a part of compat 14.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="language-server-lsp-and-linting"&gt;
&lt;h2&gt;Language Server (LSP) and Linting&lt;/h2&gt;
&lt;p&gt;I have long been frustrated by our poor editor support for Debian packaging files.
To this end, I started working on a Language Server (LSP) feature in &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;
that would cover some of our standard Debian packaging files.  This release
includes the first version of said language server, which covers the following
files:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;debian/control&lt;/li&gt;
&lt;li&gt;debian/copyright (the machine readable variant)&lt;/li&gt;
&lt;li&gt;debian/changelog (mostly just spelling)&lt;/li&gt;
&lt;li&gt;debian/rules&lt;/li&gt;
&lt;li&gt;debian/debputy.manifest (syntax checks only; use &lt;tt class="docutils literal"&gt;debputy &lt;span class="pre"&gt;check-manifest&lt;/span&gt;&lt;/tt&gt;
for the full validation for now)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;Most of the effort has been spent on the Deb822 based files such as debian/control,
which comes with diagnostics, quickfixes, spellchecking (but only for relevant fields!),
and completion suggestions.&lt;/p&gt;
&lt;p&gt;Since not everyone has a LSP capable editor and because sometimes you just want
diagnostics without having to open each file in an editor, there is also a batch
version for the diagnostics via &lt;tt class="docutils literal"&gt;debputy lint&lt;/tt&gt;. Please see &lt;tt class="docutils literal"&gt;debputy(1)&lt;/tt&gt; for
how &lt;tt class="docutils literal"&gt;debputy lint&lt;/tt&gt; compares with &lt;tt class="docutils literal"&gt;lintian&lt;/tt&gt; if you are curious about which
tool to use at what time.&lt;/p&gt;
&lt;p&gt;To help you getting started, there is a now &lt;tt class="docutils literal"&gt;debputy lsp &lt;span class="pre"&gt;editor-config&lt;/span&gt;&lt;/tt&gt; command that
can provide you with the relevant editor config glue. At the moment, &lt;tt class="docutils literal"&gt;emacs&lt;/tt&gt; (via
&lt;tt class="docutils literal"&gt;eglot&lt;/tt&gt;) and &lt;tt class="docutils literal"&gt;vim&lt;/tt&gt; with &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;vim-youcompleteme&lt;/span&gt;&lt;/tt&gt; are supported.&lt;/p&gt;
&lt;p&gt;For those that followed the previous blog posts on writing the language server, I would
like to point out that the command line for running the language server has changed
to &lt;tt class="docutils literal"&gt;debputy lsp server&lt;/tt&gt; and you no longer have to tell which format it is.  I have
decided to make the language server a &amp;quot;polyglot&amp;quot; server for now, which I will
hopefully not regret... Time will tell. :)&lt;/p&gt;
&lt;p&gt;Anyhow, to get started, you will want:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;satisfy&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;dh-debputy (&amp;gt;= 0.1.21~), python3-pygls&amp;#39;&lt;/span&gt;
&lt;span class="gp"&gt;# &lt;/span&gt;Optionally,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;spellchecking
&lt;span class="gp"&gt;$ &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;python3-hunspell&lt;span class="w"&gt; &lt;/span&gt;hunspell-en-us
&lt;span class="gp"&gt;# &lt;/span&gt;For&lt;span class="w"&gt; &lt;/span&gt;emacs&lt;span class="w"&gt; &lt;/span&gt;integration
&lt;span class="gp"&gt;$ &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;elpa-dpkg-dev-el&lt;span class="w"&gt; &lt;/span&gt;markdown-mode-el
&lt;span class="gp"&gt;# &lt;/span&gt;For&lt;span class="w"&gt; &lt;/span&gt;vim&lt;span class="w"&gt; &lt;/span&gt;integration&lt;span class="w"&gt; &lt;/span&gt;via&lt;span class="w"&gt; &lt;/span&gt;vim-youcompleteme
&lt;span class="gp"&gt;$ &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;vim-youcompleteme
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Specifically for &lt;tt class="docutils literal"&gt;emacs&lt;/tt&gt;, I also learned two things &lt;em&gt;after&lt;/em&gt; the upload. First, you
can auto-activate &lt;tt class="docutils literal"&gt;eglot&lt;/tt&gt; via &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;eglot-ensure&lt;/span&gt;&lt;/tt&gt;. This badly feature interacts with
&lt;tt class="docutils literal"&gt;imenu&lt;/tt&gt; on &lt;tt class="docutils literal"&gt;debian/changelog&lt;/tt&gt; for reasons I do not understand (causing a several
second start up delay until something times out), but it works fine for the other
formats. Oddly enough, opening a changelog file and &lt;em&gt;then&lt;/em&gt; activating &lt;tt class="docutils literal"&gt;eglot&lt;/tt&gt; does
not trigger this issue at all. In the next version, editor config for emacs will
auto-activate &lt;tt class="docutils literal"&gt;eglot&lt;/tt&gt; on all files except &lt;tt class="docutils literal"&gt;debian/changelog&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;The second thing is that if you install &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;elpa-markdown-mode&lt;/span&gt;&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;emacs&lt;/tt&gt; will accept
and process markdown in the hover documentation provided by the language server.
Accordingly, the editor config for &lt;tt class="docutils literal"&gt;emacs&lt;/tt&gt; will also mention this package from
the next version on.&lt;/p&gt;
&lt;p&gt;Finally, on a related note, Jelmer and I have been looking at moving some of this
logic into a new package called &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;debpkg-metadata&lt;/span&gt;&lt;/tt&gt;. The point being to support
easier reuse of linting and LSP related metadata - like pulling a list of known
fields for debian/control or sharing logic between &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;lintian-brush&lt;/span&gt;&lt;/tt&gt; and
&lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="minimal-integration-mode-for-rules-requires-root"&gt;
&lt;h2&gt;Minimal integration mode for Rules-Requires-Root&lt;/h2&gt;
&lt;p&gt;One of the original motivators for starting &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; was to be able to get rid of
&lt;tt class="docutils literal"&gt;fakeroot&lt;/tt&gt; in our build process. While this is possible, &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; currently does
not support most of the complex packaging features such as maintscripts and debconf.
Unfortunately, the kind of packages that need &lt;tt class="docutils literal"&gt;fakeroot&lt;/tt&gt; for static ownership tend
to also require very complex packaging features.&lt;/p&gt;
&lt;p&gt;To bridge this gap, the new version of &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; supports a very minimal integration
with &lt;tt class="docutils literal"&gt;dh&lt;/tt&gt; via the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;dh-sequence-zz-debputy-rrr&lt;/span&gt;&lt;/tt&gt;.  This integration mode keeps
the vast majority of &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; sequence in place meaning most &lt;tt class="docutils literal"&gt;dh&lt;/tt&gt; add-ons
will continue to work with &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;dh-sequence-zz-debputy-rrr&lt;/span&gt;&lt;/tt&gt;. The sequence only
replaces the following commands:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;dh_fixperms&lt;/li&gt;
&lt;li&gt;dh_gencontrol&lt;/li&gt;
&lt;li&gt;dh_md5sums&lt;/li&gt;
&lt;li&gt;dh_builddeb&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;installations&lt;/tt&gt; feature of the manifest will be disabled in this integration
mode to avoid feature interactions with &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; tools that expect
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;debian/&amp;lt;pkg&amp;gt;&lt;/span&gt;&lt;/tt&gt; to contain the materialized package.&lt;/p&gt;
&lt;p&gt;On a related note, the &lt;tt class="docutils literal"&gt;debputy &lt;span class="pre"&gt;migrate-from-dh&lt;/span&gt;&lt;/tt&gt; command now supports a
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--migration-target&lt;/span&gt;&lt;/tt&gt; option, so you can choose the desired level of integration
without doing code changes. The command will attempt to auto-detect the desired
integration from existing package features such as a build-dependency on a relevant
&lt;tt class="docutils literal"&gt;dh&lt;/tt&gt; sequence, so you do not have to remember this new option every time once
the migration has started. :)&lt;/p&gt;
&lt;/div&gt;
</content><category term="Debian, debputy"></category><category term="debian"></category><category term="debputy"></category><category term="pdo"></category></entry><entry><title>Language Server for Debian: Spellchecking</title><link href="https://people.debian.org/~nthykier/blog/2024/language-server-for-debian-spellchecking.html" rel="alternate"></link><published>2024-02-24T09:35:00+00:00</published><updated>2024-02-24T09:35:00+00:00</updated><author><name>Niels Thykier</name></author><id>tag:people.debian.org,2024-02-24:/~nthykier/blog/2024/language-server-for-debian-spellchecking.html</id><content type="html">&lt;p&gt;This is my third update on writing a language server for Debian packaging files, which
aims at providing a better developer experience for Debian packagers.&lt;/p&gt;
&lt;p&gt;Lets go over what have done since the last report.&lt;/p&gt;
&lt;div class="section" id="semantic-token-support"&gt;
&lt;h2&gt;Semantic token support&lt;/h2&gt;
&lt;p&gt;I have added support for what the Language Server Protocol (LSP) call semantic tokens. These
are used to provide the editor insights into tokens of interest for users. Allegedly,
this is what editors would use for syntax highlighting as well.&lt;/p&gt;
&lt;p&gt;Unfortunately, &lt;tt class="docutils literal"&gt;eglot&lt;/tt&gt; (emacs) does not support semantic tokens, so I was not able to test
this. There is a 3-year old PR for supporting with the last update being ~3 month basically
saying &amp;quot;Please sign the Copyright Assignment&amp;quot;. I pinged the GitHub issue in the hopes it will
get unstuck.&lt;/p&gt;
&lt;p&gt;For good measure, I also checked if I could try it via &lt;tt class="docutils literal"&gt;neovim&lt;/tt&gt;. Before installing, I read
the &lt;tt class="docutils literal"&gt;neovim&lt;/tt&gt; docs, which helpfully listed the features supported. Sadly, I did not spot
semantic tokens among those and parked from there.&lt;/p&gt;
&lt;p&gt;That was a bit of a bummer, but I left the feature in for now. If you have an LSP capable
editor that supports semantic tokens, let me know how it works for you! :)&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="spellchecking"&gt;
&lt;h2&gt;Spellchecking&lt;/h2&gt;
&lt;p&gt;Finally, I implemented something Otto was missing! :)&lt;/p&gt;
&lt;p&gt;This stared with Paul Wise reminding me that there were Python binding for the &lt;tt class="docutils literal"&gt;hunspell&lt;/tt&gt;
spellchecker. This enabled me to get started with a quick prototype that spellchecked the
&lt;tt class="docutils literal"&gt;Description&lt;/tt&gt; fields in &lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt;. I also added spellchecking of comments while
I was add it.&lt;/p&gt;
&lt;p&gt;The spellchecker runs with the standard &lt;tt class="docutils literal"&gt;en_US&lt;/tt&gt; dictionary from &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;hunspell-en-us&lt;/span&gt;&lt;/tt&gt;, which
does not have a lot of technical terms in it. Much less any of the Debian specific slang.
I spend considerable time providing a &amp;quot;built-in&amp;quot; wordlist for technical and Debian specific
slang to overcome this. I also made a &amp;quot;wordlist&amp;quot; for known Debian people that the
spellchecker did not recognise. Said wordlist is fairly short as a proof of concept, and
I fully expect it to be community maintained if the language server becomes a success.&lt;/p&gt;
&lt;p&gt;My second problem was performance. As I had suspected that spellchecking was not the
fastest thing in the world. Therefore, I added a very small language server for the
&lt;tt class="docutils literal"&gt;debian/changelog&lt;/tt&gt;, which only supports spellchecking the textual part. Even for a
small changelog of a 1000 lines, the spellchecking takes about 5 seconds, which
confirmed my suspicion. With every change you do, the existing diagnostics hangs around
for 5 seconds before being updated. Notably, in &lt;tt class="docutils literal"&gt;emacs&lt;/tt&gt;, it seems that diagnostics
gets translated into an absolute character offset, so all diagnostics after the change
gets misplaced for every character you type.&lt;/p&gt;
&lt;p&gt;Now, there is little I could do to speed up &lt;tt class="docutils literal"&gt;hunspell&lt;/tt&gt;. But I can, as always, cheat.
The way diagnostics work in the LSP is that the server listens to a set of notifications
like &amp;quot;document opened&amp;quot; or &amp;quot;document changed&amp;quot;. In a response to that, the LSP can start
its diagnostics scanning of the document and eventually publish all the diagnostics to
the editor. The spec is quite clear that the server owns the diagnostics and the
diagnostics are sent as a &amp;quot;notification&amp;quot; (that is, fire-and-forgot). Accordingly, there
is nothing that prevents the server from publishing diagnostics multiple times for a
single trigger. The only requirement is that the server publishes the accumulated
diagnostics in every publish (that is, no delta updating).&lt;/p&gt;
&lt;p&gt;Leveraging this, I had the language server for &lt;tt class="docutils literal"&gt;debian/changelog&lt;/tt&gt; scan the document and
publish once for approximately every 25 typos (diagnostics) spotted. This means you quickly
get your first result and that clears the obsolete diagnostics. Thereafter, you get
frequent updates to the remainder of the document if you do not perform any further changes.
That is, up to a predefined max of typos, so we do not overload the client for longer
changelogs. If you do any changes, it resets and starts over.&lt;/p&gt;
&lt;p&gt;The only bit missing was dealing with concurrency. By default, a &lt;tt class="docutils literal"&gt;pygls&lt;/tt&gt; language server
is single threaded. It is not great if the language server hangs for 5 seconds everytime
you type anything. Fortunately, &lt;tt class="docutils literal"&gt;pygls&lt;/tt&gt; has builtin support for &lt;tt class="docutils literal"&gt;asyncio&lt;/tt&gt; and threaded
handlers. For now, I did an &lt;tt class="docutils literal"&gt;async&lt;/tt&gt; handler that &lt;tt class="docutils literal"&gt;await&lt;/tt&gt; after each line and setup some
manual detection to stop an obsolete diagnostics run. This means the server will fairly
quickly abandon an obsolete run.&lt;/p&gt;
&lt;p&gt;Also, as a side-effect of working on the spellchecking, I fixed multiple typos in the
changelog of &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;. :)&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="follow-up-on-the-what-next-from-my-previous-update"&gt;
&lt;h2&gt;Follow up on the &amp;quot;What next?&amp;quot; from my previous update&lt;/h2&gt;
&lt;p&gt;In my previous update, I mentioned I had to finish up my &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;python-debian&lt;/span&gt;&lt;/tt&gt; changes to
support getting the location of a token in a &lt;tt class="docutils literal"&gt;deb822&lt;/tt&gt; file. That was done, the MR
is now filed, and is pending review. Hopefully, it will be merged and uploaded soon. :)&lt;/p&gt;
&lt;p&gt;I also submitted my proposal for a different way of handling relationship substvars to
debian-devel. So far, it seems to have received only positive feedback. I hope it stays
that way and we will have this feature soon. Guillem proposed to move some of this into
&lt;tt class="docutils literal"&gt;dpkg&lt;/tt&gt;, which might delay my plans a bit. However, it might be for the better in the
long run, so I will wait a bit to see what happens on that front. :)&lt;/p&gt;
&lt;p&gt;As noted above, I managed to add &lt;tt class="docutils literal"&gt;debian/changelog&lt;/tt&gt; as a support format for the
language server. Even if it only does spellchecking and trimming of trailing newlines
on save, it technically is a new format and therefore cross that item off my list. :D&lt;/p&gt;
&lt;p&gt;Unfortunately, I did not manage to write a linter variant that does not involve using
an LSP-capable editor. So that is still pending. Instead, I submitted an MR against
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;elpa-dpkg-dev-el&lt;/span&gt;&lt;/tt&gt; to have it recognize all the fields that the &lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt;
LSP knows about at this time to offset the lack of semantic token support in
&lt;tt class="docutils literal"&gt;eglot&lt;/tt&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="from-here"&gt;
&lt;h2&gt;From here...&lt;/h2&gt;
&lt;p&gt;My sprinting on this topic will soon come to an end, so I have to a bit more careful
now with what tasks I open!&lt;/p&gt;
&lt;p&gt;I think I will narrow my focus to providing a batch linting interface. Ideally, with
an auto-fix for some of the more mechanical issues, where this is little doubt about
the answer.&lt;/p&gt;
&lt;p&gt;Additionally, I think the spellchecking will need a bit more maturing. My current
code still trips on naming patterns that are &amp;quot;clearly&amp;quot; verbatim or code references
like things written in &lt;tt class="docutils literal"&gt;CamelCase&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;SCREAMING_SNAKE_CASE&lt;/tt&gt;. That gets annoying
really quickly.  It also trips on a lot of commands like &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;dpkg-gencontrol&lt;/span&gt;&lt;/tt&gt;, but that
is harder to fix since it could have been a real word. I think those will have to be
fixed people using quotes around the commands. Maybe the most popular ones will end
up in the wordlist.&lt;/p&gt;
&lt;p&gt;Beyond that, I will play it by ear if I have any time left. :)&lt;/p&gt;
&lt;/div&gt;
</content><category term="Debian, debputy"></category><category term="debian"></category><category term="debputy"></category><category term="pdo"></category></entry><entry><title>Expanding on the Language Server (LSP) support for debian/control</title><link href="https://people.debian.org/~nthykier/blog/2024/expanding-on-the-language-server-support-debian-control.html" rel="alternate"></link><published>2024-02-21T22:00:00+00:00</published><updated>2024-02-21T22:00:00+00:00</updated><author><name>Niels Thykier</name></author><id>tag:people.debian.org,2024-02-21:/~nthykier/blog/2024/expanding-on-the-language-server-support-debian-control.html</id><content type="html">&lt;p&gt;I have spent some more time on improving my language server for &lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt;. Today,
I managed to provide the following features:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;The &lt;tt class="docutils literal"&gt;X-&lt;/tt&gt; style prefixes for field names are now understood and handled. This means
the language server now considers &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;XC-Package-Type&lt;/span&gt;&lt;/tt&gt; the same as &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;Package-Type&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;More diagnostics:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Fields without values now trigger an error marker&lt;/li&gt;
&lt;li&gt;Duplicated fields now trigger an error marker&lt;/li&gt;
&lt;li&gt;Fields used in the wrong paragraph now trigger an error marker&lt;/li&gt;
&lt;li&gt;Typos in field names or values now trigger a warning marker. For field names,
&lt;tt class="docutils literal"&gt;X-&lt;/tt&gt; style prefixes are stripped before typo detection is done.&lt;/li&gt;
&lt;li&gt;The value of the &lt;tt class="docutils literal"&gt;Section&lt;/tt&gt; field is now validated against a dataset of known sections
and trigger a warning marker if not known.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;The &amp;quot;on-save trim end of line whitespace&amp;quot; now works. I had a logic bug in the server
side code that made it submit &amp;quot;no change&amp;quot; edits to the editor.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;The language server now provides &amp;quot;hover&amp;quot; documentation for field names. There is a small
screenshot of this below. Sadly, &lt;tt class="docutils literal"&gt;emacs&lt;/tt&gt; does not support markdown or, if it does, it
does not announce the support for markdown. For now, all the documentation is always in
markdown format and the language server will tag it as either markdown or plaintext
depending on the announced support.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;The language server now provides quick fixes for some of the more trivial problems such
as deprecated fields or typos of fields and values.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Added more known fields including the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;XS-Autobuild&lt;/span&gt;&lt;/tt&gt; field for &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;non-free&lt;/span&gt;&lt;/tt&gt; packages
along with a link to the relevant devref section in its hover doc.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;This covers basically all my known omissions from last update except spellchecking of the
&lt;tt class="docutils literal"&gt;Description&lt;/tt&gt; field.&lt;/p&gt;
&lt;img alt="An image of emacs showing documentation for the Provides field from the language server." src="https://people.debian.org/~nthykier/blog/images/emacs-with-lsp-hover-doc-2024-02-21.png" /&gt;
&lt;div class="section" id="spellchecking"&gt;
&lt;h2&gt;Spellchecking&lt;/h2&gt;
&lt;p&gt;Personally, I feel spellchecking would be a very welcome addition to the current feature set.
However, reviewing my options, it seems that most of the spellchecking python libraries out
there are not packaged for Debian, or at least not other the name I assumed they would be.&lt;/p&gt;
&lt;p&gt;The alternative is to pipe the spellchecking to another program like &lt;tt class="docutils literal"&gt;aspell list&lt;/tt&gt;. I did not
test this fully, but &lt;tt class="docutils literal"&gt;aspell list&lt;/tt&gt; does seem to do some input buffering that I cannot easily
default (at least not in the shell). Though, either way, the logic for this will not be trivial
and &lt;tt class="docutils literal"&gt;aspell list&lt;/tt&gt; does not seem to include the corrections either. So best case, you would get
typo markers but no suggestions for what you should have typed. Not ideal.&lt;/p&gt;
&lt;p&gt;Additionally, I am also concerned with the performance for this feature. For &lt;tt class="docutils literal"&gt;d/control&lt;/tt&gt;, it
will be a trivial matter in practice. However, I would be reusing this for &lt;tt class="docutils literal"&gt;d/changelog&lt;/tt&gt; which
is 99% free text with plenty of room for typos. For a regular linter, some slowness is
acceptable as it is basically a batch tool. However, for a language server, this potentially
translates into latency for your edits and that gets annoying.&lt;/p&gt;
&lt;p&gt;While it is definitely on my long term todo list, I am a bit afraid that it can easily become
a time sink. Admittedly, this does annoy me, because I wanted to cross off at least one of
Otto's requested features soon.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="on-wrap-and-sort-support"&gt;
&lt;h2&gt;On wrap-and-sort support&lt;/h2&gt;
&lt;p&gt;The other obvious request from Otto would be to automate &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt; formatting. Here,
the problem is that &amp;quot;we&amp;quot; in Debian do not agree on the one true formatting of
&lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt;. In fact, I am fairly certain we do not even agree on whether we should
all use &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt;. This implies we need a style configuration.&lt;/p&gt;
&lt;p&gt;However, if we have a style configuration per person, then you get style &amp;quot;ping-pong&amp;quot; for
packages where the co-maintainers do not all have the same style configuration. Additionally,
it is very likely that you are a member of multiple packaging teams or groups that all have
their own unique style. Ergo, only having a personal config file is doomed to fail.&lt;/p&gt;
&lt;p&gt;The only &amp;quot;sane&amp;quot; option here that I can think of is to have or support &amp;quot;per package&amp;quot; style
configuration. Something that would be committed to git, so the tooling would automatically
pick up the configuration. Obviously, that is not fun for large packaging teams where you
have to maintain one file per package if you want a consistent style across all packages.
But it beats &amp;quot;style ping-pong&amp;quot; any day of the week.&lt;/p&gt;
&lt;p&gt;Note that I am perfectly open to having a personal configuration file as a fallback for when
the &amp;quot;per package&amp;quot; configuration file is absent.&lt;/p&gt;
&lt;p&gt;The second problem is the question of which format to use and what to name this file.
Since file formats and naming has never been controversial at all, this will obviously be
the easy part of this problem. But the file should be parsable by both &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wrap-and-sort&lt;/span&gt;&lt;/tt&gt;
and the language server, so you get the same result regardless of which tool you use. If
we do not ensure this, then we still have the style ping-pong problem as people use
different tools.&lt;/p&gt;
&lt;p&gt;This also seems like time sink with no end. So, what next then...?&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="what-next"&gt;
&lt;h2&gt;What next?&lt;/h2&gt;
&lt;p&gt;On the language server front, I will have a look at its support for providing semantic
hints to the editors that might be used for syntax highlighting. While I think most
common Debian editors have built syntax highlighting already, I would like this language
server to stand on its own. I would like us to be in a situation where we do not have
implement yet another editor extension for Debian packaging files. At least not for
editors that support the LSP spec.&lt;/p&gt;
&lt;p&gt;On a different front, I have an idea for how we go about relationship related substvars.
It is not directly related to this language server, except I got triggered by the language
server &amp;quot;missing&amp;quot; a diagnostic for reminding people to add the magic
&lt;tt class="docutils literal"&gt;Depends: &lt;span class="pre"&gt;${misc:Depends}[,&lt;/span&gt; ${shlibs:Depends}]&lt;/tt&gt; boilerplate. The magic boilerplate that
you &lt;em&gt;have&lt;/em&gt; to write even though we really should just fix this at a tooling level instead.
Energy permitting, I will formulate a proposal for that and send it to debian-devel.&lt;/p&gt;
&lt;p&gt;Beyond that, I think I might start adding support for another file. I also need to wrap
up my &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;python-debian&lt;/span&gt;&lt;/tt&gt; branch, so I can get the position support into the Debian
soon, which would remove one papercut for using this language server.&lt;/p&gt;
&lt;p&gt;Finally, it might be interesting to see if I can extract a &amp;quot;batch-linter&amp;quot; version of
the diagnostics and related quickfix features. If nothing else, the &amp;quot;linter&amp;quot; variant
would enable many of you to get a &amp;quot;mini-Lintian&amp;quot; without having to do a package
build first.&lt;/p&gt;
&lt;/div&gt;
</content><category term="Debian, debputy"></category><category term="debian"></category><category term="debputy"></category><category term="pdo"></category></entry><entry><title>Language Server (LSP) support for debian/control</title><link href="https://people.debian.org/~nthykier/blog/2024/language-server-support-debian-control.html" rel="alternate"></link><published>2024-02-20T15:45:00+00:00</published><updated>2024-02-20T15:45:00+00:00</updated><author><name>Niels Thykier</name></author><id>tag:people.debian.org,2024-02-20:/~nthykier/blog/2024/language-server-support-debian-control.html</id><content type="html">&lt;p&gt;About a month ago, Otto Kekäläinen asked for editor extensions for debian related
files on the debian-devel mailing list. In that thread, I concluded that what we
were missing was a &amp;quot;Language Server&amp;quot; (LSP) for our packaging files.&lt;/p&gt;
&lt;p&gt;Last week, I started a prototype for such a LSP for the &lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt; file as
a starting point based on the &lt;a class="reference external" href="https://github.com/openlawlibrary/pygls"&gt;pygls&lt;/a&gt;
library. The initial prototype worked and I could do very basic diagnostics plus
completion suggestion for field names.&lt;/p&gt;
&lt;div class="section" id="current-features"&gt;
&lt;h2&gt;Current features&lt;/h2&gt;
&lt;p&gt;I got 4 basic features implemented, though I have only been able to test two of them in
emacs.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Diagnostics or linting of basic issues.&lt;/li&gt;
&lt;li&gt;Completion suggestions for all known field names that I could think of and values for
some fields.&lt;/li&gt;
&lt;li&gt;Folding ranges (untested). This feature enables the editor to &amp;quot;fold&amp;quot; multiple lines.
It is often used with multi-line comments and that is the feature currently supported.&lt;/li&gt;
&lt;li&gt;On save, trim trailing whitespace at the end of lines (untested). Might not be
registered correctly on the server end.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;Despite its very limited feature set, I feel editing &lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt; in &lt;tt class="docutils literal"&gt;emacs&lt;/tt&gt; is
now a much more pleasant experience.&lt;/p&gt;
&lt;p&gt;Coming back to the features that Otto requested, the above covers a grand total of zero.
Sorry, Otto. It is not you, it is me.&lt;/p&gt;
&lt;div class="section" id="completion-suggestions"&gt;
&lt;h3&gt;Completion suggestions&lt;/h3&gt;
&lt;p&gt;For completion, all known fields are completed. Place the cursor at the start of the line
or in a partially written out field name and trigger the completion in your editor. In my
case, I can type &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;R-R-R&lt;/span&gt;&lt;/tt&gt; and trigger the completion and the editor will automatically
replace it with &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;Rules-Requires-Root&lt;/span&gt;&lt;/tt&gt; as the only applicable match.  Your milage may
vary since I delegate most of the filtering to the editor, meaning the editor has the
final say about whether your input matches anything.&lt;/p&gt;
&lt;p&gt;The only filtering done on the server side is that the server prunes out fields already used
in the paragraph, so you are not presented with the option to repeat an already used field,
which would be an error. Admittedly, not an error the language server detects at the moment,
but other tools will.&lt;/p&gt;
&lt;p&gt;When completing field, if the field only has one non-default value such as &lt;tt class="docutils literal"&gt;Essential&lt;/tt&gt;
which can be either &lt;tt class="docutils literal"&gt;no&lt;/tt&gt; (the default, but you should not use it) or &lt;tt class="docutils literal"&gt;yes&lt;/tt&gt;, then the
completion suggestion will complete the field along with its value.&lt;/p&gt;
&lt;p&gt;This is mostly only applicable for &amp;quot;yes/no&amp;quot; fields such as &lt;tt class="docutils literal"&gt;Essential&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;Protected&lt;/tt&gt;.
But it does also trigger for &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;Package-Type&lt;/span&gt;&lt;/tt&gt; at the moment.&lt;/p&gt;
&lt;p&gt;As for completing values, here the language server can complete the value for simple fields
such as &amp;quot;yes/no&amp;quot; fields, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;Multi-Arch&lt;/span&gt;&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;Package-Type&lt;/span&gt;&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;Priority&lt;/tt&gt;. I intend to add
support for &lt;tt class="docutils literal"&gt;Section&lt;/tt&gt; as well - maybe also &lt;tt class="docutils literal"&gt;Architecture&lt;/tt&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="diagnostics"&gt;
&lt;h3&gt;Diagnostics&lt;/h3&gt;
&lt;p&gt;On the diagnostic front, I have added multiple diagnostics:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;An error marker for syntax errors.&lt;/li&gt;
&lt;li&gt;An error marker for missing a mandatory field like &lt;tt class="docutils literal"&gt;Package&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;Architecture&lt;/tt&gt;.
This also includes &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;Standards-Version&lt;/span&gt;&lt;/tt&gt;, which is admittedly mandatory by policy rather
than tooling falling part.&lt;/li&gt;
&lt;li&gt;An error marker for adding &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;Multi-Arch:&lt;/span&gt; same&lt;/tt&gt; to an &lt;tt class="docutils literal"&gt;Architecture: all&lt;/tt&gt; package.&lt;/li&gt;
&lt;li&gt;Error marker for providing an unknown value to a field with a set of known values.
As an example, writing &lt;tt class="docutils literal"&gt;foo&lt;/tt&gt; in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;Multi-Arch&lt;/span&gt;&lt;/tt&gt; would trigger this one.&lt;/li&gt;
&lt;li&gt;Warning marker for using deprecated fields such as &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;DM-Upload-Allowed&lt;/span&gt;&lt;/tt&gt;, or when
setting a field to its default value for fields like &lt;tt class="docutils literal"&gt;Essential&lt;/tt&gt;. The latter rule
only applies to selected fields and notably &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;Multi-Arch:&lt;/span&gt; no&lt;/tt&gt; does not trigger a
warning.&lt;/li&gt;
&lt;li&gt;Info level marker if a field like &lt;tt class="docutils literal"&gt;Priority&lt;/tt&gt; duplicates the value of the &lt;tt class="docutils literal"&gt;Source&lt;/tt&gt;
paragraph.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;Notable omission at this time:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;No errors are raised if a field does not have a value.&lt;/li&gt;
&lt;li&gt;No errors are raised if a field is duplicated inside a paragraph.&lt;/li&gt;
&lt;li&gt;No errors are used if a field is used in the wrong paragraph.&lt;/li&gt;
&lt;li&gt;No spellchecking of the &lt;tt class="docutils literal"&gt;Description&lt;/tt&gt; field.&lt;/li&gt;
&lt;li&gt;No understanding that &lt;tt class="docutils literal"&gt;Foo&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;X[CBS]-Foo&lt;/span&gt;&lt;/tt&gt; are related. As an example,
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;XC-Package-Type&lt;/span&gt;&lt;/tt&gt; is completely ignored despite being the old name for &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;Package-Type&lt;/span&gt;&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;Quick fixes to solve these problems... :)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="trying-it-out"&gt;
&lt;h2&gt;Trying it out&lt;/h2&gt;
&lt;p&gt;If you want to try, it is sadly a bit more involved due to things not being uploaded
or merged yet.  Also, be advised that I will regularly rebase my git branches as I
revise the code.&lt;/p&gt;
&lt;p&gt;The setup:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Build and install the deb of the &lt;tt class="docutils literal"&gt;main&lt;/tt&gt; branch of &lt;tt class="docutils literal"&gt;pygls&lt;/tt&gt; from &lt;a class="reference external" href="https://salsa.debian.org/debian/pygls"&gt;https://salsa.debian.org/debian/pygls&lt;/a&gt;
The package is in NEW and hopefully this step will soon just be a regular &lt;tt class="docutils literal"&gt;apt install&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;Build and install the deb of the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;rts-locatable&lt;/span&gt;&lt;/tt&gt; branch of my &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;python-debian&lt;/span&gt;&lt;/tt&gt; fork from &lt;a class="reference external" href="https://salsa.debian.org/nthykier/python-debian"&gt;https://salsa.debian.org/nthykier/python-debian&lt;/a&gt;
There is a draft MR of it as well on the main repo.&lt;/li&gt;
&lt;li&gt;Build and install the deb of the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;lsp-support&lt;/span&gt;&lt;/tt&gt; branch of &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; from &lt;a class="reference external" href="https://salsa.debian.org/debian/debputy"&gt;https://salsa.debian.org/debian/debputy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Configure your editor to run &lt;tt class="docutils literal"&gt;debputy lsp debian/control&lt;/tt&gt; as the language server
for &lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt;. This is depends on your editor. I figured out how to do it
for emacs (see below). I also found a guide for neovim at &lt;a class="reference external" href="https://neovim.io/doc/user/lsp"&gt;https://neovim.io/doc/user/lsp&lt;/a&gt;.
Note that &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; can be run from any directory here. The &lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt; is a
reference to the file format and not a concrete file in this case.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;Obviously, the setup should get easier over time. The first three bullet points should
eventually get resolved by merges and upload meaning you end up with an &lt;tt class="docutils literal"&gt;apt install&lt;/tt&gt;
command instead of them.&lt;/p&gt;
&lt;p&gt;For the editor part, I would obviously love it if we can add snippets for editors to make
the automatically pick up the language server when the relevant file is installed.&lt;/p&gt;
&lt;div class="section" id="using-the-debputy-lsp-in-emacs"&gt;
&lt;h3&gt;Using the debputy LSP in emacs&lt;/h3&gt;
&lt;p&gt;The guide I found so far relies on &lt;tt class="docutils literal"&gt;eglot&lt;/tt&gt;.  The guide below assumes you have the
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;elpa-dpkg-dev-el&lt;/span&gt;&lt;/tt&gt; package installed for the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;debian-control-mode&lt;/span&gt;&lt;/tt&gt;. Though it should
be a trivially matter to replace &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;debian-control-mode&lt;/span&gt;&lt;/tt&gt; with a different mode if you
use a different mode for your &lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt; file.&lt;/p&gt;
&lt;p&gt;In your emacs init file (such as &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;~/.emacs&lt;/span&gt;&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;~/.emacs.d/init.el&lt;/span&gt;&lt;/tt&gt;), you add the
follow blob.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;with-eval-after-load&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;eglot&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;add-to-list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;#39;eglot-server-programs&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;debian-control-mode&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;debputy&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;lsp&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;debian/control&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once you open the &lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt; file in &lt;tt class="docutils literal"&gt;emacs&lt;/tt&gt;, you can type &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;M-x&lt;/span&gt; eglot&lt;/tt&gt; to
activate the language server. Not sure why that manual step is needed and if someone
knows how to automate it such that &lt;tt class="docutils literal"&gt;eglot&lt;/tt&gt; activates automatically on opening
&lt;tt class="docutils literal"&gt;debian/control&lt;/tt&gt;, please let me know.&lt;/p&gt;
&lt;p&gt;For testing completions, I often have to manually activate them (with &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;C-M-i&lt;/span&gt;&lt;/tt&gt; or
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;M-x&lt;/span&gt; &lt;span class="pre"&gt;complete-symbol&lt;/span&gt;&lt;/tt&gt;).  Though, it is a bit unclear to me whether this is an
emacs setting that I have not toggled or something I need to do on the language server
side.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="from-here"&gt;
&lt;h2&gt;From here&lt;/h2&gt;
&lt;p&gt;As next steps, I will probably look into fixing some of the &amp;quot;known missing&amp;quot; items under
diagnostics. The quick fix would be a considerable improvement to assisting users.&lt;/p&gt;
&lt;p&gt;In the not so distant future, I will probably start to look at supporting other files
such as &lt;tt class="docutils literal"&gt;debian/changelog&lt;/tt&gt; or look into supporting configuration, so I can cover
formatting features like wrap-and-sort.&lt;/p&gt;
&lt;p&gt;I am also very much open to how we can provide integrations for this feature into
editors by default.  I will probably create a separate binary package for specifically
this feature that pulls all relevant dependencies that would be able to provide editor
integrations as well.&lt;/p&gt;
&lt;/div&gt;
</content><category term="Debian, debputy"></category><category term="debian"></category><category term="debputy"></category><category term="pdo"></category></entry><entry><title>Annotating the Debian packaging directory</title><link href="https://people.debian.org/~nthykier/blog/2024/annotating-the-debian-packaging-directory.html" rel="alternate"></link><published>2024-01-28T17:35:00+00:00</published><updated>2024-01-28T17:35:00+00:00</updated><author><name>Niels Thykier</name></author><id>tag:people.debian.org,2024-01-28:/~nthykier/blog/2024/annotating-the-debian-packaging-directory.html</id><content type="html">&lt;p&gt;In my previous blog post &lt;a class="reference external" href="https://people.debian.org/~nthykier/blog/2023/providing-online-reference-documentation-for-debputy.html"&gt;Providing online reference documentation for debputy&lt;/a&gt;,
I made a point about how &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; documentation was suboptimal on account
of being static rather than online. The thing is that &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; is not alone
in this problem space, even if it is a major contributor to the number of
packaging files you have to to know about.&lt;/p&gt;
&lt;p&gt;If we look at the &amp;quot;competition&amp;quot; here such as Fedora and Arch Linux, they tend to only
have one packaging file. While most Debian people will tell you a long list of cons
about having one packaging file (such a Fedora's spec file being 3+ domain specific
languages &amp;quot;mashed&amp;quot; into one file), one major advantage is that there is only
&amp;quot;the one packaging file&amp;quot;. You only need to remember where to find the documentation
for one file, which is great when you are running on wetware with limited storage
capacity.&lt;/p&gt;
&lt;p&gt;Which means as a newbie, you can dedicate less mental resources to tracking multiple
files and how they interact and more effort understanding the &amp;quot;one file&amp;quot; at hand.
I started by asking myself how can we in Debian make the packaging stack more
accessible to newcomers? Spoiler alert, I dug myself into rabbit hole and ended up
somewhere else than where I thought I was going.&lt;/p&gt;
&lt;p&gt;I started by wanting to scan the debian directory and annotate all files that I
could with documentation links. The logic was that if &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; could do that
for you, then you could spend more mental effort elsewhere. So I combined
&lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;'s packager provided files detection with a static list of files and
I quickly had a good starting point for &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;-based packages.&lt;/p&gt;
&lt;div class="section" id="adding-non-static-dpkg-and-debhelper-files-to-the-mix"&gt;
&lt;h2&gt;Adding (non-static) dpkg and debhelper files to the mix&lt;/h2&gt;
&lt;p&gt;Now, I could have closed the topic here and said &amp;quot;Look, I did &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; files
plus couple of super common files&amp;quot;. But I decided to take it a bit further. I added
support for handling  some &lt;tt class="docutils literal"&gt;dpkg&lt;/tt&gt; files like packager provided files (such as
&lt;tt class="docutils literal"&gt;debian/substvars&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;debian/symbols&lt;/tt&gt;). But even then, we all know that
&lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; is the big hurdle and a major part of the omission...&lt;/p&gt;
&lt;p&gt;In another previous blog post (&lt;a class="reference external" href="https://people.debian.org/~nthykier/blog/2023/a-new-debian-package-helper-debputy.html"&gt;A new Debian package helper: debputy&lt;/a&gt;),
I made a point about how &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; could list all auxiliary files while
&lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; could not. This was exactly the kind of feature that I would need
for this feature, if this feature was to cover &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt;. Now, I also
remarked in that blog post that I was not willing to maintain such a list.
Also, I may have ranted about static documentation being unhelpful for
debhelper as it excludes third-party provided tooling.&lt;/p&gt;
&lt;p&gt;Fortunately, a recent update to &lt;tt class="docutils literal"&gt;dh_assistant&lt;/tt&gt; had provided some basic
plumbing for loading &lt;tt class="docutils literal"&gt;dh&lt;/tt&gt; sequences. This meant that getting a list of
all relevant commands for a source package was a lot easier than it used
to be. Once you have a list of commands, it would be possible to check
all of them for &lt;tt class="docutils literal"&gt;dh&lt;/tt&gt;'s &lt;tt class="docutils literal"&gt;NOOP PROMISE&lt;/tt&gt; hints.  In these hints, a command
can assert it does nothing if a given &lt;tt class="docutils literal"&gt;pkgfile&lt;/tt&gt; is not present. This
lead to the new &lt;tt class="docutils literal"&gt;dh_assistant &lt;span class="pre"&gt;list-guessed-dh-config-files&lt;/span&gt;&lt;/tt&gt; command
that will list all declared &lt;tt class="docutils literal"&gt;pkgfiles&lt;/tt&gt; and which helpers listed them.&lt;/p&gt;
&lt;p&gt;With this combined feature set in place, &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; could call
&lt;tt class="docutils literal"&gt;dh_assistant&lt;/tt&gt; to get a list of &lt;tt class="docutils literal"&gt;pkgfiles&lt;/tt&gt;, pretend they were packager
provided files and annotate those along with manpage for the relevant
&lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; command.  The exciting thing about letting &lt;tt class="docutils literal"&gt;debpputy&lt;/tt&gt;
resolve the &lt;tt class="docutils literal"&gt;pkgfiles&lt;/tt&gt; is that &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; will resolve &amp;quot;named&amp;quot; files
automatically (&lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; tools will only do so when &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--name&lt;/span&gt;&lt;/tt&gt; is
passed), so it is much more likely to detect named &lt;tt class="docutils literal"&gt;pkgfiles&lt;/tt&gt;
correctly too. Side note: I am going to ignore the elephant in the room
for now, which is &lt;tt class="docutils literal"&gt;dh_installsystemd&lt;/tt&gt; and its &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;package&amp;#64;.service&lt;/span&gt;&lt;/tt&gt; files
and the wide-spread use of &lt;tt class="docutils literal"&gt;debian/foo.service&lt;/tt&gt; where there is no
package called &lt;tt class="docutils literal"&gt;foo&lt;/tt&gt;. For the latter case, the &amp;quot;proper&amp;quot; name would
be &lt;tt class="docutils literal"&gt;debian/pkg.foo.service&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;With the new &lt;tt class="docutils literal"&gt;dh_assistant&lt;/tt&gt; feature done and added to &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;,
&lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; could now detect the ubiquitous &lt;tt class="docutils literal"&gt;debian/install&lt;/tt&gt; file.
Excellent. But less great was that the very common &lt;tt class="docutils literal"&gt;debian/docs&lt;/tt&gt;
file was not. Turns out that &lt;tt class="docutils literal"&gt;dh_installdocs&lt;/tt&gt; cannot
be skipped by &lt;tt class="docutils literal"&gt;dh&lt;/tt&gt;, so it cannot have &lt;tt class="docutils literal"&gt;NOOP PROMISE&lt;/tt&gt; hints. Meh...&lt;/p&gt;
&lt;p&gt;Well, &lt;tt class="docutils literal"&gt;dh_assistant&lt;/tt&gt; could learn about a new &lt;tt class="docutils literal"&gt;INTROSPECTABLE&lt;/tt&gt; marker
in addition to the &lt;tt class="docutils literal"&gt;NOOP PROMISE&lt;/tt&gt; and then I could sprinkle that into
a few commands. Indeed that worked and meant that &lt;tt class="docutils literal"&gt;debian/postinst&lt;/tt&gt;
(etc.) are now also detectable.&lt;/p&gt;
&lt;p&gt;At this point, &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; would be able to identify a wide range of
&lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; related configuration files in &lt;tt class="docutils literal"&gt;debian/&lt;/tt&gt; and at least
associate each of them with one or more commands.&lt;/p&gt;
&lt;p&gt;Nice, surely, this would be a good place to stop, right...?&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="adding-more-metadata-to-the-files"&gt;
&lt;h2&gt;Adding more metadata to the files&lt;/h2&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; detected files only had a command name and manpage
URI to that command. It would be nice if we could contextualize this
a bit more.&lt;/p&gt;
&lt;p&gt;Like is this file installed into the package as is like &lt;tt class="docutils literal"&gt;debian/pam&lt;/tt&gt;
or is it a file list to be processed like &lt;tt class="docutils literal"&gt;debian/install&lt;/tt&gt;. To make
this distinction, I could add the most common &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; file types
to my static list and then merge the result together.&lt;/p&gt;
&lt;p&gt;Except, I do not want to maintain a full list in &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;.
Fortunately, &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; has a quite extensible plugin infrastructure,
so added a new plugin feature to provide this kind of detail and
now I can outsource the problem! I split my definitions into two and
placed the generic ones in the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;debputy-documentation&lt;/span&gt;&lt;/tt&gt; plugin and
moved the &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; related ones to &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;debhelper-documentation&lt;/span&gt;&lt;/tt&gt;.
Additionally, third-party &lt;tt class="docutils literal"&gt;dh&lt;/tt&gt; addons could provide their own
&lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; plugin to add context to their configuration files.&lt;/p&gt;
&lt;p&gt;So, this gave birth file categories and configuration features, which
described each file on different fronts. As an example,
&lt;tt class="docutils literal"&gt;debian/gbp.conf&lt;/tt&gt; could be tagged as a &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;maint-config&lt;/span&gt;&lt;/tt&gt; to signal
that it is not directly related to the package build but more of a
tool or style preference file. On the other hand, &lt;tt class="docutils literal"&gt;debian/install&lt;/tt&gt;
and &lt;tt class="docutils literal"&gt;debian/debputy.manifest&lt;/tt&gt; would both be tagged as a
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;pkg-helper-config&lt;/span&gt;&lt;/tt&gt;. Files like &lt;tt class="docutils literal"&gt;debian/pam&lt;/tt&gt; were
tagged as &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ppf-file&lt;/span&gt;&lt;/tt&gt; for packager provided file and so on.&lt;/p&gt;
&lt;p&gt;I mentioned configuration features above and those were added
because, I have had a beef with &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt;'s &amp;quot;standard&amp;quot;
configuration file format as read by &lt;tt class="docutils literal"&gt;filearray&lt;/tt&gt; and
&lt;tt class="docutils literal"&gt;filedoublearray&lt;/tt&gt;. They are often considered simple to
understand, but it is hard to know how a tool will actually read
the file. As an example, consider the following:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Will the &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; use &lt;tt class="docutils literal"&gt;filearray&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;filedoublearray&lt;/tt&gt;
or none of them to read the file?  This topic has about 2
bits of entropy.&lt;/li&gt;
&lt;li&gt;Will the config file be executed if it is marked executable
assuming you are using the right compat level?  If it is
executable, does &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;dh-exec&lt;/span&gt;&lt;/tt&gt; allow renaming for this file?
This topic adds 1 or 2 bit of entropy depending on the
context.&lt;/li&gt;
&lt;li&gt;Will the config file be subject to glob expansions? This
topic sounds like a boolean but is a complicated mess.
The globs can be handled either by &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; as it
parses the file for you. In this case, the globs are
applied to every token. However, this is not what
&lt;tt class="docutils literal"&gt;dh_install&lt;/tt&gt; does. Here the last token on each line
is supposed to be a directory and therefore not subject
to globs. Therefore, &lt;tt class="docutils literal"&gt;dh_install&lt;/tt&gt; does the globbing
itself afterwards but only on part of the tokens. So
that is about 2 bits of entropy more. Actually, it gets
worse...&lt;ul&gt;
&lt;li&gt;If the file is executed, &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; will refuse to
expand globs in the output of the command, which was
a deliberate design choice by the original
&lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; maintainer took when he introduced the
feature in &lt;tt class="docutils literal"&gt;debhelper/8.9.12&lt;/tt&gt;. Except, &lt;tt class="docutils literal"&gt;dh_install&lt;/tt&gt;
feature interacts with the design choice and &lt;strong&gt;does&lt;/strong&gt;
enable glob expansion in the tool output, because it
does so manually after its &lt;tt class="docutils literal"&gt;filedoublearray&lt;/tt&gt; call.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;So these &amp;quot;simple&amp;quot; files have way too many combinations of
how they can be interpreted. I figured it would be helpful
if &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; could highlight these difference, so I added
support for those as well.&lt;/p&gt;
&lt;p&gt;Accordingly, &lt;tt class="docutils literal"&gt;debian/install&lt;/tt&gt; is tagged with multiple
tags including &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;dh-executable-config&lt;/span&gt;&lt;/tt&gt; and
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;dh-glob-after-execute&lt;/span&gt;&lt;/tt&gt;. Then, I added a datatable of
these tags, so it would be easy for people to look up what
they meant.&lt;/p&gt;
&lt;p&gt;Ok, this seems like a closed deal, right...?&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="context-context-context"&gt;
&lt;h2&gt;Context, context, context&lt;/h2&gt;
&lt;p&gt;However, the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;dh-executable-config&lt;/span&gt;&lt;/tt&gt; tag among other are
only applicable in compat 9 or later. It does not seem
newbie friendly if you are told that this feature exist,
but then have to read in the extended description that
that it actually does not apply to your package.&lt;/p&gt;
&lt;p&gt;This problem seems fixable. Thanks to &lt;tt class="docutils literal"&gt;dh_assistant&lt;/tt&gt;, it is
easy to figure out which compat level the package is using.
Then tweak some metadata to enable per compat level rules.
With that tags like &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;dh-executable-config&lt;/span&gt;&lt;/tt&gt; only appears
for packages using compat 9 or later.&lt;/p&gt;
&lt;p&gt;Also, &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; should be able to tell you where packager
provided files like &lt;tt class="docutils literal"&gt;debian/pam&lt;/tt&gt; are installed. We already
have the logic for packager provided files that &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;
supports and I am already using &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; engine for
detecting the files. If only the plugin provided metadata gave
me the install pattern, &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; would be able tell you
where this file goes in the package. Indeed, a bit of tweaking
later and setting &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;install-pattern&lt;/span&gt;&lt;/tt&gt; to
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;usr/lib/pam.d/{name}&lt;/span&gt;&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; presented me with the
correct &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;install-path&lt;/span&gt;&lt;/tt&gt; with the package name placing the
&lt;tt class="docutils literal"&gt;{name}&lt;/tt&gt; placeholder.&lt;/p&gt;
&lt;p&gt;Now, I have been using &lt;tt class="docutils literal"&gt;debian/pam&lt;/tt&gt; as an example, because
&lt;tt class="docutils literal"&gt;debian/pam&lt;/tt&gt; is installed into &lt;tt class="docutils literal"&gt;usr/lib/pam.d&lt;/tt&gt; in compat
14. But in earlier compat levels, it was installed into
&lt;tt class="docutils literal"&gt;etc/pam.d&lt;/tt&gt;. Well, I already had an infrastructure for doing
compat file tags. Off we go to add &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;install-pattern&lt;/span&gt;&lt;/tt&gt; to the
complat level infrastructure and now changing the compat level
would change the path. Great. (Bug warning: The value is
off-by-one in the current version of &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt;. This is
fixed in git)&lt;/p&gt;
&lt;p&gt;Also, while we are in this &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;install-pattern&lt;/span&gt;&lt;/tt&gt; business, a
number of &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; config files causes files to be
installed into a fixed directory. Like &lt;tt class="docutils literal"&gt;debian/docs&lt;/tt&gt; which
causes file to be installed into &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/usr/share/docs/{package}&lt;/span&gt;&lt;/tt&gt;.
Surely, we can expand that as well and provide that bit of
context too... and done. (Bug warning: The code currently does
not account for the main documentation package context)&lt;/p&gt;
&lt;p&gt;It is rather common pattern for people to do debian/foo.in
files, because they want to custom generation of
&lt;tt class="docutils literal"&gt;debian/foo&lt;/tt&gt;. Which means if you have &lt;tt class="docutils literal"&gt;debian/foo&lt;/tt&gt; you get
&amp;quot;Oh, let me tell you about &lt;tt class="docutils literal"&gt;debian/foo&lt;/tt&gt; &amp;quot;. Then you rename it
to &lt;tt class="docutils literal"&gt;debian/foo.in&lt;/tt&gt; and the result is &amp;quot;&lt;tt class="docutils literal"&gt;debian/foo.in&lt;/tt&gt; is a
total mystery to me!&amp;quot;. That is suboptimal, so lets detect those
as well as if they were the original file but add a tag saying
that they are a generate template and which file we suspect it
generates.&lt;/p&gt;
&lt;p&gt;Finally, if you use &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;, almost all of the standard
&lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; commands are removed from the sequence, since
&lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; replaces them. It would be weird if these commands
still contributed configuration files when they are not actually
going to be invoked. This mostly happened naturally due to the
way the underlying &lt;tt class="docutils literal"&gt;dh_assistant&lt;/tt&gt; command works. However,
any file mentioned by the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;debhelper-documentation&lt;/span&gt;&lt;/tt&gt; plugin
would still appear unfortunately. So off I went to filter
the list of known configuration files against which &lt;tt class="docutils literal"&gt;dh_&lt;/tt&gt;
commands that &lt;tt class="docutils literal"&gt;dh_assistant&lt;/tt&gt; thought would be used for this
package.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="wrapping-it-up"&gt;
&lt;h2&gt;Wrapping it up&lt;/h2&gt;
&lt;p&gt;I was several layers into this and had to dig myself out.
I have ended up with a lot of data and metadata. But it
was quite difficult for me to arrange the output in a user
friendly manner.&lt;/p&gt;
&lt;p&gt;However, all this data did seem like it would be useful any
tool that wants to understand more about the package.
So to get out of the rabbit hole, I for now
wrapped all of this into JSON and now we have a
&lt;tt class="docutils literal"&gt;debputy &lt;span class="pre"&gt;tool-support&lt;/span&gt; &lt;span class="pre"&gt;annotate-debian-directory&lt;/span&gt;&lt;/tt&gt;
command that might be useful for other tools.&lt;/p&gt;
&lt;p&gt;To try it out, you can try the following demo:&lt;/p&gt;
&lt;!-- code-block::console
$ apt satisfy 'debhelper (&gt;= 13.13), dh-debputy (&gt;= 0.1.19)'
$ mkdir -p debputy-feature-test/debian &amp;&amp; cd debputy-feature-test
$ cat &lt;&lt;EOF &gt; debian/control
Source: test
Build-Depends: debhelper-compat (= 13),

Package: test
Architecture: any
EOF
$ touch debian/install debian/docs debian/manpages debian/pam
$ DH_COMPAT=14 debputy tool-support annotate-debian-directory &gt; compat-14.json
$ DH_COMPAT=7 tool-support annotate-debian-directory &gt; compat-7.json
# Use whatever tool that makes sense for you to compare the two files.
# To get context to the tags, you can use
$ debputy tool-support export-reference-data - -output-format=json
# Or via its ASCII art text format:
$ debputy tool-support export-reference-data file-categories --&gt;
&lt;p&gt;In another day, I will figure out how to structure this output so it is
useful for non-machine consumers. Suggestions are welcome. :)&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="limitations-of-the-approach"&gt;
&lt;h2&gt;Limitations of the approach&lt;/h2&gt;
&lt;p&gt;As a closing remark, I should probably remind people that this feature relies
heavily on declarative features. These include:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;When determining which commands are relevant, using &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;Build-Depends:&lt;/span&gt; &lt;span class="pre"&gt;dh-sequence-foo&lt;/span&gt;&lt;/tt&gt;
is much more reliable than configuring it via the Turing complete configuration we call
&lt;tt class="docutils literal"&gt;debian/rules&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;When &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; commands use &lt;tt class="docutils literal"&gt;NOOP&lt;/tt&gt; promise hints, &lt;tt class="docutils literal"&gt;dh_assistant&lt;/tt&gt; can
&amp;quot;see&amp;quot; the config files listed those hints, meaning the file will at least
be detected. For new introspectable hint and the &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; plugin, it is probably
better to wait until the dust settles a bit before adding any of those.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can help yourself and others to better results by using the declarative way rather
than using &lt;tt class="docutils literal"&gt;debian/rules&lt;/tt&gt;, which is the bane of all introspection!&lt;/p&gt;
&lt;/div&gt;
</content><category term="Debian, debputy"></category><category term="debian"></category><category term="debputy"></category><category term="pdo"></category></entry><entry><title>Making debputy: Writing declarative parsing logic</title><link href="https://people.debian.org/~nthykier/blog/2024/making-debputy-writing-declarative-parsing-logic.html" rel="alternate"></link><published>2024-01-20T17:10:00+00:00</published><updated>2024-01-20T17:10:00+00:00</updated><author><name>Niels Thykier</name></author><id>tag:people.debian.org,2024-01-20:/~nthykier/blog/2024/making-debputy-writing-declarative-parsing-logic.html</id><content type="html">&lt;p&gt;In this blog post, I will cover how &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; parses its manifest and the
conceptual improvements I did to make parsing of the manifest easier.&lt;/p&gt;
&lt;p&gt;All instructions to &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; are provided via the &lt;tt class="docutils literal"&gt;debian/debputy.manifest&lt;/tt&gt; file and
said manifest is written in the YAML format.  After the YAML parser has read the
basic file structure, &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; does another pass over the data to extract the
information from the basic structure.  As an example, the following YAML file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;manifest-version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;0.1&amp;quot;&lt;/span&gt;
&lt;span class="nt"&gt;installations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;install&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;foo&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;dest-dir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;usr/bin&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;would be transformed by the YAML parser into a structure resembling:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;&amp;quot;manifest-version&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;0.1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;&amp;quot;installations&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="s2"&gt;&amp;quot;install&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="s2"&gt;&amp;quot;source&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="s2"&gt;&amp;quot;dest-dir&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;usr/bin&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This structure is then what &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; does a pass on to translate this into
an even higher level format where the &lt;tt class="docutils literal"&gt;&amp;quot;install&amp;quot;&lt;/tt&gt; part is translated into
an &lt;tt class="docutils literal"&gt;InstallRule&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;In the original prototype of &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;, I would hand-write functions to extract
the data that should be transformed into the internal in-memory high level format.
However, it was quite tedious. Especially because I wanted to catch every possible
error condition and report &amp;quot;You are missing the required field X at Y&amp;quot; rather
than the opaque &lt;tt class="docutils literal"&gt;KeyError: X&lt;/tt&gt; message that would have been the default.&lt;/p&gt;
&lt;p&gt;Beyond being tedious, it was also quite error prone. As an example, in
&lt;tt class="docutils literal"&gt;debputy/0.1.4&lt;/tt&gt; I added support for the &lt;tt class="docutils literal"&gt;install&lt;/tt&gt; rule and you should allegedly
have been able to add a &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;dest-dir:&lt;/span&gt;&lt;/tt&gt; or an &lt;tt class="docutils literal"&gt;as:&lt;/tt&gt; inside it.  Except I crewed up the
code and &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; was attempting to look up these keywords from a dict that
could never have them.&lt;/p&gt;
&lt;p&gt;Hand-writing these parsers were so annoying that it demotivated me from making
manifest related changes to &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; simply because I did not want to code
the parsing logic. When I got this realization, I figured I had to solve this
problem better.&lt;/p&gt;
&lt;p&gt;While reflecting on this, I also considered that I eventually wanted plugins
to be able to add vocabulary to the manifest. If the API was &amp;quot;provide a
callback to extract the details of whatever the user provided here&amp;quot;, then the
result would be bad.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Most plugins would probably throw &lt;tt class="docutils literal"&gt;KeyError: X&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;ValueError&lt;/tt&gt; style
errors for quite a while. Worst case, they would end on my table because
the user would have a hard time telling where &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; ends and where
the plugins starts. &amp;quot;Best&amp;quot; case, I would teach &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; to say &amp;quot;This
poor error message was brought to you by plugin foo. Go complain to
them&amp;quot;. Either way, it would be a bad user experience.&lt;/li&gt;
&lt;li&gt;This even assumes plugin providers would actually bother writing manifest
parsing code. If it is that difficult, then just providing a custom file
in debian might tempt plugin providers and that would undermine the idea
of having the manifest be the sole input for &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;So beyond me being unsatisfied with the current situation, it was also clear
to me that I needed to come up with a better solution if I wanted externally
provided plugins for &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;. To put a bit more perspective on what I
expected from the end result:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;It had to cover as many parsing errors as possible. An error case this
code would handle for you, would be an error where I could ensure it
sufficient degree of detail and context for the user.&lt;/li&gt;
&lt;li&gt;It should be type-safe / provide typing support such that IDEs/mypy could
help you when you work on the parsed result.&lt;/li&gt;
&lt;li&gt;It had to support &amp;quot;normalization&amp;quot; of the input, such as&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="c1"&gt;# User provides&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;install&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="c1"&gt;# Which is normalized into:&lt;/span&gt;
&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;install&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="nt"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;


&lt;span class="l l-Scalar l-Scalar-Plain"&gt;4) It must be simple to tell ``debputy`` what input you expected.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;At this point, I remembered that I had seen a Python (PYPI) package where
you could give it a &lt;tt class="docutils literal"&gt;TypedDict&lt;/tt&gt; and an arbitrary input (Sadly, I do not
remember the name). The package would then validate the said input against the
&lt;tt class="docutils literal"&gt;TypedDict&lt;/tt&gt;. If the match was successful, you would get the result back
casted as the &lt;tt class="docutils literal"&gt;TypedDict&lt;/tt&gt;. If the match was unsuccessful, the code would
raise an error for you. Conceptually, this seemed to be a good starting
point for where I wanted to be.&lt;/p&gt;
&lt;p&gt;Then I looked a bit on the normalization requirement (point 3).
What is really going on here is that you have two &amp;quot;schemas&amp;quot; for the input.
One is what the programmer will see (the normalized form) and the other is
what the user can input (the manifest form). The problem is providing an
automatic normalization from the user input to the simplified programmer
structure.  To expand a bit on the following example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# User provides&lt;/span&gt;
&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;install&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;
&lt;span class="c1"&gt;# Which is normalized into:&lt;/span&gt;
&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;install&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;foo&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Given that &lt;tt class="docutils literal"&gt;install&lt;/tt&gt; has the attributes &lt;tt class="docutils literal"&gt;source&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;sources&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;dest-dir&lt;/span&gt;&lt;/tt&gt;,
&lt;tt class="docutils literal"&gt;as&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;into&lt;/tt&gt;, and &lt;tt class="docutils literal"&gt;when&lt;/tt&gt;, how exactly would you automatically normalize
&lt;tt class="docutils literal"&gt;&amp;quot;foo&amp;quot;&lt;/tt&gt; (str) into &lt;tt class="docutils literal"&gt;source: &amp;quot;foo&amp;quot;&lt;/tt&gt;?  Even if the code filtered by &amp;quot;type&amp;quot; for these
attributes, you would end up with at least &lt;tt class="docutils literal"&gt;source&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;dest-dir&lt;/span&gt;&lt;/tt&gt;, and &lt;tt class="docutils literal"&gt;as&lt;/tt&gt;
as candidates.  Turns out that &lt;tt class="docutils literal"&gt;TypedDict&lt;/tt&gt; actually got this covered. But
the Python package was not going in this direction, so I parked it here and
started looking into doing my own.&lt;/p&gt;
&lt;p&gt;At this point, I had a general idea of what I wanted. When defining an extension
to the manifest, the plugin would provide &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; with one or two
definitions of &lt;tt class="docutils literal"&gt;TypedDict&lt;/tt&gt;.  The first one would be the &amp;quot;parsed&amp;quot; or &amp;quot;target&amp;quot; format, which
would be the normalized form that plugin provider wanted to work on. For this
example, lets look at an earlier version of the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;install-examples&lt;/span&gt;&lt;/tt&gt; rule:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Example input matching this typed dict.&lt;/span&gt;
&lt;span class="c1"&gt;#   {&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;source&amp;quot;: [&amp;quot;foo&amp;quot;]&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;into&amp;quot;: [&amp;quot;pkg&amp;quot;]&lt;/span&gt;
&lt;span class="c1"&gt;#   }&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InstallExamplesTargetFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TypedDict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Which source files to install (dest-dir is fixed)&lt;/span&gt;
    &lt;span class="n"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="c1"&gt;# Which package(s) that should have these files installed.&lt;/span&gt;
    &lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NotRequired&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In this form, the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;install-examples&lt;/span&gt;&lt;/tt&gt; has two attributes - both are list of
strings.  On the flip side, what the user can input would look something like
this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Example input matching this typed dict.&lt;/span&gt;
&lt;span class="c1"&gt;#   {&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;source&amp;quot;: &amp;quot;foo&amp;quot;&lt;/span&gt;
&lt;span class="c1"&gt;#       &amp;quot;into&amp;quot;: &amp;quot;pkg&amp;quot;&lt;/span&gt;
&lt;span class="c1"&gt;#   }&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InstallExamplesManifestFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TypedDict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Note that sources here is split into source (str) vs. sources (List[str])&lt;/span&gt;
    &lt;span class="n"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NotRequired&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
    &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NotRequired&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="c1"&gt;# We allow the user to write ``into: foo`` in addition to ``into: [foo]``&lt;/span&gt;
    &lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Union&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;

&lt;span class="n"&gt;FullInstallExamplesManifestFormat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Union&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;InstallExamplesManifestFormat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The idea was that the plugin provider would use these two definitions to tell
&lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; how to parse &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;install-examples&lt;/span&gt;&lt;/tt&gt;. Pseudo-registration code could
look something like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;normalized_form&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;InstallExamplesTargetFormat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;InstallRule&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;  &lt;span class="c1"&gt;# Do something with the normalized form and return an InstallRule.&lt;/span&gt;

&lt;span class="n"&gt;concept_debputy_api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_install_rule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;keyword&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;install-examples&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;target_form&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;InstallExamplesTargetFormat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;manifest_form&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;FullInstallExamplesManifestFormat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_handler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This was my conceptual target and while the current actual API ended up being slightly different,
the core concept remains the same.&lt;/p&gt;
&lt;div class="section" id="from-concept-to-basic-implementation"&gt;
&lt;h2&gt;From concept to basic implementation&lt;/h2&gt;
&lt;p&gt;Building this code is kind like swallowing an elephant. There was no way I would just sit down
and write it from one end to the other.  So the first prototype of this did not have all the
features it has now.&lt;/p&gt;
&lt;p&gt;Spoiler warning, these next couple of sections will contain some Python typing details.
When reading this, it might be helpful to know things such as &lt;tt class="docutils literal"&gt;Union[str, List[str]]&lt;/tt&gt; being
the Python type for either a &lt;tt class="docutils literal"&gt;str&lt;/tt&gt; (string) or a &lt;tt class="docutils literal"&gt;List[str]&lt;/tt&gt; (list of strings). If typing
makes your head spin, these sections might less interesting for you.&lt;/p&gt;
&lt;p&gt;To build this required a lot of playing around with Python's introspection and
typing APIs. My very first draft only had one &amp;quot;schema&amp;quot; (the normalized form) and
had the following features:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Read &lt;tt class="docutils literal"&gt;TypedDict.__required_attributes__&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;TypedDict.__optional_attributes__&lt;/tt&gt; to
determine which attributes where present and which were required. This was used for
reporting errors when the input did not match.&lt;/li&gt;
&lt;li&gt;Read the types of the provided &lt;tt class="docutils literal"&gt;TypedDict&lt;/tt&gt;, strip the &lt;tt class="docutils literal"&gt;Required&lt;/tt&gt; / &lt;tt class="docutils literal"&gt;NotRequired&lt;/tt&gt;
markers and use basic &lt;tt class="docutils literal"&gt;isinstance&lt;/tt&gt; checks based on the resulting type for &lt;tt class="docutils literal"&gt;str&lt;/tt&gt; and
&lt;tt class="docutils literal"&gt;List[str]&lt;/tt&gt;.  Again, used for reporting errors when the input did not match.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;This prototype did not take a long (I remember it being within a day) and worked surprisingly
well though with some poor error messages here and there. Now came the first challenge,
adding the manifest format schema plus relevant normalization rules. The very first
normalization I did was transforming &lt;tt class="docutils literal"&gt;into: Union[str, List[str]]&lt;/tt&gt; into &lt;tt class="docutils literal"&gt;into: List[str]&lt;/tt&gt;.
At that time, &lt;tt class="docutils literal"&gt;source&lt;/tt&gt; was not a separate attribute. Instead, &lt;tt class="docutils literal"&gt;sources&lt;/tt&gt; was a
&lt;tt class="docutils literal"&gt;Union[str, List[str]]&lt;/tt&gt;, so it was the only normalization I needed for all my
use-cases at the time.&lt;/p&gt;
&lt;p&gt;There are two problems when writing a normalization.  First is determining what the &amp;quot;source&amp;quot;
type is, what the target type is and how they relate.  The second is providing a runtime
rule for normalizing from the manifest format into the target format.  Keeping it simple,
the runtime normalizer for &lt;tt class="docutils literal"&gt;Union[str, List[str]] &lt;span class="pre"&gt;-&amp;gt;&lt;/span&gt; List[str]&lt;/tt&gt; was written as:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;normalize_into_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Union&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This basic form basically works for all types (assuming none of the types will have &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;List[List[...]]&lt;/span&gt;&lt;/tt&gt;).
The logic for determining when this rule is applicable is slightly more involved.  My current code
is about 100 lines of Python code that would probably lose most of the casual readers.
&lt;a class="reference external" href="https://salsa.debian.org/debian/debputy/-/blob/debian/0.1.17/src/debputy/manifest_parser/declarative_parser.py?ref_type=tags#L1171"&gt;For the interested, you are looking for _union_narrowing in declarative_parser.py&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;With this, when the manifest format had &lt;tt class="docutils literal"&gt;Union[str, List[str]]&lt;/tt&gt; and the target format
had &lt;tt class="docutils literal"&gt;List[str]&lt;/tt&gt; the generated parser would silently map a string into a list of strings
for the plugin provider.&lt;/p&gt;
&lt;p&gt;But with that in place, I had covered the basics of what I needed to get started. I was
quite excited about this milestone of having my first keyword parsed without handwriting
the parser logic (at the expense of writing a more generic parse-generator framework).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="adding-the-first-parse-hint"&gt;
&lt;h2&gt;Adding the first parse hint&lt;/h2&gt;
&lt;p&gt;With the basic implementation done, I looked at what to do next. As mentioned, at the time
&lt;tt class="docutils literal"&gt;sources&lt;/tt&gt; in the manifest format was &lt;tt class="docutils literal"&gt;Union[str, List[str]]&lt;/tt&gt; and I considered to split
into a &lt;tt class="docutils literal"&gt;source: str&lt;/tt&gt; and a &lt;tt class="docutils literal"&gt;sources: List[str]&lt;/tt&gt; on the manifest side while keeping
the normalized form as &lt;tt class="docutils literal"&gt;sources: List[str]&lt;/tt&gt;. I ended up committing to this change and
that meant I had to solve the problem getting my parser generator to understand the
situation:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Map from&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InstallExamplesManifestFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TypedDict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Note that sources here is split into source (str) vs. sources (List[str])&lt;/span&gt;
    &lt;span class="n"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NotRequired&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
    &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NotRequired&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="c1"&gt;# We allow the user to write ``into: foo`` in addition to ``into: [foo]``&lt;/span&gt;
    &lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Union&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;

&lt;span class="c1"&gt;# ... into&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InstallExamplesTargetFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TypedDict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Which source files to install (dest-dir is fixed)&lt;/span&gt;
    &lt;span class="n"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="c1"&gt;# Which package(s) that should have these files installed.&lt;/span&gt;
    &lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NotRequired&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There are two related problems to solve here:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;How will the parser generator understand that &lt;tt class="docutils literal"&gt;source&lt;/tt&gt; should be normalized
and then mapped into &lt;tt class="docutils literal"&gt;sources&lt;/tt&gt;?&lt;/li&gt;
&lt;li&gt;Once that is solved, the parser generator has to understand that while &lt;tt class="docutils literal"&gt;source&lt;/tt&gt;
and &lt;tt class="docutils literal"&gt;sources&lt;/tt&gt; are declared as &lt;tt class="docutils literal"&gt;NotRequired&lt;/tt&gt;, they are part of a &lt;tt class="docutils literal"&gt;exactly one of&lt;/tt&gt;
rule (since &lt;tt class="docutils literal"&gt;sources&lt;/tt&gt; in the target form is &lt;tt class="docutils literal"&gt;Required&lt;/tt&gt;). This mainly came down
to extra book keeping and an extra layer of validation once the previous step is solved.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;While working on all of this type introspection for Python, I had noted the &lt;tt class="docutils literal"&gt;Annotated[X, &lt;span class="pre"&gt;...]&lt;/span&gt;&lt;/tt&gt;
type. It is basically a fake type that enables you to attach metadata into the type system.
A very random example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# For all intents and purposes, ``foo`` is a string despite all the ``Annotated`` stuff.&lt;/span&gt;
&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;hello world&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;my string here&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The exciting thing is that you can put arbitrary details into the type field and read it
out again in your introspection code. Which meant, I could add &amp;quot;parse hints&amp;quot; into the type.
Some &amp;quot;quick&amp;quot; prototyping later (a day or so), I got the following to work:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Map from&lt;/span&gt;
&lt;span class="c1"&gt;#     {&lt;/span&gt;
&lt;span class="c1"&gt;#        &amp;quot;source&amp;quot;: &amp;quot;foo&amp;quot;  # (or &amp;quot;sources&amp;quot;: [&amp;quot;foo&amp;quot;])&lt;/span&gt;
&lt;span class="c1"&gt;#        &amp;quot;into&amp;quot;: &amp;quot;pkg&amp;quot;&lt;/span&gt;
&lt;span class="c1"&gt;#     }&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InstallExamplesManifestFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TypedDict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Note that sources here is split into source (str) vs. sources (List[str])&lt;/span&gt;
    &lt;span class="n"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NotRequired&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
    &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NotRequired&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;DebputyParseHint&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;target_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;sources&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="c1"&gt;# We allow the user to write ``into: foo`` in addition to ``into: [foo]``&lt;/span&gt;
    &lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Union&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;

&lt;span class="c1"&gt;# ... into&lt;/span&gt;
&lt;span class="c1"&gt;#     {&lt;/span&gt;
&lt;span class="c1"&gt;#        &amp;quot;source&amp;quot;: [&amp;quot;foo&amp;quot;]&lt;/span&gt;
&lt;span class="c1"&gt;#        &amp;quot;into&amp;quot;: [&amp;quot;pkg&amp;quot;]&lt;/span&gt;
&lt;span class="c1"&gt;#     }&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InstallExamplesTargetFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TypedDict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Which source files to install (dest-dir is fixed)&lt;/span&gt;
    &lt;span class="n"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="c1"&gt;# Which package(s) that should have these files installed.&lt;/span&gt;
    &lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NotRequired&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Without me (as a plugin provider) writing a line of code, I can have &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; rename
or &amp;quot;merge&amp;quot; attributes from the manifest form into the normalized form. Obviously, this
required me (as the &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; maintainer) to write a lot code so other me and future
plugin providers did not have to write it.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="high-level-typing"&gt;
&lt;h2&gt;High level typing&lt;/h2&gt;
&lt;p&gt;At this point, basic normalization between one mapping to another mapping form worked.
But one thing irked me with these install rules. The &lt;tt class="docutils literal"&gt;into&lt;/tt&gt; was a list of strings
when the parser handed them over to me. However, I needed to map them to the actual
&lt;tt class="docutils literal"&gt;BinaryPackage&lt;/tt&gt; (for technical reasons).  While I felt I was careful with my manual
mapping, I knew this was exactly the kind of case where a busy programmer would skip
the &amp;quot;is this a known package name&amp;quot; check and some user would typo their package
resulting in an opaque &lt;tt class="docutils literal"&gt;KeyError: foo&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Side note: &amp;quot;Some user&amp;quot; was me today and I was super glad to see &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; tell me
that I had typoed a package name (I would have been more happy if I had remembered to
use &lt;tt class="docutils literal"&gt;debputy &lt;span class="pre"&gt;check-manifest&lt;/span&gt;&lt;/tt&gt;, so I did not have to wait through the upstream part
of the build that happened before &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; passed control to &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;...)&lt;/p&gt;
&lt;p&gt;I thought adding this feature would be simple enough. It basically needs two things:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Conversion table where the parser generator can tell that &lt;tt class="docutils literal"&gt;BinaryPackage&lt;/tt&gt; requires
an input of &lt;tt class="docutils literal"&gt;str&lt;/tt&gt; and a callback to map from &lt;tt class="docutils literal"&gt;str&lt;/tt&gt; to &lt;tt class="docutils literal"&gt;BinaryPackage&lt;/tt&gt;.
(That is probably lie. I think the conversion table came later, but honestly I do
remember and I am not digging into the git history for this one)&lt;/li&gt;
&lt;li&gt;At runtime, said callback needed access to the list of known packages, so it could
resolve the provided string.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;It was not super difficult given the existing infrastructure, but it did take some hours
of coding and debugging.  Additionally, I added a parse hint to support making the
&lt;tt class="docutils literal"&gt;into&lt;/tt&gt; conditional based on whether it was a single binary package. With this done,
you could now write something like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Map from&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InstallExamplesManifestFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TypedDict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Note that sources here is split into source (str) vs. sources (List[str])&lt;/span&gt;
    &lt;span class="n"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NotRequired&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
    &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NotRequired&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;DebputyParseHint&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;target_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;sources&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="c1"&gt;# We allow the user to write ``into: foo`` in addition to ``into: [foo]``&lt;/span&gt;
    &lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Union&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;BinaryPackage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;BinaryPackage&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;

&lt;span class="c1"&gt;# ... into&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InstallExamplesTargetFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TypedDict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Which source files to install (dest-dir is fixed)&lt;/span&gt;
    &lt;span class="n"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="c1"&gt;# Which package(s) that should have these files installed.&lt;/span&gt;
    &lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;NotRequired&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;BinaryPackage&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;DebputyParseHint&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;required_when_multi_binary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Code-wise, I still had to check for &lt;tt class="docutils literal"&gt;into&lt;/tt&gt; being absent and providing a default for that
case (that is still true in the current codebase - I will hopefully fix that eventually). But
I now had less room for mistakes and a standardized error message when you misspell the package
name, which was a plus.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-added-side-effect-introspection"&gt;
&lt;h2&gt;The added side-effect - Introspection&lt;/h2&gt;
&lt;p&gt;A lovely side-effect of all the parsing logic being provided to &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; in a declarative
form was that the generated parser snippets had fields containing all expected attributes
with their types, which attributes were required, etc.  This meant that adding an
introspection feature where you can ask &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; &amp;quot;What does an &lt;tt class="docutils literal"&gt;install&lt;/tt&gt; rule look like?&amp;quot;
was quite easy. The code base already knew all of this, so the &amp;quot;hard&amp;quot; part was resolving the
input the to concrete rule and then rendering it to the user.&lt;/p&gt;
&lt;p&gt;I added this feature recently along with the ability to provide online documentation for
parser rules. I covered that in more details in my blog post
&lt;a class="reference external" href="https://people.debian.org/~nthykier/blog/2023/providing-online-reference-documentation-for-debputy.html"&gt;Providing online reference documentation for debputy&lt;/a&gt;
in case you are interested. :)&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="wrapping-it-up"&gt;
&lt;h2&gt;Wrapping it up&lt;/h2&gt;
&lt;p&gt;This was a short insight into how &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; parses your input. With this declarative
technique:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The parser engine handles most of the error reporting meaning users get most of the errors
in a standard format without the plugin provider having to spend any effort on it.
There will be some effort in more complex cases. But the common cases are done for you.&lt;/li&gt;
&lt;li&gt;It is easy to provide flexibility to users while avoiding having to write code to normalize
the user input into a simplified programmer oriented format.&lt;/li&gt;
&lt;li&gt;The parser handles mapping from basic types into higher forms for you.  These days, we have
high level types like &lt;tt class="docutils literal"&gt;FileSystemMode&lt;/tt&gt; (either an octal or a symbolic mode), different
kind of file system matches depending on whether globs should be performed, etc.  These
types includes their own validation and parsing rules that &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; handles for you.&lt;/li&gt;
&lt;li&gt;Introspection and support for providing online reference documentation. Also, &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;
checks that the provided attribute documentation covers all the attributes in the manifest
form. If you add a new attribute, &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; will remind you if you forget to document
it as well. :)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;In this way everybody wins. Yes, writing this parser generator code was more enjoyable than
writing the ad-hoc manual parsers it replaced. :)&lt;/p&gt;
&lt;/div&gt;
</content><category term="Debian, debputy"></category><category term="debian"></category><category term="debputy"></category><category term="pdo"></category></entry><entry><title>Providing online reference documentation for debputy</title><link href="https://people.debian.org/~nthykier/blog/2023/providing-online-reference-documentation-for-debputy.html" rel="alternate"></link><published>2023-11-26T17:25:00+00:00</published><updated>2023-11-26T17:25:00+00:00</updated><author><name>Niels Thykier</name></author><id>tag:people.debian.org,2023-11-26:/~nthykier/blog/2023/providing-online-reference-documentation-for-debputy.html</id><content type="html">&lt;p&gt;I do not think seasoned Debian contributors quite appreciate how much knowledge we
have picked up and internalized. As an example, when I need to look up
documentation for debhelper, I generally know which manpage to look in. I suspect
most long time contributors would be able to a similar thing (maybe down 2-3
manpages).  But new contributors does not have the luxury of years of experience.
This problem is by no means unique to &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;One thing that &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; does very well, is that it is hard for users to tell
where a addon &amp;quot;starts&amp;quot; and &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; &amp;quot;ends&amp;quot;. It is clear you use addons, but
the transition in and out of third party provided tools is generally smooth. This
is a sign that things &amp;quot;just work(tm)&amp;quot;.&lt;/p&gt;
&lt;p&gt;Except when it comes to documentation.  Here, &lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt;'s static documentation
does not include documentation for third party tooling. If you think from a
&lt;tt class="docutils literal"&gt;debhelper&lt;/tt&gt; maintainer's perspective, this seems obvious. Embedding documentation
for all the third-party code would be very hard work, a layer-violation, etc.. But
from a user perspective, we should not have to care &amp;quot;who&amp;quot; provides &amp;quot;what&amp;quot;. As as
user, I want to understand how this works and the more hoops I have to jump through
to get that understanding, the more frustrated I will be with the toolstack.&lt;/p&gt;
&lt;p&gt;With this, I came to the conclusion that the best way to help users and solve the
problem of finding the documentation was to provide &amp;quot;online documentation&amp;quot;. It
should be possible to ask &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;, &amp;quot;What attributes can I use in
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;install-man&lt;/span&gt;&lt;/tt&gt;?&amp;quot; or &amp;quot;What does &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;path-metadata&lt;/span&gt;&lt;/tt&gt; do?&amp;quot;. Additionally, the lookup
should work the same no matter if &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; provided the feature or some
third-party plugin did.  In the future, perhaps also other types of documentation
such as tutorials or how-to guides.&lt;/p&gt;
&lt;p&gt;Below, I have some tentative results of my work so far. There are some
improvements to be done. Notably, the commands for these documentation features
are still treated a &amp;quot;plugin&amp;quot; subcommand features and should probably have its own
top level &amp;quot;ask-me-anything&amp;quot; subcommand in the future.&lt;/p&gt;
&lt;div class="section" id="automatic-discard-rules"&gt;
&lt;h2&gt;Automatic discard rules&lt;/h2&gt;
&lt;p&gt;Since the introduction of install rules, &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; has included an automatic
filter mechanism that prunes out unwanted content. In 0.1.9, these filters
have been named &amp;quot;Automatic discard rules&amp;quot; and you can now ask &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; to
list them.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;debputy&lt;span class="w"&gt; &lt;/span&gt;plugin&lt;span class="w"&gt; &lt;/span&gt;list&lt;span class="w"&gt; &lt;/span&gt;automatic-discard-rules
&lt;span class="go"&gt;+-----------------------+-------------+&lt;/span&gt;
&lt;span class="go"&gt;| Name                  | Provided By |&lt;/span&gt;
&lt;span class="go"&gt;+-----------------------+-------------+&lt;/span&gt;
&lt;span class="go"&gt;| python-cache-files    | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| la-files              | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| backup-files          | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| version-control-paths | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| gnu-info-dir-file     | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| debian-dir            | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| doxygen-cruft-files   | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;+-----------------------+-------------+&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For these rules, the provider can both provide a description but also an example
of their usage.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;debputy&lt;span class="w"&gt; &lt;/span&gt;plugin&lt;span class="w"&gt; &lt;/span&gt;show&lt;span class="w"&gt; &lt;/span&gt;automatic-discard-rules&lt;span class="w"&gt; &lt;/span&gt;la-files
&lt;span class="go"&gt;Automatic Discard Rule: la-files&lt;/span&gt;
&lt;span class="go"&gt;================================&lt;/span&gt;

&lt;span class="go"&gt;Documentation: Discards any .la files beneath /usr/lib&lt;/span&gt;

&lt;span class="go"&gt;Example&lt;/span&gt;
&lt;span class="go"&gt;-------&lt;/span&gt;

&lt;span class="go"&gt;    /usr/lib/libfoo.la        &amp;lt;&amp;lt; Discarded (directly by the rule)&lt;/span&gt;
&lt;span class="go"&gt;    /usr/lib/libfoo.so.1.0.0&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The example is a &lt;em&gt;live&lt;/em&gt; example.  That is, the provider will provide &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;
with a scenario &lt;strong&gt;and&lt;/strong&gt; the expected outcome of that scenario. Here is the concrete
code in &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; that registers this example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;automatic_discard_rule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;&amp;quot;la-files&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;_debputy_prune_la_files&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;rule_reference_documentation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Discards any .la files beneath /usr/lib&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;examples&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;automatic_discard_rule_example&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;usr/lib/libfoo.la&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;usr/lib/libfoo.so.1.0.0&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When showing the example, &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; will &lt;em&gt;validate&lt;/em&gt; the example matches what the plugin
provider intended. Lets say I was to introduce a bug in the code, so that the discard rule
no longer worked. Then &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; would start to show the following:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;Output&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;code&lt;span class="w"&gt; &lt;/span&gt;or&lt;span class="w"&gt; &lt;/span&gt;example&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;broken
&lt;span class="gp"&gt;$ &lt;/span&gt;debputy&lt;span class="w"&gt; &lt;/span&gt;plugin&lt;span class="w"&gt; &lt;/span&gt;show&lt;span class="w"&gt; &lt;/span&gt;automatic-discard-rules&lt;span class="w"&gt; &lt;/span&gt;la-files
&lt;span class="go"&gt;[...]&lt;/span&gt;
&lt;span class="go"&gt;Automatic Discard Rule: la-files&lt;/span&gt;
&lt;span class="go"&gt;================================&lt;/span&gt;

&lt;span class="go"&gt;Documentation: Discards any .la files beneath /usr/lib&lt;/span&gt;

&lt;span class="go"&gt;Example&lt;/span&gt;
&lt;span class="go"&gt;-------&lt;/span&gt;

&lt;span class="go"&gt;    /usr/lib/libfoo.la        !! INCONSISTENT (code: keep, example: discard)&lt;/span&gt;
&lt;span class="go"&gt;    /usr/lib/libfoo.so.1.0.0&lt;/span&gt;
&lt;span class="go"&gt;debputy: warning: The example was inconsistent. Please file a bug against the plugin debputy&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Obviously, it would be better if this validation could be added directly as a plugin test,
so the CI pipeline would catch it.  That is one my personal TODO list. :)&lt;/p&gt;
&lt;p&gt;One final remark about automatic discard rules before moving on. In 0.1.9, &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; will also
list any path automatically discarded by one of these rules in the build output to make sure
that the automatic discard rule feature is more discoverable.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="plugable-manifest-rules-like-the-install-rule"&gt;
&lt;h2&gt;Plugable manifest rules like the &lt;tt class="docutils literal"&gt;install&lt;/tt&gt; rule&lt;/h2&gt;
&lt;p&gt;In the manifest, there are several places where rules can be provided by plugins.  To make
life easier for users, &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; can now since 0.1.8 list all provided rules:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;debputy&lt;span class="w"&gt; &lt;/span&gt;plugin&lt;span class="w"&gt; &lt;/span&gt;list&lt;span class="w"&gt; &lt;/span&gt;plugable-manifest-rules
&lt;span class="go"&gt;+-------------------------------+------------------------------+-------------+&lt;/span&gt;
&lt;span class="go"&gt;| Rule Name                     | Rule Type                    | Provided By |&lt;/span&gt;
&lt;span class="go"&gt;+-------------------------------+------------------------------+-------------+&lt;/span&gt;
&lt;span class="go"&gt;| install                       | InstallRule                  | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| install-docs                  | InstallRule                  | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| install-examples              | InstallRule                  | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| install-doc                   | InstallRule                  | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| install-example               | InstallRule                  | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| install-man                   | InstallRule                  | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| discard                       | InstallRule                  | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| move                          | TransformationRule           | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| remove                        | TransformationRule           | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| [...]                         | [...]                        | [...]       |&lt;/span&gt;
&lt;span class="go"&gt;| remove                        | DpkgMaintscriptHelperCommand | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| rename                        | DpkgMaintscriptHelperCommand | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| cross-compiling               | ManifestCondition            | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| can-execute-compiled-binaries | ManifestCondition            | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| run-build-time-tests          | ManifestCondition            | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| [...]                         | [...]                        | [...]       |&lt;/span&gt;
&lt;span class="go"&gt;+-------------------------------+------------------------------+-------------+&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;(Output trimmed a bit for space reasons)&lt;/p&gt;
&lt;p&gt;And you can then ask &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; to describe any of these rules:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;debputy&lt;span class="w"&gt; &lt;/span&gt;plugin&lt;span class="w"&gt; &lt;/span&gt;show&lt;span class="w"&gt; &lt;/span&gt;plugable-manifest-rules&lt;span class="w"&gt; &lt;/span&gt;install
&lt;span class="go"&gt;Generic install (`install`)&lt;/span&gt;
&lt;span class="go"&gt;===========================&lt;/span&gt;

&lt;span class="go"&gt;The generic `install` rule can be used to install arbitrary paths into packages&lt;/span&gt;
&lt;span class="go"&gt;and is *similar* to how `dh_install` from debhelper works.  It is a two &amp;quot;primary&amp;quot; uses.&lt;/span&gt;

&lt;span class="go"&gt;  1) The classic &amp;quot;install into directory&amp;quot; similar to the standard `dh_install`&lt;/span&gt;
&lt;span class="go"&gt;  2) The &amp;quot;install as&amp;quot; similar to `dh-exec`&amp;#39;s `foo =&amp;gt; bar` feature.&lt;/span&gt;

&lt;span class="go"&gt;Attributes:&lt;/span&gt;
&lt;span class="go"&gt; - `source` (conditional): string&lt;/span&gt;
&lt;span class="go"&gt;   `sources` (conditional): List of string&lt;/span&gt;

&lt;span class="go"&gt;   A path match (`source`) or a list of path matches (`sources`) defining the&lt;/span&gt;
&lt;span class="go"&gt;   source path(s) to be installed. [...]&lt;/span&gt;

&lt;span class="go"&gt; - `dest-dir` (optional): string&lt;/span&gt;

&lt;span class="go"&gt;   A path defining the destination *directory*. [...]&lt;/span&gt;

&lt;span class="go"&gt; - `into` (optional): string or a list of string&lt;/span&gt;

&lt;span class="go"&gt;   A path defining the destination *directory*. [...]&lt;/span&gt;

&lt;span class="go"&gt; - `as` (optional): string&lt;/span&gt;

&lt;span class="go"&gt;   A path defining the path to install the source as. [...]&lt;/span&gt;

&lt;span class="go"&gt; - `when` (optional): manifest condition (string or mapping of string)&lt;/span&gt;

&lt;span class="go"&gt;   A condition as defined in [Conditional rules](https://salsa.debian.org/debian/debputy/-/blob/main/MANIFEST-FORMAT.md#Conditional rules).&lt;/span&gt;


&lt;span class="go"&gt;This rule enforces the following restrictions:&lt;/span&gt;
&lt;span class="go"&gt; - The rule must use exactly one of: `source`, `sources`&lt;/span&gt;
&lt;span class="go"&gt; - The attribute `as` cannot be used with any of: `dest-dir`, `sources`&lt;/span&gt;

&lt;span class="go"&gt;[...]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;(Output trimmed a bit for space reasons)&lt;/p&gt;
&lt;p&gt;All the attributes and restrictions are auto-computed by &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; from information
provided by the plugin.  The associated documentation for each attribute is supplied
by the plugin itself, The &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; API validates that all attributes are covered
and the documentation does not describe non-existing fields.  This ensures that you as
a plugin provider never forget to document new attributes when you add them later.&lt;/p&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; API for manifest rules are not quite stable yet. So currently only
&lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; provides rules here. However, it is my intention to lift that restriction
in the future.&lt;/p&gt;
&lt;p&gt;I got the idea of supporting online validated examples when I was building this feature.
However, sadly, I have not gotten around to supporting it yet.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="manifest-variables-like-package"&gt;
&lt;h2&gt;Manifest variables like &lt;tt class="docutils literal"&gt;{{PACKAGE}}&lt;/tt&gt;&lt;/h2&gt;
&lt;p&gt;I also added a similar documentation feature for manifest variables such as
&lt;tt class="docutils literal"&gt;{{PACKAGE}}&lt;/tt&gt;. When I implemented this, I realized listing all manifest variables
&lt;em&gt;by default&lt;/em&gt; would probably be counter productive to new users.  As an example, if you
list all variables by default it would include &lt;tt class="docutils literal"&gt;DEB_HOST_MULTIARCH&lt;/tt&gt; (the most common
case) side-by-side with the the much less used &lt;tt class="docutils literal"&gt;DEB_BUILD_MULTIARCH&lt;/tt&gt; and the even
lessor used &lt;tt class="docutils literal"&gt;DEB_TARGET_MULTIARCH&lt;/tt&gt; variable.  Having them side-by-side implies they
are of equal importance, which they are not. As an example, the ballpark number of
unique packages for which &lt;tt class="docutils literal"&gt;DEB_TARGET_MULTIARCH&lt;/tt&gt; is useful can be counted on two
hands (and maybe two feet if you consider &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;gcc-X&lt;/span&gt;&lt;/tt&gt; distinct from &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;gcc-Y&lt;/span&gt;&lt;/tt&gt;).&lt;/p&gt;
&lt;p&gt;This is one of the cases, where experience makes us blind. Many of us probably have the
&amp;quot;show me everything and I will find what I need&amp;quot; mentality. But that requires experience
to be able to pull that off - especially if all alternatives are presented as equals.
The cross-building terminology has proven to notoriously match poorly to people's
expectation.&lt;/p&gt;
&lt;p&gt;Therefore, I took a deliberate choice to reduce the list of shown variables by default
and in the output explicitly list what filters were active. In the current version
of &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; (0.1.9), the listing of manifest-variables look something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;debputy&lt;span class="w"&gt; &lt;/span&gt;plugin&lt;span class="w"&gt; &lt;/span&gt;list&lt;span class="w"&gt; &lt;/span&gt;manifest-variables
&lt;span class="go"&gt;+----------------------------------+----------------------------------------+------+-------------+&lt;/span&gt;
&lt;span class="go"&gt;| Variable (use via: `{{ NAME }}`) | Value                                  | Flag | Provided by |&lt;/span&gt;
&lt;span class="go"&gt;+----------------------------------+----------------------------------------+------+-------------+&lt;/span&gt;
&lt;span class="go"&gt;| DEB_HOST_ARCH                    | amd64                                  |      | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| [... other DEB_HOST_* vars ...]  | [...]                                  |      | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| DEB_HOST_MULTIARCH               | x86_64-linux-gnu                       |      | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| DEB_SOURCE                       | debputy                                |      | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| DEB_VERSION                      | 0.1.8                                  |      | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| DEB_VERSION_EPOCH_UPSTREAM       | 0.1.8                                  |      | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| DEB_VERSION_UPSTREAM             | 0.1.8                                  |      | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| DEB_VERSION_UPSTREAM_REVISION    | 0.1.8                                  |      | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| PACKAGE                          | &amp;lt;package-name&amp;gt;                         |      | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;| path:BASH_COMPLETION_DIR         | /usr/share/bash-completion/completions |      | debputy     |&lt;/span&gt;
&lt;span class="go"&gt;+----------------------------------+----------------------------------------+------+-------------+&lt;/span&gt;

&lt;span class="go"&gt;+-----------------------+--------+-------------------------------------------------------+&lt;/span&gt;
&lt;span class="go"&gt;| Variable type         | Value  | Option                                                |&lt;/span&gt;
&lt;span class="go"&gt;+-----------------------+--------+-------------------------------------------------------+&lt;/span&gt;
&lt;span class="go"&gt;| Token variables       | hidden | --show-token-variables OR --show-all-variables        |&lt;/span&gt;
&lt;span class="go"&gt;| Special use variables | hidden | --show-special-case-variables OR --show-all-variables |&lt;/span&gt;
&lt;span class="go"&gt;+-----------------------+--------+-------------------------------------------------------+&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I will probably tweak the concrete listing in the future. Personally, I am considering to provide
short-hands variables for some of the &lt;tt class="docutils literal"&gt;DEB_HOST_*&lt;/tt&gt; variables and then hide the &lt;tt class="docutils literal"&gt;DEB_HOST_*&lt;/tt&gt;
group from the default view as well.  Maybe something like &lt;tt class="docutils literal"&gt;ARCH&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;MULTIARCH&lt;/tt&gt;, which
would default to their &lt;tt class="docutils literal"&gt;DEB_HOST_*&lt;/tt&gt; counter part. This variable could then have extended
documentation that high lights &lt;tt class="docutils literal"&gt;DEB_HOST_&amp;lt;X&amp;gt;&lt;/tt&gt; as its source and imply that there are special
cases for cross-building where you might need &lt;tt class="docutils literal"&gt;DEB_BUILD_&amp;lt;X&amp;gt;&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;DEB_TARGET_&amp;lt;X&amp;gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Speaking of variable documentation, you can also lookup the documentation for a given
manifest variable:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;debputy&lt;span class="w"&gt; &lt;/span&gt;plugin&lt;span class="w"&gt; &lt;/span&gt;show&lt;span class="w"&gt; &lt;/span&gt;manifest-variables&lt;span class="w"&gt; &lt;/span&gt;path:BASH_COMPLETION_DIR
&lt;span class="go"&gt;Variable: path:BASH_COMPLETION_DIR&lt;/span&gt;
&lt;span class="go"&gt;==================================&lt;/span&gt;

&lt;span class="go"&gt;Documentation: Directory to install bash completions into&lt;/span&gt;
&lt;span class="go"&gt;Resolved: /usr/share/bash-completion/completions&lt;/span&gt;
&lt;span class="go"&gt;Plugin: debputy&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This was my update on online reference documentation for &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;. I hope you found it
useful. :)&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="thanks"&gt;
&lt;h2&gt;Thanks&lt;/h2&gt;
&lt;p&gt;On a closing note, I would like to thanks Jochen Sprickerhof, Andres Salomon, Paul Gevers
for their recent contributions to &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt;. Jochen and Paul provided a number of real
world cases where &lt;tt class="docutils literal"&gt;debputy&lt;/tt&gt; would crash or not work, which have now been fixed. Andres
and Paul also provided corrections to the documentation.&lt;/p&gt;
&lt;/div&gt;
</content><category term="Debian, debputy"></category><category term="debian"></category><category term="debputy"></category><category term="pdo"></category></entry></feed>