<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Carlo Lobrano</title>
    <description>The latest articles on DEV Community by Carlo Lobrano (@clobrano).</description>
    <link>https://dev.to/clobrano</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F243181%2Fa013272c-592d-4cbb-819d-0978f73afd22.jpeg</url>
      <title>DEV Community: Carlo Lobrano</title>
      <link>https://dev.to/clobrano</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/clobrano"/>
    <language>en</language>
    <item>
      <title>Test Kernel changes on QEMU</title>
      <dc:creator>Carlo Lobrano</dc:creator>
      <pubDate>Sat, 23 Apr 2022 13:26:52 +0000</pubDate>
      <link>https://dev.to/clobrano/test-kernel-changes-on-qemu-1j5e</link>
      <guid>https://dev.to/clobrano/test-kernel-changes-on-qemu-1j5e</guid>
      <description>&lt;p&gt;This is all about testing Linux kernel changes on a QEMU virtual machine in cycles of &lt;em&gt;build, install and run&lt;/em&gt; shortest than 5 minutes.&lt;/p&gt;

&lt;p&gt;The Internet is full of tutorials about building the Linux Kernel, yet I found less content about &lt;em&gt;applying the changes&lt;/em&gt; to a virtualized environment, so that will be the goal of this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running QEMU virtual image
&lt;/h2&gt;

&lt;p&gt;Also this content is quite common, so I will be short&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create the disk: a qcow2 format image of 2G&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;qemu-img create &lt;span class="nt"&gt;-f&lt;/span&gt; qcow2 alpine.img 2G

&lt;span class="c"&gt;# Download a live CD ISO image (e.g. Ubuntu, Alpine, whatever), then run the live image and install&lt;/span&gt;
&lt;span class="c"&gt;# it into the disk created above.&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;qemu-system-x86_64 &lt;span class="nt"&gt;-cdrom&lt;/span&gt; alpine-downloaded.iso alpine.img &lt;span class="nt"&gt;-m&lt;/span&gt; 512M &lt;span class="nt"&gt;-enable-kvm&lt;/span&gt;

&lt;span class="c"&gt;# Run the newly installed image from the disk&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;qemu-system-x86_64 &lt;span class="nt"&gt;-enable-kvm&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; 512M &lt;span class="nt"&gt;-smp&lt;/span&gt; 4 &lt;span class="nt"&gt;-cpu&lt;/span&gt; host &lt;span class="nt"&gt;-drive&lt;/span&gt; &lt;span class="nv"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;alpine.img
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running QEMU with a custom kernel
&lt;/h2&gt;

&lt;p&gt;You downloaded and built the Linux Kernel under &lt;code&gt;~/workspace/linux&lt;/code&gt;, that is you know have a &lt;code&gt;/linux/arch/x86_64/boot/bzImage&lt;/code&gt;, the command to run it in QEMU is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;qemu-system-x86_64 &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-enable-kvm&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; 512M &lt;span class="nt"&gt;-smp&lt;/span&gt; 4 &lt;span class="nt"&gt;-cpu&lt;/span&gt; host &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-kernel&lt;/span&gt; ~/workspace/linux/arch/x86_64/boot/bzImage &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-append&lt;/span&gt; &lt;span class="s2"&gt;"root=/dev/sda3 console=ttyS0 rw"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-drive&lt;/span&gt; &lt;span class="nv"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;alpine.img
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Make a change and test it
&lt;/h2&gt;

&lt;p&gt;Consider now you made a change to the kernel. Any change, maybe even so silly like the one below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index e7755d9cf..534e8051e 100644
&lt;/span&gt;&lt;span class="gd"&gt;--- a/drivers/usb/serial/option.c
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/drivers/usb/serial/option.c
&lt;/span&gt;&lt;span class="p"&gt;@@ -2138,7 +2138,7 @@&lt;/span&gt; static struct usb_serial_driver option_1port_device = {
        .owner =    THIS_MODULE,
        .name =     "option1",
    },
&lt;span class="gd"&gt;-   .description       = "GSM modem (1-port)",
&lt;/span&gt;&lt;span class="gi"&gt;+   .description       = "it's me, Mario!",
&lt;/span&gt;    .id_table          = option_ids,
    .num_ports         = 1,
    .probe             = option_probe,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How do you test it on your QEMU VM?&lt;/p&gt;

&lt;p&gt;First, mount the VM image on the host&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; /tmp/alpine
&lt;span class="nv"&gt;$ &lt;/span&gt;guestmount &lt;span class="nt"&gt;--add&lt;/span&gt; alpine.img &lt;span class="nt"&gt;--root&lt;/span&gt; /dev/sda3 /tmp/alpine
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; /tmp/alpine
bin   dev  home  lost+found  mnt  proc  run   srv   sys  usr
boot  etc  lib   media       opt  root  sbin  swap  tmp  var
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second and last, during the build, tell the kernel to install the modules inside the mounted image&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;make &lt;span class="nt"&gt;-j&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;nproc&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
...
&lt;span class="nv"&gt;$ &lt;/span&gt;make &lt;span class="nt"&gt;-j&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;nproc&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; modules
&lt;span class="nv"&gt;$ INSTALL_MOD_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/tmp/alpine make modules_install
INSTALL /tmp/alpine/lib/modules/5.18.0-rc2-custom-ga1901b464e7e-dirty/kernel/drivers/thermal/intel/x86_pkg_temp_thermal.ko
INSTALL /tmp/alpine/lib/modules/5.18.0-rc2-custom-ga1901b464e7e-dirty/kernel/drivers/usb/serial/option.ko
INSTALL /tmp/alpine/lib/modules/5.18.0-rc2-custom-ga1901b464e7e-dirty/kernel/drivers/usb/serial/usb-serial-simple.ko
INSTALL /tmp/alpine/lib/modules/5.18.0-rc2-custom-ga1901b464e7e-dirty/kernel/drivers/usb/serial/usb_wwan.ko
INSTALL /tmp/alpine/lib/modules/5.18.0-rc2-custom-ga1901b464e7e-dirty/kernel/drivers/usb/serial/usbserial.ko
INSTALL /tmp/alpine/lib/modules/5.18.0-rc2-custom-ga1901b464e7e-dirty/kernel/fs/efivarfs/efivarfs.ko
INSTALL /tmp/alpine/lib/modules/5.18.0-rc2-custom-ga1901b464e7e-dirty/kernel/net/ipv4/netfilter/iptable_nat.ko
INSTALL /tmp/alpine/lib/modules/5.18.0-rc2-custom-ga1901b464e7e-dirty/kernel/net/netfilter/nf_log_syslog.ko
INSTALL /tmp/alpine/lib/modules/5.18.0-rc2-custom-ga1901b464e7e-dirty/kernel/net/netfilter/xt_LOG.ko
INSTALL /tmp/alpine/lib/modules/5.18.0-rc2-custom-ga1901b464e7e-dirty/kernel/net/netfilter/xt_MASQUERADE.ko
INSTALL /tmp/alpine/lib/modules/5.18.0-rc2-custom-ga1901b464e7e-dirty/kernel/net/netfilter/xt_addrtype.ko
INSTALL /tmp/alpine/lib/modules/5.18.0-rc2-custom-ga1901b464e7e-dirty/kernel/net/netfilter/xt_mark.ko
INSTALL /tmp/alpine/lib/modules/5.18.0-rc2-custom-ga1901b464e7e-dirty/kernel/net/netfilter/xt_nat.ko
DEPMOD  /tmp/alpine/lib/modules/5.18.0-rc2-custom-ga1901b464e7e-dirty
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;don't forget to UNMOUNT the image&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;umount /tmp/alpine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run again your kernel, the change (at least the one above) will be visible loading "option" module.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;This process is so fast that one can test changes to the kernel in few seconds, moreover you are going to have a wide range of debug features provided by QEMU and, not less important, your machine is preserved.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>kernel</category>
      <category>qemu</category>
      <category>testing</category>
    </item>
    <item>
      <title>Let mkdir create parents directories</title>
      <dc:creator>Carlo Lobrano</dc:creator>
      <pubDate>Mon, 21 Mar 2022 15:17:04 +0000</pubDate>
      <link>https://dev.to/clobrano/let-mkdir-create-parents-directories-1gbd</link>
      <guid>https://dev.to/clobrano/let-mkdir-create-parents-directories-1gbd</guid>
      <description>&lt;p&gt;I noticed that it is more than a year since the last post, so I thought that a small new content would have been easier to write.&lt;br&gt;
This in particular refers to an extension I wrote some time ago for the &lt;code&gt;mkdir&lt;/code&gt; system call to make it a bit more proactive 😄.&lt;/p&gt;

&lt;p&gt;90% of the times, I use &lt;code&gt;mkdir&lt;/code&gt; to create a new folder in the current directory, so the few times I want to create a new folder in a deeper path I always forget to add the &lt;code&gt;--parent&lt;/code&gt; flag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;me/blog &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;foo/bin/bar
&lt;span class="nb"&gt;mkdir&lt;/span&gt;: cannot create directory ‘foo/bin/bar’: No such file or directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;as the &lt;a href="https://man7.org/linux/man-pages/man1/mkdir.1.html"&gt;Man-pages&lt;/a&gt; would tell you, since the parent directory &lt;code&gt;foo/bin&lt;/code&gt; does not exist, &lt;code&gt;mkdir&lt;/code&gt; fails to create &lt;code&gt;bar&lt;/code&gt;, but what if &lt;code&gt;mkdir&lt;/code&gt; were so kind to tell me just that, and suggests to add the flag on my behalf? 😄&lt;/p&gt;

&lt;p&gt;This is actually somehow easy.&lt;/p&gt;

&lt;p&gt;First of all, you need a way to define new functions in your shell. I use &lt;strong&gt;zsh&lt;/strong&gt;, but in &lt;strong&gt;bash&lt;/strong&gt; would be the same: write a function definition in a file and source it in &lt;em&gt;.bashrc&lt;/em&gt; or &lt;em&gt;.zshrc&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This comes from my &lt;em&gt;.zshrc&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# Functions and aliases&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; ~/.dot/.config/cconf/zsh/functions.zsh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What to do for &lt;code&gt;mkdir&lt;/code&gt;?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;override it with a custom function ➡️ &lt;code&gt;function mkdir() {}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;find the parent directory &lt;code&gt;mkdir&lt;/code&gt; expects to exist already ➡️ &lt;code&gt;echo $1 | grep -E -q '[\S+/]+'&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;if it doesn't exist, ask if it must be created or not ➡️ &lt;code&gt;echo "Press ENTER to run mkdir with --parents."&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ok, point 2 probably requires some explaination, so let's back to the example above&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;me/blog &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;foo/bin/bar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;bar&lt;/code&gt; is the folder to be created, while &lt;code&gt;foo/bin/&lt;/code&gt; is the parent.&lt;br&gt;
The &lt;code&gt;grep&lt;/code&gt; expression above looks for any sequence of substrings ending with slash &lt;code&gt;/&lt;/code&gt;: &lt;code&gt;foo/&lt;/code&gt;, &lt;code&gt;foo/bar/&lt;/code&gt;, etc., but not &lt;code&gt;bar&lt;/code&gt;, or &lt;code&gt;foo&lt;/code&gt;, which would be a directory in the &lt;em&gt;current&lt;/em&gt; folder.&lt;/p&gt;

&lt;p&gt;So putting all these pieces together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;function &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;# extend mkdir with custom features&lt;/span&gt;
    &lt;span class="c"&gt;# propose adding "--parents" flag.&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s1"&gt;'[\S+/]+'&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; 0 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="c"&gt;# one of the directories in $1 path do not exist&lt;/span&gt;
        &lt;span class="c"&gt;# suggest adding "--parents" flag.&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"# Some parents in &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt; do not exist. Press ENTER to run mkdir with --parents."&lt;/span&gt;
        &lt;span class="nb"&gt;read
        command mkdir&lt;/span&gt; &lt;span class="nt"&gt;--parents&lt;/span&gt; &lt;span class="nv"&gt;$@&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"# done"&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;command mkdir&lt;/span&gt; &lt;span class="nv"&gt;$@&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's try again&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;me/blog &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;foo/bin/bar                                                                                    
&lt;span class="c"&gt;# Some parents in foo/bin/bar do not exist. Press ENTER to run mkdir with --parents.&lt;/span&gt;

&lt;span class="c"&gt;# done&lt;/span&gt;

me/blog &lt;span class="nv"&gt;$ &lt;/span&gt;tree | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; foo &lt;span class="nt"&gt;-e&lt;/span&gt; bar &lt;span class="nt"&gt;-e&lt;/span&gt; bin
├── foo
│   └── bin
│       └── bar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course in a similar manner you can override &lt;code&gt;rm&lt;/code&gt; as well (see &lt;a href="https://github.com/clobrano/dot/blob/master/.config/cconf/zsh/functions.zsh#L21"&gt;here&lt;/a&gt;).&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to use GitHub Actions to connect with Launchpad</title>
      <dc:creator>Carlo Lobrano</dc:creator>
      <pubDate>Mon, 02 Nov 2020 08:42:12 +0000</pubDate>
      <link>https://dev.to/clobrano/how-to-use-github-actions-to-connect-with-launchpad-3320</link>
      <guid>https://dev.to/clobrano/how-to-use-github-actions-to-connect-with-launchpad-3320</guid>
      <description>&lt;p&gt;Since the beginning of the Yaru project, three years ago, we always had a little problem: two different places for bug tracing.&lt;br&gt;
Our main repository is hosted on &lt;a href="https://github.com/ubuntu/yaru"&gt;GitHub&lt;/a&gt;, but we also have the &lt;a href="https://launchpad.net/ubuntu/+source/yaru-theme"&gt;Launchpad&lt;/a&gt; page taking care of the &lt;code&gt;yaru-theme&lt;/code&gt; package.&lt;/p&gt;
&lt;h1&gt;
  
  
  TLDR
&lt;/h1&gt;

&lt;p&gt;Whith &lt;a href="https://github.com/ubuntu/yaru/blob/master/.github/lpbugtracker.py"&gt;this python script&lt;/a&gt;, and GitHub actions, I mirrored the bugs from one bug tracking system to the other.&lt;/p&gt;
&lt;h1&gt;
  
  
  The full story
&lt;/h1&gt;

&lt;p&gt;I can not tress enough how much I appreciate our users that take time to report problems, desires, ideas, but indeed our responsiveness on Launchpad has not been the best so far, and we shall thank the Ubuntu Desktop Team that took care of these reports.&lt;/p&gt;

&lt;p&gt;A couple of weeks ago, we thought that automatically mirroring Launchpad bugs on our GitHub repository could have been a solution, and we decided to take care of this with our CI.&lt;/p&gt;

&lt;p&gt;Ideally, our solution would have the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Perform daily checks.&lt;/li&gt;
&lt;li&gt;Get the list of all open bugs on Launchpad.&lt;/li&gt;
&lt;li&gt;For any &lt;strong&gt;new bug&lt;/strong&gt; found, create a GitHub bug with ID, Title and link to the original report.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We already have some GitHub Actions to keep track of our upstreams. It's configured to run periodically, and create a PR when there is new content we shall take care of. This configuration looked promising for the first point.&lt;/p&gt;

&lt;p&gt;launchpad provides &lt;a href="https://help.launchpad.net/API/launchpadlib"&gt;Launchpadlib&lt;/a&gt;, a nice python library to interact with the servers.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;launchpadlib is an open-source Python library that lets you treat the HTTP resources published by Launchpad's web service as Python objects responding to a standard set of commands. With launchpadlib you can integrate your applications into Launchpad without knowing a lot about HTTP client programming.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Playing with this library was pretty fun, and the best way to learn it was through the &lt;a href="https://help.launchpad.net/API/Uses"&gt;listed examples&lt;/a&gt;. For instance, I must thank &lt;a href="https://launchpad.net/bughugger"&gt;Bughugger&lt;/a&gt;, that showed me the way to get the list of bugs of a given application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;launchpadlib.launchpad&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Launchpad&lt;/span&gt;

&lt;span class="n"&gt;HOME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expanduser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"~"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;CACHEDIR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HOME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;".launchpadlib"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"cache"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;lp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Launchpad&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;login_anonymously&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"Yaru LP bug checker"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"production"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CACHEDIR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"devel"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;ubuntu&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;distributions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"ubuntu"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;archive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ubuntu&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main_archive&lt;/span&gt;
&lt;span class="n"&gt;packages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getPublishedSources&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"yaru"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;package&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ubuntu&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getSourcePackage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;packages&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;source_package_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;bug_tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;searchTasks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;bug_tasks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I then extract three field from the task:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ID: &lt;code&gt;task.id&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Title: &lt;code&gt;task.title&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Link: &lt;code&gt;"https://bugs.launchpad.net/ubuntu/+source/yaru-theme/+bug/" + str(task.id)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The third point is actually made of two different steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Identify &lt;strong&gt;new&lt;/strong&gt; issues.&lt;/li&gt;
&lt;li&gt;Create an issue.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both points have been resolved using &lt;a href="https://github.com/github/hub"&gt;HUB&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;hub is a command line tool that wraps git in order to extend it with extra features and commands that make working with GitHub easier.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;GitHub provides its own &lt;a href="https://cli.github.com/"&gt;CLI tool&lt;/a&gt;, which I use on a daily bases, but the point that convinced me to use HUB is the following:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;hub can also be used to make shell scripts that directly interact with the GitHub API.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Create issues
&lt;/h2&gt;

&lt;p&gt;Let's start from the last step. Creating an issue with HUB is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hub issue create &lt;span class="nt"&gt;-m&lt;/span&gt; &amp;lt;title&amp;gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &amp;lt;message&amp;gt; &lt;span class="nt"&gt;-l&lt;/span&gt; Launchpad
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here I used the &lt;code&gt;-m&lt;/code&gt; flag twice to set the title:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;LP#[ID] [TITLE]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and the body of the issue:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Reported first on Launchpad at &lt;a href="https://bugs.launchpad.net/ubuntu/+source/yaru-theme/+bug/%5BID%5D"&gt;https://bugs.launchpad.net/ubuntu/+source/yaru-theme/+bug/[ID]&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;then I added a &lt;strong&gt;Launchpad&lt;/strong&gt; label (&lt;code&gt;-l&lt;/code&gt;), which makes bug management easier, and ended up very useful for the next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create only NEW bugs
&lt;/h2&gt;

&lt;p&gt;HUB can list all the bugs from the repository, but - at the time of writing - Yaru has more than 1.000 bugs (only 44 open 😀), then it takes a while to get them all at once. Luckily, HUB can filter by label!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hub issue &lt;span class="nt"&gt;--state&lt;/span&gt; all &lt;span class="nt"&gt;--label&lt;/span&gt; Launchpad
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Parsing the output is easy with Python, all the rest is just a little glue logic to put all together.&lt;/p&gt;

&lt;h1&gt;
  
  
  Results
&lt;/h1&gt;

&lt;p&gt;I am satisfied with the end result. We can be more responsive to our user base requests now, and I had fun writing the &lt;a href="https://github.com/ubuntu/yaru/blob/master/.github/lpbugtracker.py"&gt;python script&lt;/a&gt;, and learned something new of GitHub Action.&lt;/p&gt;

</description>
      <category>python</category>
      <category>ci</category>
      <category>yaru</category>
    </item>
  </channel>
</rss>
