<?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: Jordan Duabe</title>
    <description>The latest articles on DEV Community by Jordan Duabe (@j4ckofalltrades).</description>
    <link>https://dev.to/j4ckofalltrades</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%2F507117%2F3b8be0a0-4d18-4103-93b8-07fbbd4cac28.jpg</url>
      <title>DEV Community: Jordan Duabe</title>
      <link>https://dev.to/j4ckofalltrades</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/j4ckofalltrades"/>
    <language>en</language>
    <item>
      <title>Programming a digital badge (with a Badger2040 and MicroPython)</title>
      <dc:creator>Jordan Duabe</dc:creator>
      <pubDate>Fri, 16 Jun 2023 13:00:00 +0000</pubDate>
      <link>https://dev.to/j4ckofalltrades/programming-a-digital-badge-with-a-badger2040-and-micropython-3mjc</link>
      <guid>https://dev.to/j4ckofalltrades/programming-a-digital-badge-with-a-badger2040-and-micropython-3mjc</guid>
      <description>&lt;p&gt;&lt;a href="https://shop.pimoroni.com/products/badger-2040" rel="noopener noreferrer"&gt;Badger2040&lt;/a&gt; is a programmable E Paper/eInk/EPD badge, powered by the &lt;a href="https://www.raspberrypi.com/products/rp2040" rel="noopener noreferrer"&gt;RP2040&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Some of its features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;2.9" black and white E Ink® display (296 x 128 pixels)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Powered by RP2040 (Dual Arm Cortex M0+ running at up to 133Mhz with 264kB of SRAM)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;2MB of QSPI flash supporting XiP&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Five front user buttons&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Reset and boot buttons (the boot button can also be used as a user button)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;USB-C connector for power and programming&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;JST-PH connector for attaching a battery (input range 2.7V - 6V)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I got one to play around with and to use as a digital lanyard / badge when attending meetups.&lt;/p&gt;

&lt;h2&gt;
  
  
  Programming
&lt;/h2&gt;

&lt;p&gt;The Badger2040 ships with a custom &lt;a href="https://micropython.org/" rel="noopener noreferrer"&gt;MicroPython&lt;/a&gt; build and a graphics library &lt;a href="https://github.com/pimoroni/pimoroni-pico/blob/main/micropython/modules/picographics/README.md" rel="noopener noreferrer"&gt;PicoGraphics&lt;/a&gt; that contains useful utilities for drawing on the screen. There are also a couple of examples loaded on the device by default.&lt;/p&gt;

&lt;p&gt;In order to write code or update / run the existing code samples, you’ll need to connect it to your computer and open up an IDE with MicroPython support i.e. &lt;a href="https://thonny.org/" rel="noopener noreferrer"&gt;Thonny&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One thing to note is you opt to use a different IDE is to make sure to name the file as &lt;code&gt;main.py&lt;/code&gt; as it will be automatically run once uploaded to the device (this is the default behavior of the RP2040), otherwise your code will not be executed.&lt;/p&gt;

&lt;p&gt;I ended up using &lt;a href="https://www.jetbrains.com/pycharm/" rel="noopener noreferrer"&gt;PyCharm&lt;/a&gt; with the &lt;a href="https://plugins.jetbrains.com/plugin/9777-micropython" rel="noopener noreferrer"&gt;MicroPython&lt;/a&gt; plugin, one extra step I needed to do was to point the plugin to the device which was located at &lt;code&gt;/dev/ttyACM0&lt;/code&gt;(if you’re on a Unix environment).&lt;/p&gt;

&lt;h2&gt;
  
  
  Drawing
&lt;/h2&gt;

&lt;p&gt;With the following built-in functions, we can draw text and shapes on the screen.&lt;/p&gt;

&lt;h3&gt;
  
  
  Constants
&lt;/h3&gt;

&lt;p&gt;The screen dimensions are provided as constants.&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="n"&gt;badger2040&lt;/span&gt;
&lt;span class="n"&gt;WIDTH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;badger2040&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WIDTH&lt;/span&gt; &lt;span class="c1"&gt;# 296
&lt;/span&gt;&lt;span class="n"&gt;HEIGHT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;badger2040&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HEIGHT&lt;/span&gt; &lt;span class="c1"&gt;# 128
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pen
&lt;/h3&gt;

&lt;p&gt;There are 16 pen colours - or “shades of grey” - to choose, from 0 (black) to 15 (white).&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="n"&gt;badger2040&lt;/span&gt;
&lt;span class="n"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;badger2040&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Badger2040&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_pen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;colour&lt;/span&gt; &lt;span class="c1"&gt;# int: color from 0 to 15
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The thickness of the lines drawn on the screen can be also be configured.&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="n"&gt;badger2040&lt;/span&gt;
&lt;span class="n"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;badger2040&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Badger2040&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_thickness&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="c1"&gt;# int: thickness in pixels
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Text
&lt;/h3&gt;

&lt;p&gt;The font can be changed with the &lt;code&gt;set_font&lt;/code&gt; method and write using the &lt;code&gt;text&lt;/code&gt; function.&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="n"&gt;badger2040&lt;/span&gt;
&lt;span class="n"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;badger2040&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Badger2040&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# Available fonts
#
# Bitmap
# - bitmap6
# - bitmap8
# - bitmap14_outline
#
# Vector
# - sans
# - gothic
# - cursive
# - serif_italic
# - serif
&lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_font&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# the text string to draw 
&lt;/span&gt;  &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# the destination X coordinate
&lt;/span&gt;  &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# the destination Y coordinate
&lt;/span&gt;  &lt;span class="n"&gt;wordwrap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# number of pixels width before trying to break text into multiple lines
&lt;/span&gt;  &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# size
&lt;/span&gt;  &lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# rotation angle (Vector only!)
&lt;/span&gt;  &lt;span class="n"&gt;spacing&lt;/span&gt; &lt;span class="c1"&gt;# letter spacing
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Shapes
&lt;/h3&gt;

&lt;p&gt;There are quite a few functions available here, I just included the ones I used. You can check out the full list in the &lt;a href="https://github.com/pimoroni/pimoroni-pico/tree/main/micropython/modules/picographics" rel="noopener noreferrer"&gt;Pico Graphics function reference&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line&lt;/strong&gt;&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="n"&gt;badger2040&lt;/span&gt;
&lt;span class="n"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;badger2040&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Badger2040&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rectangle&lt;/strong&gt;&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="n"&gt;badger2040&lt;/span&gt;
&lt;span class="n"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;badger2040&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Badger2040&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rectangle&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;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Putting it all together
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Using the Badger2040 as a digital badge / lanyard
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;badger2040&lt;/span&gt;

&lt;span class="n"&gt;WIDTH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;badger2040&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WIDTH&lt;/span&gt;    &lt;span class="c1"&gt;# 296
&lt;/span&gt;&lt;span class="n"&gt;HEIGHT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;badger2040&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HEIGHT&lt;/span&gt;  &lt;span class="c1"&gt;# 128
&lt;/span&gt;
&lt;span class="n"&gt;BANNER_HEIGHT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;
&lt;span class="n"&gt;BANNER_TEXT_SIZE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt;
&lt;span class="n"&gt;NAME_PADDING_LEFT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;

&lt;span class="c1"&gt;# badge contents
&lt;/span&gt;&lt;span class="n"&gt;BANNER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Random Event&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;First Name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;HANDLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;@handle&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;badger2040&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Badger2040&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# draw banner
&lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_pen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_font&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;serif&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BANNER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BANNER_HEIGHT&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WIDTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BANNER_TEXT_SIZE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rectangle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BANNER_HEIGHT&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WIDTH&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HEIGHT&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;BANNER_HEIGHT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# draw name
&lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_pen&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;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_thickness&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_font&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sans&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NAME_PADDING_LEFT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HEIGHT&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WIDTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HANDLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NAME_PADDING_LEFT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HEIGHT&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WIDTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# draw border enclosing banner and name
&lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_thickness&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;line&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WIDTH&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;WIDTH&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HEIGHT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;line&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;HEIGHT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WIDTH&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HEIGHT&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code is also available on GitHub as a &lt;a href="https://gist.github.com/j4ckofalltrades/a4ec95b3e077fd7c294d20ef8095f480" rel="noopener noreferrer"&gt;gist&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>micropython</category>
      <category>eink</category>
      <category>badger2040</category>
    </item>
    <item>
      <title>Programmatically invoking JetBrains IDE actions</title>
      <dc:creator>Jordan Duabe</dc:creator>
      <pubDate>Sat, 20 May 2023 05:22:32 +0000</pubDate>
      <link>https://dev.to/j4ckofalltrades/programmatically-invoking-jetbrains-ide-actions-2c08</link>
      <guid>https://dev.to/j4ckofalltrades/programmatically-invoking-jetbrains-ide-actions-2c08</guid>
      <description>&lt;p&gt;&lt;em&gt;Update (2024-03-08):&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;JetBrains has since released an &lt;a href="https://github.com/JetBrains/intellij-streamdeck-plugin" rel="noopener noreferrer"&gt;official plugin&lt;/a&gt; on August 2023 but if you're running Linux (Stream Deck SDK only supports Windows and Mac currently) you may still find this article guide useful.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ve had a Stream Deck for a while now but haven’t really configured it for any coding related workflows. I use several JetBrains IDEs for work and personal use (IntelliJ, PyCharm, WebStorm to name a few), so I started looking into what the possible options were.&lt;/p&gt;

&lt;p&gt;The simplest solution would be to just invoke keyboard shortcut for a specific but the downside of this approach is you can only have so much keycodes assigned (which may conflict with system shortcuts and possibly other apps), not to mention you’d have to change these when you switched keymaps or used another operating system.&lt;/p&gt;

&lt;p&gt;Ideally there would be a command where you can provide an &lt;em&gt;action&lt;/em&gt; to the IDE, possibly through a plugin.&lt;/p&gt;

&lt;h2&gt;
  
  
  IDE Scripting Console
&lt;/h2&gt;

&lt;p&gt;While reading through the IntelliJ documentation, I stumbled upon the &lt;a href="https://www.jetbrains.com/help/idea/ide-scripting-console.html" rel="noopener noreferrer"&gt;IDE scripting console&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The IDE Scripting Console can be used to write simple scripts that automate IntelliJ IDEA features and extract various information. With access to the IntelliJ platform API, you can think of it as a lightweight alternative to a plugin, which adds or modifies some behavior of the IDE.&lt;/p&gt;

&lt;p&gt;By default, it supports scripts written in Kotlin, JavaScript, and Groovy. However, you can use any scripting language that is compliant with JSR 223, for example, Python, Ruby, Clojure, and so on.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This looks promising; I copied and modified some sample code from the docs to display a “Hello World” message in a dialog in the IDE.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import com.intellij.openapi.ui.Messages
val b = bindings as Map&amp;lt;String, Any&amp;gt;
val IDE = b["IDE"] as com.intellij.ide.script.IDE
Messages.showInfoMessage("Hello World")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Invoking Actions
&lt;/h2&gt;

&lt;p&gt;The next step was figuring out how to invoke &lt;em&gt;actions&lt;/em&gt; in the IDE. The IntelliJ Platform Plugin SDK defines the requirements in its&lt;a href="https://plugins.jetbrains.com/docs/intellij/basic-action-system.html" rel="noopener noreferrer"&gt;Action system&lt;/a&gt; documentation.&lt;/p&gt;

&lt;p&gt;An &lt;code&gt;ActionManager&lt;/code&gt; instance is used to execute an IDE &lt;code&gt;Action&lt;/code&gt; that is referred to by its unique id – this can either be a custom action from an installed plugin or the &lt;a href="https://github.com/JetBrains/intellij-community/tree/idea/231.8109.175/platform/ide-core/src/com/intellij/openapi/actionSystem/IdeActions.java" rel="noopener noreferrer"&gt;standard IntelliJ Platform actions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The following snippet executes the standard &lt;em&gt;NextTab&lt;/em&gt; action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.AnAction
val b = bindings as Map&amp;lt;String, Any&amp;gt;
val IDE = b["IDE"] as com.intellij.ide.script.IDE
val actionManager: ActionManager = ActionManager.getInstance()
// move focus to the next editor tab
val action: AnAction = actionManager.getAction("NextTab")
actionManager.tryToExecute(action, null, null, null, false)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Invoking the script from the command line
&lt;/h2&gt;

&lt;p&gt;Up to now, I’ve been able to play around with the scripts from within the IDEs – the next step is to find a way to invoke them externally.&lt;/p&gt;

&lt;p&gt;Fortunately, this feature is already available since &lt;a href="https://youtrack.jetbrains.com/issue/IDEA-245847" rel="noopener noreferrer"&gt;version 2021.1&lt;/a&gt;; it requires the command line scripts for the IDEs to be installed e.g.&lt;code&gt;idea&lt;/code&gt; for IntelliJ IDEA. This can be configured via the &lt;a href="https://www.jetbrains.com/help/idea/working-with-the-ide-features-from-command-line.html#toolbox" rel="noopener noreferrer"&gt;JetBrains Toolbox&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The command to run script(s) is &lt;code&gt;idea ideScript &amp;lt;files&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Caveat: The script does not work (when invoked from the command line at least) unless the IDE action execution code is wrapped in the following snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IDE.application.invokeLater {
// action execution code goes here
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My guess is that it needs to be non-blocking, since it is invoked externally. One other thing to note is that if you have multiple windows of an IDE running, the action will be executed in the last active window.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parameterizing the script
&lt;/h2&gt;

&lt;p&gt;The last thing I wanted to add was to parameterize the script, where I could pass in the action name e.g. “Run tests” and the IDE where the aforementioned action would be executed.&lt;/p&gt;

&lt;p&gt;I tried a couple of things; my first implementation involved passing the params as environment variables and parsing them in the script. That didn’t seem to work as when I tried to log the params they would always be &lt;code&gt;null&lt;/code&gt;. The next thing I tried was to reading the params from a text file which also did not work.&lt;/p&gt;

&lt;p&gt;Eventually, my solution involved creating a shell script that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Requires (and parses) the IDE name and action name as arguments&lt;/li&gt;
&lt;li&gt;Writes out the IDE script to a file (taking params into account)&lt;/li&gt;
&lt;li&gt;Executes the IDE script&lt;/li&gt;
&lt;li&gt;Deletes the IDE script file&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So if I wanted to perform an action on an Intellij IDEA window, the command will look like &lt;code&gt;ide-script.sh --ide idea --action action_name_to_perform&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it all together
&lt;/h2&gt;

&lt;p&gt;As a side note, Intellij IDEA ships with Kotlin so the samples will work there – it may work on other IDEs if the Kotlin plugin is installed but I haven’t tested that so YMMV.&lt;/p&gt;

&lt;p&gt;I ended up using Groovy since that ships with the other IDEs I mentioned earlier and I’ve been able to verify that it works on WebStorm and PyCharm. As for operating systems, I’ve tested this script on both macOS and Linux. The steps should be similar on Windows; you also might be able to reuse the shell script if you are running WSL but I imagine you’ll need to update the path of the command-line IDE launchers.&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;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-Eeuo&lt;/span&gt; pipefail
&lt;span class="nb"&gt;trap &lt;/span&gt;cleanup SIGINT SIGTERM ERR EXIT

&lt;span class="nv"&gt;script_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BASH_SOURCE&lt;/span&gt;&lt;span class="p"&gt;[0]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &amp;amp;&amp;gt;/dev/null &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;pwd&lt;/span&gt; &lt;span class="nt"&gt;-P&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;ide_script_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ide_script.groovy"&lt;/span&gt;

usage&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
Usage: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BASH_SOURCE&lt;/span&gt;&lt;span class="p"&gt;[0]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt; [-h] [-f] -i ide -a action
Shell script wrapper that executes actions on JetBrains IDEs via the scripting console.
Available options:
-h, --help    Print this help and exit
-i, --ide     JetBrains IDE command-line launcher e.g. idea, webstorm, pycharm
-a, --action  IDE action to execute
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;  &lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

cleanup&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;trap&lt;/span&gt; - SIGINT SIGTERM ERR EXIT
  &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$script_dir&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$ide_script_name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

setup_colors&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; 2 &lt;span class="o"&gt;]]&lt;/span&gt; &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;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;NO_COLOR&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TERM&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"dumb"&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="nv"&gt;NOFORMAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'\033[0m'&lt;/span&gt; &lt;span class="nv"&gt;RED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'\033[0;31m'&lt;/span&gt; &lt;span class="nv"&gt;GREEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'\033[0;32m'&lt;/span&gt; &lt;span class="nv"&gt;ORANGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'\033[0;33m'&lt;/span&gt; &lt;span class="nv"&gt;BLUE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'\033[0;34m'&lt;/span&gt; &lt;span class="nv"&gt;PURPLE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'\033[0;35m'&lt;/span&gt; &lt;span class="nv"&gt;CYAN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'\033[0;36m'&lt;/span&gt; &lt;span class="nv"&gt;YELLOW&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'\033[1;33m'&lt;/span&gt;
  &lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nv"&gt;NOFORMAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="nv"&gt;RED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="nv"&gt;GREEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="nv"&gt;ORANGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="nv"&gt;BLUE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="nv"&gt;PURPLE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="nv"&gt;CYAN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="nv"&gt;YELLOW&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

msg&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2 &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

die&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="p"&gt;-1&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="c"&gt;# default exit status 1&lt;/span&gt;
  msg &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$msg&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$code&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

parse_params&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;ide&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;
  &lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;

  &lt;span class="k"&gt;while&lt;/span&gt; :&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="nt"&gt;-h&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nt"&gt;--help&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; usage &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nt"&gt;--verbose&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="nt"&gt;--no-color&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;NO_COLOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nt"&gt;--flag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;flag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nt"&gt;--ide&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nv"&gt;ide&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;shift&lt;/span&gt;
        &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nt"&gt;--action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;shift&lt;/span&gt;
        &lt;span class="p"&gt;;;&lt;/span&gt;
    -?&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; die &lt;span class="s2"&gt;"Unknown option: &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;break&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;esac&lt;/span&gt;
    &lt;span class="nb"&gt;shift
  &lt;/span&gt;&lt;span class="k"&gt;done

  &lt;/span&gt;&lt;span class="nv"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

  &lt;span class="c"&gt;# check required params and arguments&lt;/span&gt;
  &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ide&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; die &lt;span class="s2"&gt;"Missing required parameter: ide"&lt;/span&gt;
  &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; die &lt;span class="s2"&gt;"Missing required parameter: action"&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;0
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Invoke supported IDE actions using the IDE scripting console. See links for more details.&lt;/span&gt;
&lt;span class="c"&gt;# &lt;/span&gt;
&lt;span class="c"&gt;# Supported IDE actions: https://github.com/JetBrains/intellij-community/blob/idea/231.8109.175/platform/ide-core/src/com/intellij/openapi/actionSystem/IdeActions.java&lt;/span&gt;
&lt;span class="c"&gt;# IDE scripting console: https://www.jetbrains.com/help/idea/ide-scripting-console.html&lt;/span&gt;
generate_ide_script&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.ui.Messages
var actionManager = ActionManager.getInstance()
IDE.application.invokeLater {
  try {
    var action = actionManager.getAction("&lt;/span&gt;&lt;span class="nv"&gt;$action&lt;/span&gt;&lt;span class="sh"&gt;")
    var result = actionManager.tryToExecute(action, null, null, null, false)
    if (result.rejected) {
      Messages.showErrorDialog(result.error, "IDE action error")
    }
  } catch (ex) {
    Messages.showErrorDialog(ex.message, "IDE action error")
  }
}
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;

parse_params &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
setup_colors

&lt;span class="c"&gt;# Generate IDE script and execute the given action&lt;/span&gt;
&lt;span class="nv"&gt;ide_script&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$script_dir&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$ide_script_name&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
generate_ide_script &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ide_script&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;$ide&lt;/span&gt; ideScript &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ide_script&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
cleanup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code is also available on GitHub as a &lt;a href="https://gist.github.com/j4ckofalltrades/d7aac303466746e67287441e4fb9e0fe" rel="noopener noreferrer"&gt;gist&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo: Launching the Help Menu programmatically
&lt;/h2&gt;

&lt;p&gt;The Help Menu action is invoked programmatically by clicking on a configured button on the Stream Deck.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WTuQGgPu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://res.cloudinary.com/j4ckofalltrades/image/upload/v1709915929/blog/jetbrains-ide-actions_qyighl.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WTuQGgPu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://res.cloudinary.com/j4ckofalltrades/image/upload/v1709915929/blog/jetbrains-ide-actions_qyighl.gif" alt="Stream deck UI and an Intellij IDEA instance side-by-side running IDE actions launching the Help Menu in the IDE programmatically" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;View full size image &lt;a href="https://res.cloudinary.com/j4ckofalltrades/image/upload/v1709915929/blog/jetbrains-ide-actions_qyighl.gif" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I hope others find this useful; now on to configuring the Stream Deck.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>ide</category>
      <category>automation</category>
    </item>
    <item>
      <title>Contributing to open-source mechanical keyboard firmware</title>
      <dc:creator>Jordan Duabe</dc:creator>
      <pubDate>Tue, 25 Apr 2023 09:16:02 +0000</pubDate>
      <link>https://dev.to/j4ckofalltrades/ncr-80-qmkvia-firmware-4je9</link>
      <guid>https://dev.to/j4ckofalltrades/ncr-80-qmkvia-firmware-4je9</guid>
      <description>&lt;p&gt;Built the &lt;a href="https://jduabe.dev/posts/2022/ncr-80" rel="noopener noreferrer"&gt;NCR-80&lt;/a&gt; last year, great-looking board especially if you like the retro aesthetic, and is a pleasure to type on.&lt;/p&gt;

&lt;p&gt;One thing I did notice was that the product listing points to a Google Drive link to the precompiled &lt;a href="https://drive.google.com/drive/folders/1e3mjUg-N15SFVrExlBiI01-XOKpPm9ry?usp=sharing" rel="noopener noreferrer"&gt;firmware&lt;/a&gt;, but it hasn’t been added to the QMK and VIA repositories. I thought this would be a good weekend project (&lt;strong&gt;Spoiler&lt;/strong&gt;: it took longer than a weekend).&lt;/p&gt;

&lt;p&gt;I wrote the steps of how I got it done (the steps also apply to any keyboard firmware).&lt;/p&gt;

&lt;h2&gt;
  
  
  Converting KBFirmware JSON to QMK
&lt;/h2&gt;

&lt;p&gt;The starting point is to convert the KBFirmware JSON from the provided “QMK” files from the product listing, and then converting them to QMK formatted files.&lt;/p&gt;

&lt;p&gt;There are a couple of tools that can be used for this purpose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recommended: &lt;a href="https://noroadsleft.github.io/kbf_qmk_converter" rel="noopener noreferrer"&gt;KBFirmware JSON to QMK Parser&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Deprecated: &lt;a href="https://kbfirmware.com" rel="noopener noreferrer"&gt;Keyboard Firmware Builder&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The resulting files will be the base of the QMK firmware for the keyboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a QMK pull request
&lt;/h2&gt;

&lt;p&gt;Make sure to read the &lt;a href="https://docs.qmk.fm/#/contributing?id=keyboards" rel="noopener noreferrer"&gt;contribution guide&lt;/a&gt; as a first step. It is also a good idea to check out other supported keyboard firmware to get a feel for the directory structure, and conventions in use.&lt;/p&gt;

&lt;p&gt;A keyboard firmware folder (simplified example) should look something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mt
|-- ncr80
| |-- keymaps
| |-- default
| |-- keymap.c
|-- info.json
|-- readme.md
`-- rules.mk

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In cases where keyboards have multiple versions or revisions e.g. rev1, rev2 or hotswap/soldered, the directory structure will look different; refer to QMK’s contribution guide linked above.&lt;/p&gt;

&lt;p&gt;Link to the pull request on GitHub for reference: &lt;a href="https://github.com/qmk/qmk_firmware/pull/19130" rel="noopener noreferrer"&gt;[Keyboard] Add NCR-80&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a VIA pull request
&lt;/h2&gt;

&lt;p&gt;In order to add VIA support for a keyboard, it is required to enable the VIA feature in QMK, and adding a &lt;code&gt;via&lt;/code&gt;-compatible keymap for the keyboard. You can check out the QMK pull request linked above; look for the &lt;code&gt;keymaps/via&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;Read the VIA docs for &lt;a href="https://www.caniusevia.com/docs/configuring_qmk" rel="noopener noreferrer"&gt;configuring QMK&lt;/a&gt; for a more in-depth guide. I also recommend reading about the &lt;a href="https://www.caniusevia.com/docs/specification" rel="noopener noreferrer"&gt;VIA spec&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It is also required to have the QMK pull request merged in before contributing to the VIA repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  VIA V2 vs V3 definitions
&lt;/h2&gt;

&lt;p&gt;It is basically just a matter of copying the VIA &lt;code&gt;json&lt;/code&gt; files from the Drive link referenced at the start of this post. The main difference here is the location of the definitions depend on the version; &lt;code&gt;V2&lt;/code&gt; definitions are located in the&lt;code&gt;src/&amp;lt;manufacturer&amp;gt;/&amp;lt;keyboard&amp;gt;&lt;/code&gt; directory while the &lt;code&gt;V3&lt;/code&gt; definitions are in the &lt;code&gt;v3/&amp;lt;manufacturer&amp;gt;/&amp;lt;keyboard&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you have a &lt;code&gt;V2&lt;/code&gt; definition, you can convert it a &lt;code&gt;V3&lt;/code&gt; definition by running the &lt;code&gt;scripts/build-all.ts&lt;/code&gt; file in the&lt;a href="https://github.com/the-via/keyboards" rel="noopener noreferrer"&gt;via keyboards&lt;/a&gt; repository.&lt;/p&gt;

&lt;p&gt;Link to the pull request on GitHub for reference: &lt;a href="https://github.com/the-via/keyboards/pull/1548" rel="noopener noreferrer"&gt;Add support for NCR-80&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s it, once the pull request gets merged VIA should be able to detect your keyboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--B_PhbLMI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/j4ckofalltrades/image/upload/v1676107180/keebs/ncr80/ncr-80-via_hlgb5c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--B_PhbLMI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/j4ckofalltrades/image/upload/v1676107180/keebs/ncr80/ncr-80-via_hlgb5c.png" alt="VIA NCR-80" width="800" height="526"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>qmk</category>
      <category>via</category>
    </item>
    <item>
      <title>Writing a custom Powerline plugin</title>
      <dc:creator>Jordan Duabe</dc:creator>
      <pubDate>Sun, 12 Sep 2021 12:20:20 +0000</pubDate>
      <link>https://dev.to/j4ckofalltrades/writing-a-custom-powerline-plugin-1gkk</link>
      <guid>https://dev.to/j4ckofalltrades/writing-a-custom-powerline-plugin-1gkk</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/powerline/powerline" rel="noopener noreferrer"&gt;Powerline&lt;/a&gt; is a tool I use as part of my dev environment setup, and have my config backed up as part of my &lt;a href="https://github.com/j4ckofalltrades/dotfiles" rel="noopener noreferrer"&gt;dotfiles&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While it comes with a lot of integrations out of the box i.e. &lt;code&gt;bash&lt;/code&gt;, &lt;code&gt;zsh&lt;/code&gt;, &lt;code&gt;tmux&lt;/code&gt;, &lt;code&gt;vim&lt;/code&gt;, etc, it also provides a way for you to write your own "segments". This should serve as a quick guide for rolling your own custom Powerline plugin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic structure and configuration
&lt;/h2&gt;

&lt;p&gt;Each powerline segment is a callable object. It is supposed to be either a Python function or &lt;code&gt;powerline.segments.Segment&lt;/code&gt; class. &lt;/p&gt;

&lt;p&gt;I recently wrote my own custom one that displays the current Kubernetes context and namespace, which uses a &lt;code&gt;Segment&lt;/code&gt; class. Here is a shortened version which shows the basic structure.&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;KubernetesSegment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Segment&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Constructs the segment&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s sections with the configured colorscheme and
    visibility options applied.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="nd"&gt;@staticmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;kube_ctx_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Resolves the current active Kubernetes context (and namespace)
        from `$KUBECONFIG`.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;current_context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list_kube_config_contexts&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;1&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;current_context&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;N/A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; \
                   &lt;span class="n"&gt;current_context&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;context&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;namespace&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;default&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__call__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Running powerline-k8s...&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;sections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

        &lt;span class="c1"&gt;# additional logic to determine segment contents
&lt;/span&gt;
        &lt;span class="n"&gt;sections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;contents&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;u&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;\&lt;span class="n"&gt;U00002638&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;highlight_groups&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;k8s&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;divider_highlight_group&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;k8s:divider&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&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;sections&lt;/span&gt;

&lt;span class="n"&gt;k8s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;with_docstring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;KubernetesSegment&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Return the current Kubernetes context and namespace.

It will show the current context and namespace from `$KUBECONFIG`.

Divider highlight group used: ``k8s:divider``.

Highlight groups used: ``k8s``, ``k8s_context``, ``k8s_namespace``.
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Custom segment entry point.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Make sure to add the &lt;a href="https://pypi.org/project/powerline-status" rel="noopener noreferrer"&gt;powerline-status&lt;/a&gt; package as a dependency.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Basically the class contains a function that returns a &lt;a href="https://powerline.readthedocs.io/en/latest/develop/segments.html#segment-dictionary" rel="noopener noreferrer"&gt;Segment dictionary&lt;/a&gt; which tells &lt;code&gt;Powerline&lt;/code&gt; what to display. In this particular example, the following keys were used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;contents&lt;/code&gt;: Actual segment contents, excluding dividers and before/after. May be &lt;code&gt;None&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;highlight_groups&lt;/code&gt;, &lt;code&gt;divider_highlight_group&lt;/code&gt;: Used highlight groups. May be &lt;code&gt;None&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Highlight groups determine the 'style' that is used for a particular segment e.g. background and foreground, divider color to clearly distinguish one segment from another.&lt;/p&gt;

&lt;p&gt;The colors that are available to you will depend on the current colorscheme that you are using. In general you'll need to add the "groups" definition to the colorscheme config file, which in this case was &lt;code&gt;&amp;lt;powerline_dir&amp;gt;/colorschemes/solarized.json&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"k8s"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"fg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"solarized:blue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"bg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"solarized:base02"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"attrs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"k8s:divider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"fg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gray4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nl"&gt;"bg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"solarized:base02"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"attrs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check out the Powerline docs for a more detailed view into&lt;br&gt;
&lt;a href="https://powerline.readthedocs.io/en/latest/configuration.html" rel="noopener noreferrer"&gt;configuration and customization&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The next step is to let Powerline know of the new segment by adding it to the segment's config file. To add the new segment to the current shell prompt, add the following entry to the &lt;code&gt;&amp;lt;powerline_dir&amp;gt;/themes/shell/default.json&lt;/code&gt; config file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"function"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"k8s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;Once all configuration has been done it's time to "install" the segment and try it out, you can do so by executing:&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="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;python3&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;pip&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;editable&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Installing the package in editable mode saves you from having to "re-install" to see the latest changes. If everything went well you should be able to see your new segment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aIiVrfSB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/j4ckofalltrades/image/upload/v1623588713/foss/powerline-k8s_uc0cxj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aIiVrfSB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/j4ckofalltrades/image/upload/v1623588713/foss/powerline-k8s_uc0cxj.png" alt="powerline-k8s" width="560" height="18"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;Having issues with your plugin, try out the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;powerline-lint&lt;/code&gt; to check for errors in the configuration files i.e. colorscheme and/or segment config.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Restart powerline by running &lt;code&gt;powerline-daemon --replace&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configure logging with &lt;a href="https://powerline.readthedocs.io/en/master/develop/segments.html#powerlinelogger-class" rel="noopener noreferrer"&gt;PowerlineLogger&lt;/a&gt; and &lt;a href="https://powerline.readthedocs.io/en/master/configuration/reference.html#config-common-log" rel="noopener noreferrer"&gt;where you want the logs to be written&lt;/a&gt; for easier debugging.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is also worth checking out the &lt;a href="https://powerline.readthedocs.io/en/master/troubleshooting.html" rel="noopener noreferrer"&gt;Powerline docs&lt;/a&gt; for other common issues that you may encounter. &lt;/p&gt;

&lt;h2&gt;
  
  
  Misc
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Check out the full source on GitHub for the &lt;a href="https://github.com/j4ckofalltrades/powerline-k8s" rel="noopener noreferrer"&gt;powerline-k8s&lt;/a&gt; plugin.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you want to share your custom Powerline segment, you might want to check out this guide about &lt;a href="https://dev.to/posts/pypi-publish/"&gt;publishing packages to PyPI&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>kubernetes</category>
      <category>powerline</category>
      <category>python</category>
      <category>pypi</category>
    </item>
    <item>
      <title>Publishing to Maven Central</title>
      <dc:creator>Jordan Duabe</dc:creator>
      <pubDate>Sun, 25 Jul 2021 07:38:23 +0000</pubDate>
      <link>https://dev.to/j4ckofalltrades/publishing-to-maven-central-4958</link>
      <guid>https://dev.to/j4ckofalltrades/publishing-to-maven-central-4958</guid>
      <description>&lt;p&gt;This guide walks you through the necessary steps to upload your package to the &lt;a href="https://repo.maven.apache.org/maven2" rel="noopener noreferrer"&gt;Maven Central&lt;/a&gt; repository (and optionally to GitHub Packages registry) with some recommendations along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup OSSRH Repository
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Sign up for a &lt;a href="https://issues.sonatype.org/secure/Signup!default.jspa" rel="noopener noreferrer"&gt;new account&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Create a &lt;a href="https://issues.sonatype.org/secure/CreateIssue.jspa?issuetype=21&amp;amp;pid=10134" rel="noopener noreferrer"&gt;new project ticket&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Claim your namespace by setting the &lt;code&gt;groupId&lt;/code&gt; for your artifact&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re using a free code hosting service like GitHub or Bitbucket:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;groupId: io.github.j4ckofalltrades&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Alternatively if you want to use a custom domain, the group id should:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;groupId: com.your-custom-domain&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You will also need to prove ownership of the public repository or the custom domain. For the former you will be asked to create an empty repository using the ticket number for your created OSSRH ticket e.g. &lt;code&gt;OSSRH-73148&lt;/code&gt;; for the latter you’ll need to add a &lt;code&gt;TXT&lt;/code&gt; record linked to the OSSRH ticket that was created to register your &lt;code&gt;groupId&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure GPG/PGP key
&lt;/h2&gt;

&lt;p&gt;This will be used for signing your artifacts, at it is a requirement for publishing them to the Central Repository.&lt;/p&gt;

&lt;p&gt;You’ll need to generate a key pair and distribute it to a known key server (so that others can validate it).&lt;/p&gt;

&lt;p&gt;The following examples show how to do this with &lt;a href="https://gnupg.org" rel="noopener noreferrer"&gt;GPG&lt;/a&gt;; you can &lt;a href="https://gnupg.org" rel="noopener noreferrer"&gt;download&lt;/a&gt; or install it using your package manager if it is not already available in your machine.&lt;/p&gt;

&lt;p&gt;To generate a key pair, run:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ gpg --gen-key&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Fill in the details you are prompted for e.g. name, email, time of validity, and passphrase for the generated key. &lt;strong&gt;This passphrase and your private key are all that is needed to sign artifacts with your signature.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the key pair is generated, you’ll need to distribute your public key to a known key server i.e. &lt;code&gt;keyserver.ubuntu.com&lt;/code&gt;. In order to do so, run the following commands:&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;# list gpg keys&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;gpg &lt;span class="nt"&gt;--list-keys&lt;/span&gt;
&lt;span class="c"&gt;# the line starting with **pub** shows your public key details&lt;/span&gt;

&lt;span class="c"&gt;# copy the keyid and run the following command&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;gpg &lt;span class="nt"&gt;--keyserver&lt;/span&gt; keyserver.ubuntu.com &lt;span class="nt"&gt;--send-keys&lt;/span&gt; &amp;lt;your_key_here&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To check that your key was successfully uploaded, you can search for it at &lt;a href="https://keyserver.ubuntu.com" rel="noopener noreferrer"&gt;https://keyserver.ubuntu.com&lt;/a&gt; using your public key (and prepending ‘0x’ to it).&lt;/p&gt;

&lt;p&gt;Other supported key servers are &lt;code&gt;keys.openpgp.org&lt;/code&gt;, and &lt;code&gt;pgp.mit.edu&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure build script
&lt;/h2&gt;

&lt;p&gt;In order to publish your artifact to the Central Repository, it will need to meet the following requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Project coordinates&lt;/li&gt;
&lt;li&gt;Javadoc and sources jar&lt;/li&gt;
&lt;li&gt;POM metadata (includes project name and description, url, SCM info, license)&lt;/li&gt;
&lt;li&gt;Sign artifacts with GPG/PGP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The configuration will vary slightly depending on the build tool you are using, below is an abridged sample configuration &lt;code&gt;build.gradle.kts&lt;/code&gt; file (Kotlin DSL) used with &lt;code&gt;gradle&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// build.gradle.kts&lt;/span&gt;

&lt;span class="c1"&gt;// project coordinates&lt;/span&gt;
&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"io.github.j4ckofalltrades"&lt;/span&gt;
&lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.0.0"&lt;/span&gt;

&lt;span class="c1"&gt;// generate KDoc and Javadoc using Dokka&lt;/span&gt;
&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;withType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DokkaTask&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="nf"&gt;configureEach&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;dokkaSourceSets&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;named&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;outputDirectory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"docs"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"steam-webapi"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Module.md"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"JVM"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jetbrains&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dokka&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Platform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jvm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;sourceLink&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;localDirectory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"src/main/kotlin"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="n"&gt;remoteUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="s"&gt;"https://github.com/j4ckofalltrades/steam-webapi-kt"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt;
                            &lt;span class="s"&gt;"/tree/main/lib/src/main/kotlin"&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;remoteLineSuffix&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"#L"&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;span class="c1"&gt;// javadoc and sources jar&lt;/span&gt;
&lt;span class="nf"&gt;tasks&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;sourcesJar&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="nf"&gt;registering&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Jar&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;dependsOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JavaPlugin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CLASSES_TASK_NAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;archiveClassifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sources"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;archiveBaseName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"steam-webapi-kt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sourceSets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;allSource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;javadocJar&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="nf"&gt;registering&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Jar&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;dependsOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dokkaJavadoc"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;archiveClassifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"javadoc"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;archiveBaseName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"steam-webapi-kt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"docs"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;artifacts&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;archives&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sourcesJar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;archives&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;javadocJar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;archives&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jar&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="nf"&gt;publishing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;publications&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MavenPublication&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"mavenJava"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;groupId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;artifactId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rootProject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
            &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

            &lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"java"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="nf"&gt;artifact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"sourcesJar"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="nf"&gt;artifact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"javadocJar"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

            &lt;span class="c1"&gt;// POM metadata&lt;/span&gt;
            &lt;span class="nf"&gt;pom&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"steam-webapi-kt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Steam WebAPI wrapper in Kotlin and Ktor"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://github.com/j4ckofalltrades/steam-webapi-kt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;licenses&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nf"&gt;license&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MIT"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://opensource.org/licenses/MIT"&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="nf"&gt;developers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nf"&gt;developer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"j4ckofalltrades"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Jordan Duabe"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"me@jduabe.dev"&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="nf"&gt;scm&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"scm:git:git://github.com/j4ckofalltrades/steam-webapi-kt.git"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="n"&gt;developerConnection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"scm:git:ssh://github.com/j4ckofalltrades/steam-webapi-kt.git"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://github.com/j4ckofalltrades/steam-webapi-kt"&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;span class="nf"&gt;repositories&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Maven Central&lt;/span&gt;
        &lt;span class="nf"&gt;maven&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"OSSRH"&lt;/span&gt;
            &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;credentials&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"OSSRH_USERNAME"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"OSSRH_PASSWORD"&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;// GitHub Packages&lt;/span&gt;
        &lt;span class="nf"&gt;maven&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"GitHubPackages"&lt;/span&gt;
            &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://maven.pkg.github.com/j4ckofalltrades/steam-webapi-kt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;credentials&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GITHUB_ACTOR"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GITHUB_TOKEN"&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;span class="c1"&gt;// sign artifact&lt;/span&gt;
&lt;span class="nf"&gt;signing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setRequired&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
       &lt;span class="n"&gt;gradle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;taskGraph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"publishMavenJavaToOSSRHRepository"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;signingKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SIGNING_KEY"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;signingPassword&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SIGNING_PASSWORD"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;useInMemoryPgpKeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;signingKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;signingPassword&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;publishing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;publications&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"mavenJava"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Publishing artifacts
&lt;/h2&gt;

&lt;p&gt;The configuration above uses the &lt;a href="https://docs.gradle.org/current/userguide/signing_plugin.html#sec:in-memory-keys%5D" rel="noopener noreferrer"&gt;in-memory ascii-armored keys&lt;/a&gt; approach for signing artifacts. Check out the docs for the &lt;a href="https://docs.gradle.org/current/userguide/signing_plugin.html" rel="noopener noreferrer"&gt;Gradle Signing Plugin&lt;/a&gt; to find out what best suits your use case.&lt;/p&gt;

&lt;p&gt;Once everything is configured, publishing the artifacts is as simple as running:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ gradle publish&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Note that this will publish your artifacts to &lt;strong&gt;all&lt;/strong&gt; target repositories, in order to publish to a specific repository you need to specify the repo &lt;em&gt;name&lt;/em&gt;. Using the above configuration as an example:&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;# publish to Maven Central&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;gradle publishMavenJavaPublicationToOSSRHRepository

&lt;span class="c"&gt;# publish to GitHub Packages&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;gradle publishMavenJavaPublicationToGitHubPackagesToRepository
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Releasing artifacts
&lt;/h2&gt;

&lt;p&gt;Login to the &lt;a href="https://s01.oss.sonatype.org/#stagingRepositories" rel="noopener noreferrer"&gt;Nexus staging repository&lt;/a&gt;, where you should be able to see your &lt;code&gt;groupId&lt;/code&gt; listed. In order to &lt;em&gt;sync&lt;/em&gt; the artifacts to the Central Repository you’ll need to click on the &lt;code&gt;Close&lt;/code&gt; button which will close said staging repository.&lt;/p&gt;

&lt;p&gt;The close operation will run a series of checks to ensure that the uploaded artifacts meet the requirements.&lt;/p&gt;

&lt;p&gt;If the release was successful, you should be able to see your artifacts in the Central Repository at &lt;a href="https://repo1.maven.org/maven2/," rel="noopener noreferrer"&gt;https://repo1.maven.org/maven2/,&lt;/a&gt; typically within ~30 minutes. Updates to &lt;a href="https://search.maven.org" rel="noopener noreferrer"&gt;https://search.maven.org&lt;/a&gt; can take up to a couple of hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: Automation with GitHub Actions
&lt;/h2&gt;

&lt;p&gt;You’ll probably want to automate this process as part of your CI/CD pipeline. Here’s how to set it up using GitHub Actions.&lt;/p&gt;

&lt;p&gt;You’ll need to add your OSSRH credentials, and signing key details as secrets to your repository, you can find this under Settings &amp;gt; Secrets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish to OSSRH and GitHub Package Registry&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;created&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;publish&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-java@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;distribution&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;adopt-openj9'&lt;/span&gt;
          &lt;span class="na"&gt;java-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;17'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./gradlew publish&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;GITHUB_ACTOR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.actor }}&lt;/span&gt;
          &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;OSSRH_USERNAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.OSSRH_USERNAME }}&lt;/span&gt;
          &lt;span class="na"&gt;OSSRH_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.OSSRH_PASSWORD }}&lt;/span&gt;
          &lt;span class="na"&gt;SIGNING_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SIGNING_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;SIGNING_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SIGNING_PASSWORD }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The configuration samples used here can also be checked out in full at this &lt;a href="https://github.com/j4ckofalltrades/steam-webapi-kt" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That’s it, time to get publishing.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>mavencentral</category>
      <category>gradle</category>
    </item>
    <item>
      <title>Publishing to the npm registry</title>
      <dc:creator>Jordan Duabe</dc:creator>
      <pubDate>Sat, 19 Jun 2021 05:43:02 +0000</pubDate>
      <link>https://dev.to/j4ckofalltrades/publishing-to-the-npm-registry-hl7</link>
      <guid>https://dev.to/j4ckofalltrades/publishing-to-the-npm-registry-hl7</guid>
      <description>&lt;p&gt;This guide walks you through the necessary steps to upload your package to the &lt;a href="https://npmjs.com" rel="noopener noreferrer"&gt;npm registry&lt;/a&gt; with some recommendations along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup npm account
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Register for an &lt;a href="https://npmjs.com/signup" rel="noopener noreferrer"&gt;npm account&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Test your account by logging in using &lt;code&gt;npm&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# you should be prompted for your username, password, and email address&lt;/span&gt;
&lt;span class="c"&gt;# and an OTP if you've enabled 2FA&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;npm login
&lt;span class="c"&gt;# this should print out your npm username&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;npm &lt;span class="nb"&gt;whoami&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Dry-run (Testing your package locally)
&lt;/h2&gt;

&lt;p&gt;Test out your package locally to ensure that everything works before publihsing it to the npm registry. You can do this by invoking &lt;code&gt;npm install&lt;/code&gt; and providing the path to your package e.g.:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ npm install /path/to/your/package&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Another thing to do is to review the contents of the published package to make sure that it doesn’t include any sensitive or unnecessary information. You can perform a &lt;code&gt;dry-run&lt;/code&gt; with:&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;npm publish &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;span class="c"&gt;# output should look something like&lt;/span&gt;
npm notice
npm notice &amp;lt;package_name@version&amp;gt;
npm notice &lt;span class="o"&gt;===&lt;/span&gt; Tarball Contents &lt;span class="o"&gt;===&lt;/span&gt;
npm notice &amp;lt;tarball_contents_here&amp;gt;
npm notice &lt;span class="o"&gt;===&lt;/span&gt; Tarball Details &lt;span class="o"&gt;===&lt;/span&gt;
npm notice &amp;lt;tarball_details_here&amp;gt;
npm notice
+ &amp;lt;package_name@version&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally you can create a &lt;code&gt;.npmignore&lt;/code&gt; file to exclude files that you don’t want to publish to the registry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Publishing to the npm registry
&lt;/h2&gt;

&lt;p&gt;In your package’s root directory, execute:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ npm publish&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If publishing a scoped public package, execute:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ npm publish --access public&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;When the publish process finishes, you should be able to see your public package page at &lt;a href="https://npmjs.com/package/your-package-name" rel="noopener noreferrer"&gt;https://npmjs.com/package/your-package-name&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Congrats, you’ve successfully published your package to the npm registry. 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: Automation with GitHub Actions
&lt;/h2&gt;

&lt;p&gt;You’ll probably want to automate this process as part of your CI/CD pipeline. Here’s how to set it up using GitHub Actions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Create a &lt;a href="https://docs.npmjs.com/creating-and-viewing-access-tokens" rel="noopener noreferrer"&gt;new npm access token&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add the access token as a secret to your target GitHub repository. You can find this under Settings &amp;gt; Secrets for your repo. Give the secret a name e.g.&lt;code&gt;NPM_TOKEN&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a GitHub action workflow file in your repo at&lt;code&gt;.github/workflows/npm-publish.yml&lt;/code&gt; with the following contents:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish package to npm&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;created&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
    &lt;span class="na"&gt;packages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="s"&gt;with&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;18.x'&lt;/span&gt;
        &lt;span class="na"&gt;registry-url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://registry.npmjs.org'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm test&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run build&lt;/span&gt;
    &lt;span class="c1"&gt;# Publish to npm (append `--access public` for scoped packages)&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm publish&lt;/span&gt;
        &lt;span class="s"&gt;env&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;NODE_AUTH_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.NPM_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This workflow is triggered when a new release is created but you can also configure it to be triggered when a different event happens e.g. when a new tag gets pushed. Refer to the &lt;a href="https://docs.github.com/en/actions" rel="noopener noreferrer"&gt;GitHub Actions documentation&lt;/a&gt; for more configuration options.&lt;/p&gt;

&lt;p&gt;That’s it, time to get publishing.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>npm</category>
    </item>
    <item>
      <title>Publishing to PyPI</title>
      <dc:creator>Jordan Duabe</dc:creator>
      <pubDate>Sun, 13 Jun 2021 14:08:53 +0000</pubDate>
      <link>https://dev.to/j4ckofalltrades/publishing-to-pypi-29mk</link>
      <guid>https://dev.to/j4ckofalltrades/publishing-to-pypi-29mk</guid>
      <description>&lt;p&gt;This guide walks you through the necessary steps to upload your package to the 🐍 Python Package Index (PyPI) with some recommendations along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dry-run (Publishing to TestPyPI)
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;This step is optional but recommended.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://test.pypi.org" rel="noopener noreferrer"&gt;TestPyPI&lt;/a&gt; is a separate instance of the &lt;em&gt;real&lt;/em&gt; package index which is intended for testing and experimentation. This is a good way to test out your package before uploading to the real index.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Register an account for &lt;a href="https://test.pypi.org/account/register" rel="noopener noreferrer"&gt;TestPyPI&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a &lt;a href="https://test.pypi.org/manage/account/#api-tokens" rel="noopener noreferrer"&gt;TestPyPI API token&lt;/a&gt; – make sure to set the scope to “Entire account”&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use &lt;a href="https://packaging.python.org/key_projects/#twine" rel="noopener noreferrer"&gt;twine&lt;/a&gt; to upload your package&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&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;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; twine
&lt;span class="c"&gt;# replace dist with the directory where your distribution archive is located&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; twine upload &lt;span class="nt"&gt;--repository&lt;/span&gt; testpypi dist/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will be prompted for your TestPyPI username and password. Use the following values.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;username&lt;/th&gt;
&lt;th&gt;password&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;__token__&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;API token including the &lt;code&gt;pypi-&lt;/code&gt; prefix&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Alternatively you can also use a &lt;code&gt;.pypirc&lt;/code&gt; file to define your package indexes config i.e. TestPyPI, PyPi. If you choose to go this route, create a &lt;code&gt;$HOME/.pypirc&lt;/code&gt; file with the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[testpypi]
username=__token__
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also use the &lt;code&gt;password&lt;/code&gt; field and paste in your API token but the recommended way is to use &lt;code&gt;keyring&lt;/code&gt; (which is installed by Twine) for saving credentials such as API tokens and passwords. You can do so with:&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;keyring &lt;span class="nb"&gt;set &lt;/span&gt;https://test.pypi.org/legacy/ &amp;lt;value_of_api_token&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the upload process has finished, you should be able to view your package on TestPyPI at &lt;a href="https://test.pypi.org/project/your-package-name" rel="noopener noreferrer"&gt;https://test.pypi.org/project/your-package-name&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installing and testing out your package
&lt;/li&gt;
&lt;/ul&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;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--index-url&lt;/span&gt; https://test.pypi.org/simple/ &lt;span class="nt"&gt;--no-deps&lt;/span&gt; your-package
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the &lt;code&gt;no-deps&lt;/code&gt; flag is specified as one or more of your package’s dependencies might not be present in TestPyPI and may cause the installation to fail.&lt;/p&gt;

&lt;p&gt;Once the installation has finished you can test it out by importing your package.&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;python3
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; from example_package import your_function
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; your_function.do_something&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the import package will always be &lt;code&gt;example_package&lt;/code&gt; regardless of the &lt;code&gt;name&lt;/code&gt; you’ve configured for your package.&lt;/p&gt;

&lt;p&gt;That’s it, you can now try and upload your package to the &lt;em&gt;real&lt;/em&gt; index.&lt;/p&gt;

&lt;h2&gt;
  
  
  Publishing to PyPI
&lt;/h2&gt;

&lt;p&gt;The steps are identical with how you would upload a package to TestPyPI. The main difference is that you’ll have to register an account and create an API token at &lt;a href="https://pypi.org" rel="noopener noreferrer"&gt;PyPI&lt;/a&gt;, as the former is a separate instance of PyPI.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Register an account for &lt;a href="https://pypi.org/account/register" rel="noopener noreferrer"&gt;PyPI&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a &lt;a href="https://pypi.org/manage/account/#api-tokens" rel="noopener noreferrer"&gt;PyPI API token&lt;/a&gt; – make sure to set the scope to “Entire account”&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use &lt;a href="https://packaging.python.org/key_projects/#twine" rel="noopener noreferrer"&gt;twine&lt;/a&gt; to upload your package&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&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;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; twine
&lt;span class="c"&gt;# replace dist with the directory where your distribution archive is located&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; twine upload &lt;span class="nt"&gt;--repository&lt;/span&gt; pypi dist/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will be prompted for your PyPI username and password. Use the following values.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;username&lt;/th&gt;
&lt;th&gt;password&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;__token__&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;API token including the &lt;code&gt;pypi-&lt;/code&gt; prefix&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Alternatively you can also use a &lt;code&gt;.pypirc&lt;/code&gt; file to define your package indexes config i.e. TestPyPI, PyPi. If you choose to go this route, create a &lt;code&gt;$HOME/.pypirc&lt;/code&gt; file with the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[pypi]
username=__token__
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also use the &lt;code&gt;password&lt;/code&gt; field and paste in your API token but the recommended way is to use &lt;code&gt;keyring&lt;/code&gt; (which is installed by Twine) for saving credentials such as API tokens and passwords. You can do so with:&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;keyring &lt;span class="nb"&gt;set &lt;/span&gt;https://upload.pypi.org/legacy/ &amp;lt;value_of_api_token&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the upload process finishes, you should be able to view your package on PyPI at &lt;a href="https://pypi.org/project/your-package-name" rel="noopener noreferrer"&gt;https://pypi.org/project/your-package-name&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installing and testing out your package
&lt;/li&gt;
&lt;/ul&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;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;your-package
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the installation has finished you can test it out by importing your package.&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;python3
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; from example_package import your_function
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; your_function.do_something&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Congrats, you’ve successfully published your package to PyPI. 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: Automation with GitHub Actions
&lt;/h2&gt;

&lt;p&gt;You’ll probably want to automate this process as part of your CI/CD pipeline. Here’s how to set it up using GitHub Actions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Create a &lt;a href="https://pypi.org/manage/account/#api-tokens" rel="noopener noreferrer"&gt;new PyPI API token&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add the API token as a secret to your target GitHub repository. You can find this under Settings &amp;gt; Secrets for your repo. Give the secret a name e.g.&lt;code&gt;PYPI_API_TOKEN&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a GitHub action workflow file in your repo at&lt;code&gt;.github/workflows/pypi-publish.yml&lt;/code&gt; with the following contents:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish package to PyPI&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;created&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;build-n-publish&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and publish to PyPI&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@master&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Python &lt;/span&gt;&lt;span class="m"&gt;3.9&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-python@v4&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3.9&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install pypa/build&lt;/span&gt;
        &lt;span class="s"&gt;run&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
        &lt;span class="s"&gt;python -m&lt;/span&gt;
        &lt;span class="s"&gt;pip install&lt;/span&gt;
        &lt;span class="s"&gt;build&lt;/span&gt;
        &lt;span class="s"&gt;--user&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build a binary wheel and a source tarball&lt;/span&gt;
        &lt;span class="s"&gt;run&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
        &lt;span class="s"&gt;python -m&lt;/span&gt;
        &lt;span class="s"&gt;build&lt;/span&gt;
        &lt;span class="s"&gt;--sdist&lt;/span&gt;
        &lt;span class="s"&gt;--wheel&lt;/span&gt;
        &lt;span class="s"&gt;--outdir dist/&lt;/span&gt;
        &lt;span class="s"&gt;.&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish package&lt;/span&gt;
        &lt;span class="s"&gt;uses&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pypa/gh-action-pypi-publish@release/v1&lt;/span&gt;
        &lt;span class="s"&gt;with&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.PYPI_API_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This workflow is triggered when a new release is created but you can also configure it to be triggered when a different event happens e.g. when a new tag gets pushed. Refer to the &lt;a href="https://docs.github.com/en/actions" rel="noopener noreferrer"&gt;GitHub Actions documentation&lt;/a&gt; for more configuration options.&lt;/p&gt;

&lt;p&gt;That’s it, time to get publishing.&lt;/p&gt;

</description>
      <category>python</category>
      <category>pypi</category>
    </item>
    <item>
      <title>Hacktoberfest 2020 Retrospective</title>
      <dc:creator>Jordan Duabe</dc:creator>
      <pubDate>Sat, 23 Jan 2021 04:50:21 +0000</pubDate>
      <link>https://dev.to/j4ckofalltrades/hacktoberfest-2020-retrospective-3b9e</link>
      <guid>https://dev.to/j4ckofalltrades/hacktoberfest-2020-retrospective-3b9e</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qxJOYGVL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/j4ckofalltrades/image/upload/c_limit%2Cw_500/v1611377061/blog/20210123/hacktoberfest_y899nk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qxJOYGVL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/j4ckofalltrades/image/upload/c_limit%2Cw_500/v1611377061/blog/20210123/hacktoberfest_y899nk.jpg" alt="hacktoberfest_shirt" width="500" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This one’s long overdue and when the swag arrived last week I figured I really need to get this done.&lt;/p&gt;

&lt;p&gt;The past year has been tough to say the least and getting to write code for fun while helping out the open-source community was a very welcome distraction.&lt;/p&gt;

&lt;p&gt;Though I have been programming for a while this was my first time participating in Hacktoberfest and contributing to open-source in general, it was nice to get to talk to and interact with maintainers and other like-minded individuals who are passionate about code and giving back to the community.&lt;/p&gt;

&lt;p&gt;Even with all the drama that surrounded this past year’s iteration I’d say this was a positive experience overall, and I look forward to joining this year’s iteration.&lt;/p&gt;

&lt;p&gt;Here are some of the projects I found interesting and contributed to (and still try to contribute to):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/trinodb/trino" rel="noopener noreferrer"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DQJKH7DI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github-readme-stats.vercel.app/api/pin/%3Fusername%3Dtrinodb%26repo%3Dtrino%26theme%3Ddark" alt="trino" width="400" height="150"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/strongbox/strongbox" rel="noopener noreferrer"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LDEi5bwj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github-readme-stats.vercel.app/api/pin/%3Fusername%3Dstrongbox%26repo%3Dstrongbox%26theme%3Ddark" alt="strongbox" width="400" height="120"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/oshi/oshi" rel="noopener noreferrer"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cr_XNFPY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github-readme-stats.vercel.app/api/pin/%3Fusername%3Doshi%26repo%3Doshi%26theme%3Ddark" alt="oshi" width="400" height="120"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/jayehernandez/letra-extension" rel="noopener noreferrer"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--v-eusXzj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github-readme-stats.vercel.app/api/pin/%3Fusername%3Djayehernandez%26repo%3Dletra-extension%26theme%3Ddark" alt="letra-extension" width="400" height="120"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy hacking!&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
