<?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: Pranjal Agrawal</title>
    <description>The latest articles on DEV Community by Pranjal Agrawal (@gcc42).</description>
    <link>https://dev.to/gcc42</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%2F247290%2F07ed1a96-5f85-4c2b-9789-1f6acbe33e91.jpg</url>
      <title>DEV Community: Pranjal Agrawal</title>
      <link>https://dev.to/gcc42</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gcc42"/>
    <language>en</language>
    <item>
      <title>Exporting your Pixel 'Now playing' history</title>
      <dc:creator>Pranjal Agrawal</dc:creator>
      <pubDate>Mon, 07 Sep 2020 21:21:50 +0000</pubDate>
      <link>https://dev.to/gcc42/exporting-your-pixel-now-playing-history-2ad1</link>
      <guid>https://dev.to/gcc42/exporting-your-pixel-now-playing-history-2ad1</guid>
      <description>&lt;p&gt;Google Pixel phones have the handy 'now playing' feature, which is like auto-Shazam - it allows the Pixel to recognize which song is playing in the background and make it visible to you &lt;em&gt;right&lt;/em&gt; at your lock screen. It also allows you to view the now playing history and export a song to Play Music / Spotify / Apple Music from settings. Having this enabled by default, I have been accruing songs on the 'Now playing' for about a year and a half, which amounts to about 1500 songs.&lt;/p&gt;

&lt;p&gt;But, as I recently discovered to my endless frustration when I decided to move off my Pixel, Google doesn't give you a straightforward way to bulk export the list to CSV / Spotify / text. Individually 'Share'-ing every one of 1500 songs and adding it to a Spotify playlist would take me hours, which is simply unacceptable. &lt;/p&gt;

&lt;p&gt;It turns out that it's fairly simple to use &lt;code&gt;adb backup&lt;/code&gt; to extract the 'Now playing' history database from the phone (as of the time of this writing, on Android 9, Pixel 3XL), and then use a simple script to extract data in whatever format is most convenient. I'm going to use shell utilities to generate a CSV with &lt;code&gt;timestamp&lt;/code&gt; and &lt;code&gt;spotify_track_id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The pre-requisites are very simple - you need a working adb setup on your computer handy. There are plenty of guides for all operating systems (for example &lt;a href="https://www.xda-developers.com/install-adb-windows-macos-linux/"&gt;this&lt;/a&gt; one), so I won't elaborate on that here. Once adb is set up properly, connecting your phone via a USB cable and then running &lt;code&gt;./adb devices&lt;/code&gt; on the shell should output something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./adb.exe devices
List of devices attached
8BPK0YFHJ device
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;(I'm using my Ubuntu in Windows 10 installation so you'll see bash commands with &lt;code&gt;.exe&lt;/code&gt; executables in this post). Once you have adb showing your device, you can continue with the steps below.&lt;/p&gt;

&lt;h3&gt;
  
  
  Backing up the app data
&lt;/h3&gt;

&lt;p&gt;This took me a bit of time because it's not clear which app 'Now playing' is associated with. You can access the history in Settings &amp;gt; Sound, but it really wouldn't make sense for it to be integrated with the settings app. So it should contain, for example, to 'now playing' or 'music' in the name, right? Wrong. After a bit of elimination from system apps plus a bit of Googling tells me that the correct app is &lt;strong&gt;Pixel Ambient Servies&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Next, to figure out which package the app corresponds to, I list the system packages (&lt;code&gt;./adb.exe shell pm packages list -s&lt;/code&gt;) and grep for the phrase &lt;code&gt;com.google&lt;/code&gt;; this makes it fairly easy. The package is &lt;code&gt;com.google.intelligence.sense&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The next part requires a bit of luck, and thankfully, Google hasn't disabled the &lt;code&gt;android:allowBackup&lt;/code&gt; flag in the app. If they had, this would have been a dead end (unless you have a rooted phone). The only option then probably would have been to create a touch command based adb script to export each of the songs to a Spotify playlist. And while that wouldn't have required manual work (beyond the scripting), it would have still taken time. That is the last thing I want to do. &lt;/p&gt;

&lt;p&gt;But luckily adb debugging on &lt;code&gt;com.google.intelligence.sense&lt;/code&gt; remains enabled, either because of an oversight, or as an intended 'feature'. We can then backup all app data to our pc:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./adb.exe backup -f ambient.ab com.google.intelligence.sense
WARNING: adb backup is deprecated and may be removed in a future release
Now unlock your device and confirm the backup operation...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;(Another useful feature that's deprecated by Google, what a surprise). Confirm the backup on your device, and it should finish in under a minute. My app has a lot of something called Superpacks which inflate the backup size to around 170 Mb.&lt;/p&gt;

&lt;h3&gt;
  
  
  Extracting the database file
&lt;/h3&gt;

&lt;p&gt;Now that we've extracted the app data, it's time to un-archive it and extract the db. Android backups are essentially tar gzip archives with a bit of a header twist. There are many ways to extract them (including a java utility called &lt;a href="https://github.com/nelenkov/android-backup-extractor"&gt;Android backup extractor&lt;/a&gt;), but I just used the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="s2"&gt;1f&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="s2"&gt;8b&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="s2"&gt;08&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="s2"&gt;00&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="s2"&gt;00&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="s2"&gt;00&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="s2"&gt;00&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="s2"&gt;00"&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; +25 ambient.ab &lt;span class="o"&gt;)&lt;/span&gt; | &lt;span class="nb"&gt;tar &lt;/span&gt;xfvz -
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the above command we pre-pend the &lt;code&gt;\x1f\x8b\x08\x00\x00\x00\x00\x00&lt;/code&gt; header to the &lt;code&gt;ambient.ab&lt;/code&gt; file and then extract it using &lt;code&gt;tar xvzf -&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This should create an &lt;code&gt;apps/com.google.intelligence.sense&lt;/code&gt; in the current directory, which is where the app data resides. We're only interested in the db file &lt;code&gt;apps/com.google.intelligence.sense/db/history_db&lt;/code&gt;, which is a SQLite database file (another bit of luck is that it's un-encrypted).&lt;/p&gt;

&lt;h3&gt;
  
  
  Figuring out the 'Now playing' history database
&lt;/h3&gt;

&lt;p&gt;Install &lt;a href="https://www.sqlite.org/download.html"&gt;SQLlite&lt;/a&gt;, and let's take a look at the database. The command &lt;code&gt;.tables&lt;/code&gt; lists all tables in the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sqlite&amp;gt; .tables
android_metadata recognition_history
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Great, that seems pretty straightforward. The &lt;code&gt;android metadata&lt;/code&gt; table has only one row with the locale, so that's pretty much useless. Let's look at the &lt;code&gt;recognition_history&lt;/code&gt; table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;sqlite&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;PRAGMA&lt;/span&gt; &lt;span class="n"&gt;table_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;recognition_history&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;LONG&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;history_entry&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;BLOB&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;track_id&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;STRING&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;shards_region&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;STRING&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;downloaded_shards_version&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;core_shard_version&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Ah, pay dirt! The &lt;code&gt;history_entry&lt;/code&gt; seems to be a BLOB, but if you take a look at a single row:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sqlite&amp;gt; select * from recognition_history limit 1;
Europe/WarsawJ
/g/11c6tt7zl9Jyandroid-app://com.apple.android.music/https/itunes.apple.com/us/album/confidently-lost/1209647010?i=1209647307&amp;amp;ign-gact=2/m/013d79knJH:https://www.deezer.com/track/143171428/radio?autoplay=true
/m/03c2ckdJVHandroid-app://com.spotify.music/spotify/track/0RTnMChwd0ARVY3zOyNdaP?v=T
/m/04yhd6cJpbhttps://www.iheart.com/artist/Sabrina+Claudio-31483691/songs/Orion%27s+Belt-45519571?autoplay=true
/m/0c3zxqxJyandroid-app://com.google.android.music/http/play.google.com/music/m/Tigjmk45m2k2yqavlr7edrkcut4?signup_if_needed=1&amp;amp;play=1
t_tfgj6vzLvnUjConfidently Lostp|Tigjmk45m2k2yqavlr7edrkcut4|xa|100|2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It looks like the Blob format is just Unicode data. The best part, though, is that this is almost as good as exporting directly to the playlist. Because we have the Spotify track id, the Play Music track id, Deezer id, and Apple Music id here, you can directly (using APIs or a bulk import tool) add the tracks to a playlist regardless of what subscription service you use (as long as it's one of these!). &lt;/p&gt;

&lt;h3&gt;
  
  
  Exporting the database
&lt;/h3&gt;

&lt;p&gt;Finally, let's open the SQLite db, query the &lt;code&gt;recognition_history&lt;/code&gt; for all rows, export it to a file, and convert each row to the desired format. My first instinct was to write a Python script, but that's lengthier and more error prone than just using some &lt;code&gt;sed&lt;/code&gt; to convert each record into the desired format. Besides, it doesn't require any environment setup.&lt;/p&gt;

&lt;p&gt;I'm going to extract the &lt;code&gt;timestamp&lt;/code&gt; and &lt;code&gt;spotify_track_id&lt;/code&gt; into a CSV file. First, we want to export the columns to a file. Because the &lt;code&gt;history_entry&lt;/code&gt; contains '\n' and possibly '\r' characters, we need to remove them so we can have one record per line and use &lt;code&gt;sed&lt;/code&gt; conveniently (hence the use of the &lt;code&gt;replace()&lt;/code&gt; function in the &lt;code&gt;SELECT&lt;/code&gt; below).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;sqlite&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt;
&lt;span class="n"&gt;sqlite&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;mode&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;
&lt;span class="n"&gt;sqlite&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;csv&lt;/span&gt;
&lt;span class="n"&gt;sqlite&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="nb"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;history_entry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;CHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;'|'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;CHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;'|'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;spotify_track_id&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;recognition_history&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This should create a file &lt;code&gt;results.csv&lt;/code&gt; in your current directory with the query rows. Before continuing, ensure that there's one query row per line (there are still non-printable characters and Unicode in the BLOB, that's fine).&lt;/p&gt;

&lt;p&gt;Now let's remove the Unicode characters so &lt;code&gt;sed&lt;/code&gt; doesn't give us problems. Some versions of &lt;code&gt;sed&lt;/code&gt; work with Unicode, but I just spent hours debugging locale issues over this so I'm not a fan. To remove Unicode characters, we use a simple &lt;code&gt;sed&lt;/code&gt; command that searches for characters in the range &lt;code&gt;\x80-\xFF&lt;/code&gt; (i.e decimal 128 - 255, out of the ASCII range). The &lt;code&gt;LC_ALL=C&lt;/code&gt; sets the locale to ISO 'C' and this is the only one that worked properly for me.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;LC_ALL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;C &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'s:[\x80-\xFF]:.:g'&lt;/span&gt; results.csv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-i&lt;/code&gt; switch above ensures that the changes are made to the input file in place. To verify that all Unicode characters are gone, you can check that &lt;code&gt;grep --color=auto -P '[\x80-\xFF]' results.csv&lt;/code&gt; returns no matches. After this, all that's left to do is just &lt;code&gt;sed&lt;/code&gt; out the timestamp and Spotify track id:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'s:^([0-9]+).*spotify/track/([A-Za-z0-9]+).*:\1,\2:g'&lt;/span&gt; results.csv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The regex in the above command matches first a timestamp at the beginning of the line (&lt;code&gt;^[0-9]+&lt;/code&gt;), then matches the rest of the line, taking note of the string &lt;code&gt;spotify/track/[A-Za-z0-9]+&lt;/code&gt; (i.e, extracting the track id).&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;And that's it. &lt;code&gt;head -n 5&lt;/code&gt; your &lt;code&gt;results.csv&lt;/code&gt; file and you should see the timestamp and track ids, one on each line. A bulk add tool or some API calls will help you add the tracks to a playlist (beware of the API quotas though). The above solution is simple to modify (by changing the regex) or extend (by scripting) to fit whatever end goals you have for your tracks in mind.&lt;/p&gt;

</description>
      <category>pixel</category>
      <category>nowplaying</category>
      <category>android</category>
      <category>adb</category>
    </item>
    <item>
      <title>Hugo, Beyond.</title>
      <dc:creator>Pranjal Agrawal</dc:creator>
      <pubDate>Fri, 22 May 2020 02:45:13 +0000</pubDate>
      <link>https://dev.to/gcc42/hugo-beyond-2i1g</link>
      <guid>https://dev.to/gcc42/hugo-beyond-2i1g</guid>
      <description>&lt;h4&gt;
  
  
  Tl; Dr - I tried out Hugo. Setup a template, build system, CI, and blogging workflow. It’s lean and effective.
&lt;/h4&gt;

&lt;p&gt;Ever since I first had the urge to start blogging many years ago, I’ve had a few failed iterations (about five). This is the story documenting my current successful one, and how I set it up using &lt;a href="https://gohugo.io/" rel="noopener noreferrer"&gt;Hugo&lt;/a&gt;. I intend this to be a guide to help rapidly people set up Hugo. If you’ve ever been paralysed by &lt;a href="https://en.wikipedia.org/wiki/Overchoice" rel="noopener noreferrer"&gt;overchoice&lt;/a&gt; while attempting to set up your own site, this may help.    &lt;/p&gt;

&lt;h2&gt;
  
  
  Medium
&lt;/h2&gt;

&lt;p&gt;The first thing one needs to do to start a blog is to choose a medium. Of course, the best advice would be to, you know, just start writing. Yet for the purposes of this post, here we are.&lt;/p&gt;

&lt;p&gt;With the vast number of choices out there, this can sometimes lead to analysis paralysis. The first thing that comes to mind might be traditional CMSs like WordPress and Joomla; they run millions of successful blogs. However, two things hold me back - that they require a supported hosting (likely paid) and they are largely overkill for a blog as simple as the one I would create. Next you may think about recently emergent content sites like Medium and dev.to. And while they &lt;em&gt;are&lt;/em&gt; easy to get started with, they’re too homogenous causing said blog to lose an individual identity or sense of style.&lt;/p&gt;

&lt;p&gt;Finally, consider the recently trending &lt;a href="https://jamstack.org/" rel="noopener noreferrer"&gt;JAMstacks&lt;/a&gt; (or static site generators), which use the build process to hide all the complexity and generate static sites that are &lt;em&gt;ready to deploy&lt;/em&gt;. If you’re building the next great social network, they might not be the best choice, but a blog &lt;em&gt;is&lt;/em&gt; slightly simpler. I chose this option because static site generators are perfect for what I aim to build. They allow me the option to create my site from scratch, involve little setup, and combine the speed and simplicity of static sites with the power of &lt;code&gt;git&lt;/code&gt; versioning and the beauty of Markdown editing.&lt;/p&gt;

&lt;p&gt;Two of the biggest static site generators that captured my attention were Jekyll and Hugo. Ruby-based Jekyll is in its twelfth year of development. It is vastly more mature an ecosystem with gems, themes, and countless supported plugins. In contrast, Hugo (written in Go) is much more in its infancy (version 0.69.x at the time of writing), does not support plugins, and occasionally introduces breaking changes in new versions. I still chose to go with Hugo, because it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Has a small footprint&lt;/strong&gt;: The entire Hugo “environment” comprises a single 72 Mb cross-platform binary that can be downloaded from the &lt;a href="https://github.com/gohugoio/hugo/releases" rel="noopener noreferrer"&gt;releases page&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is blazing-fast&lt;/strong&gt;: It can generate large sites in seconds. The speed also lends itself to its hot reloading feature, which allows you to rebuild local instances instantaneously and iterate quickly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Has a flexible theme system&lt;/strong&gt;: Hugo offers a simple yet powerful theme system with theme composition support so you can ‘plug-in’ components for various functions to support reasonably advanced site structures
 
Overall, Hugo embodies my interpretation of the &lt;a href="https://en.wikipedia.org/wiki/KISS_principle" rel="noopener noreferrer"&gt;KISS&lt;/a&gt; principle well: use the simplest thing that works.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Steps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Theme
&lt;/h3&gt;

&lt;p&gt;The first step, once you’ve decided on Hugo, is to choose a theme. Unlike Jekyll, Hugo does not come with a default theme in place, so this step is not optional. There are &lt;a href="https://themes.gohugo.io/" rel="noopener noreferrer"&gt;many options&lt;/a&gt;, though, of varying sizes: from minimalist to designer. Spend some minutes (or hours, in my case) picking one that you think will best fit the personality of the blog you want to build.&lt;/p&gt;

&lt;h3&gt;
  
  
  Git Repositories
&lt;/h3&gt;

&lt;p&gt;My ideal repository setup adds a fork of the theme repository as a &lt;code&gt;submodule&lt;/code&gt; to the source repository. Say you choose the minimalist &lt;a href="https://themes.gohugo.io/hyde/" rel="noopener noreferrer"&gt;Hyde&lt;/a&gt; theme. Pictorially, this looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5tuekp63yv3o69l1vugb.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5tuekp63yv3o69l1vugb.jpeg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This setup allows me to develop the site independently of my theme, yet still rebase the theme onto the upstream &lt;code&gt;HEAD&lt;/code&gt; periodically. For my site I chose a custom theme based on Nate Finch’s &lt;a href="https://npf.io" rel="noopener noreferrer"&gt;npf.io&lt;/a&gt;, which is based on Hugo’s Hyde which itself is based on Jekyll’s Hyde theme. Open source, at its finest.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;To set up everything, I executed these steps (after installing Hugo):&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Create a new repository for the source and commit it
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;REPONAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pragio
&lt;span class="c"&gt;# Generate a new site&lt;/span&gt;
hugo new site &lt;span class="nv"&gt;$REPONAME&lt;/span&gt;
&lt;span class="c"&gt;# Initialize and add a remote repo (make sure the remote is empty)&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$REPONAME&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git init &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git remote add origin https://github.com/gcc42/pragio
&lt;span class="c"&gt;# Commit and push&lt;/span&gt;
git add . &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git commit &lt;span class="nt"&gt;-m&lt;/span&gt; “Initial site files” &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git push origin 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Add the theme as a &lt;code&gt;submodule&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git submodule add https://github.com/gcc42/hyde themes/hyde
&lt;span class="c"&gt;# Commit and push&lt;/span&gt;
git add . &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git commit &lt;span class="nt"&gt;-m&lt;/span&gt; “Add theme” &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git push origin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Test your site locally
&lt;/h4&gt;

&lt;p&gt;Test your site locally by running the Hugo server: &lt;code&gt;hugo serve&lt;/code&gt; and browsing to &lt;code&gt;localhost:1313&lt;/code&gt; in your browser.&lt;/p&gt;

&lt;p&gt;With the &lt;code&gt;hugo serve&lt;/code&gt;d site on the left in hot reload mode and the editor on the right, I could rapidly iterate over features like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding a tag like gray highlight around tags&lt;/li&gt;
&lt;li&gt;Adding a &lt;code&gt;/cv&lt;/code&gt; page for my resume to the site which redirects to &lt;code&gt;/cv.pdf&lt;/code&gt; (Check out &lt;a href="https://github.com/gcc42/hugo-redirect" rel="noopener noreferrer"&gt;hugo-redirect&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That concludes the one-time setup. From this point forth the most frequent updates will be writing and publishing individual posts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deployment
&lt;/h3&gt;

&lt;p&gt;The last step to having a functioning site is deploying. Because of its simplicity, Hugo allows any possible deployment you can imagine for a static site: a manual build and push to GCS bucket, deployment using &lt;a href="https://pages.github.com/" rel="noopener noreferrer"&gt;Github pages&lt;/a&gt;, or a setup with &lt;a href="https://netlify.com" rel="noopener noreferrer"&gt;Netlify&lt;/a&gt; integration. I went with Netlify because it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Has a generous quota in the free tier, more than enough to build and serve simple sites&lt;/li&gt;
&lt;li&gt;Has native Hugo support&lt;/li&gt;
&lt;li&gt;Integrates well with Github; it hooks into a configurable Github repository and deploys changes automatically &lt;/li&gt;
&lt;li&gt;Supports custom build configurations using &lt;code&gt;netlify.toml&lt;/code&gt; and native redirections using &lt;code&gt;_redirect.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Supports smoothly custom domains and free one-click HTTPS&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are several guides out there on setting up a Netlify integration for your Github repository (for example &lt;a href="https://www.netlify.com/blog/2016/09/21/a-step-by-step-guide-victor-hugo-on-netlify/" rel="noopener noreferrer"&gt;this&lt;/a&gt; one); I won’t waste words on another.&lt;/p&gt;

&lt;p&gt;To summarize, you can choose any option you want. Further, changing it later based on growing needs is simple.&lt;/p&gt;

&lt;h2&gt;
  
  
  A consolidated look
&lt;/h2&gt;

&lt;p&gt;After setting up as described above, the ‘blogging’ process (adding new posts) looks like:&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="nb"&gt;cd &lt;/span&gt;pragio
&lt;span class="c"&gt;# Generate a new post file&lt;/span&gt;
hugo new posts/howdy.md
&lt;span class="c"&gt;# Edit your post using your favourite text editor&lt;/span&gt;
subl content/posts/howdy.md
&lt;span class="c"&gt;# Commit and push&lt;/span&gt;
git add . &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git commit &lt;span class="nt"&gt;-m&lt;/span&gt; “Add post Howdy” &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git push origin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Netlify, by default, builds and deploys your site. Voilà! Your new post is live.&lt;/p&gt;

&lt;h2&gt;
  
  
  Synchronizing with dev.to
&lt;/h2&gt;

&lt;p&gt;This is strictly optional for a little additional convenience.&lt;/p&gt;

&lt;p&gt;If like me, you are a CI/CD enthusiast, and want to set up your blog on an additional platform (for eg, dev.to) you might like to add two-way sync using hooks. The idea is that you draft and publish a post either on dev.to (with their fancy editor) or to your site using the Github repo and the sync mechanism would figure it out and simultaneously update it in the other place:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dev.to &amp;lt;-----&amp;gt; gcc42/pragio&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;However, that is out of the scope of this post and will form a post of its own. &lt;/p&gt;

&lt;p&gt;Hugo is a fast, lightweight static site building environment that is simple to set up, and allows a lot of flexibility in deployment. In conclusion, happy blogging!&lt;/p&gt;

</description>
      <category>hugo</category>
      <category>webdev</category>
      <category>blogging</category>
    </item>
  </channel>
</rss>
