<?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: Adam K Dean</title>
    <description>The latest articles on DEV Community by Adam K Dean (@adamkdean).</description>
    <link>https://dev.to/adamkdean</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%2F319269%2F3391af3d-5bfe-4cee-b6b4-a30bcf07230b.png</url>
      <title>DEV Community: Adam K Dean</title>
      <link>https://dev.to/adamkdean</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/adamkdean"/>
    <language>en</language>
    <item>
      <title>BBC Micro:bit — a story of curiosity</title>
      <dc:creator>Adam K Dean</dc:creator>
      <pubDate>Sat, 12 Nov 2022 20:22:17 +0000</pubDate>
      <link>https://dev.to/adamkdean/bbc-microbit-a-story-of-curiosity-1plm</link>
      <guid>https://dev.to/adamkdean/bbc-microbit-a-story-of-curiosity-1plm</guid>
      <description>&lt;p&gt;I have a six year old (let's call him v2.0). His brain never stops, and my work as an engineer (primarily software) seems to be of great interest to him. He asks lots of questions, but also knows during incidents that I need to focus. After a particularly difficult incident a few months ago, he congratulated me on fixing the "glitch" and called me a "glitch hunter".&lt;/p&gt;

&lt;p&gt;I've been looking at ways to introduce him to the wonderful world of building things: programming, making, hacking, the amazing world that we live in where we both learn and create things on a daily basis. I thought perhaps Python, but the IDEs are too complicated. I thought JavaScript, but should a six year old be introduced to NPM and dependency hell at such a young age? (Yes officer, this parent here.)&lt;/p&gt;

&lt;p&gt;I decided to search, to scour the internet, for something that would fulfil requirements I'd only know when I saw them, and came across the BBC micro:bit, a pocket-sized computer designed for 11-12 year olds (UK year 7, US 6th grade).&lt;/p&gt;

&lt;h2&gt;
  
  
  BBC Micro:bit
&lt;/h2&gt;

&lt;p&gt;The microbit is a great little device, the spiritual successor to the BBC micro from the days of yore, designed to introduce children to both software and hardware. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc7ulnfgez8igys5lb41n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc7ulnfgez8igys5lb41n.png" alt="BBC Micro:bit" width="800" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Onboarding starts with a Scratch-like block coding language, and progresses onto Python (MicroPython, in fact). Microsoft provide an online IDE &lt;a href="https://makecode.microbit.org/"&gt;MakeCode&lt;/a&gt; and there's a wealth of tutorials and guides.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx58qeqeuvbs25s829wfx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx58qeqeuvbs25s829wfx.png" alt="Coding is easy with block code" width="760" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But you can do more with it. You can use JavaScript, Pascal, Simulink, C++, Forth, Lisp, Rust, Ada, Swift, Basic, and even flash it with Zephyr, a lightweight OS.&lt;/p&gt;

&lt;p&gt;While it's really aimed more towards 11-12 year olds, I figured 6 is half of 12 and—armed with this excuse and my own share of excitement (who doesn't love to buy electronics?)—I bought a microbit, and a few extras.&lt;/p&gt;

&lt;p&gt;First, the Inventor's Kit, which comprises of 10 experiments, from a physical hello world to handling human device input through to wind power and capacitor charge circuits.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F46nzq8d4lm9yhe6b4dpd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F46nzq8d4lm9yhe6b4dpd.png" alt="Inventor's Kit" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also decided to buy a robot kit, because robots.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw7pm2fs2f1g4hbxa6hzl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw7pm2fs2f1g4hbxa6hzl.png" alt="Robots are cool" width="800" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial test
&lt;/h2&gt;

&lt;p&gt;The kit arrived and we opened it up. We plugged into the micro-usb and the intro sequence started. I won't spoil it, but it's very cool.&lt;/p&gt;

&lt;p&gt;Now it's not until you try to explain something to a six year old that you realise just how much knowledge you've acquired over the year. Most of us sit here day to day with varying degrees of imposter syndrome, but that's because we become unconsciously competent in our various fields.&lt;/p&gt;

&lt;p&gt;Having had many discussions about electronics, and completing a few of the experiments in the inventors kit, we're now at the point where v2.0 understands (to an age appropriate level) voltage, current, resistance, and diodes. We're still working on capacitors and transistors, and have much more to get to, in the future.&lt;/p&gt;

&lt;p&gt;The experience so far has been amazing. It's been a great educational tool, playing around with the LED matrix, the onboard speaker, the onboard microphone... and then I discovered, the onboard radio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Radio
&lt;/h2&gt;

&lt;p&gt;Now, I'm a bit of a geek. I've always loved making things, but the thing which has always amazed me is communication. I remember writing my first network program at the age of 11 or 12, and communicating with a friend a few hundred miles away in London via UDP packets. Everything grew from there.&lt;/p&gt;

&lt;p&gt;The pandemic wasn't quite the same for me as for most people. I already worked from home, having been fully remote since 2016, and I already baked bread, because carbs are great. So I decided to pick up radio (UK amateur radio, US HAM radio) as a hobby. It's amazing, but a little bit off-topic for this post (though amazing to think that I was able to make contact (albeit one way) with the Halley VI research station in Antartica via a 1mm thick, 40-ish ft long piece of speaker wire!)&lt;/p&gt;

&lt;p&gt;So while playing with the microbit, I discovered that it has a radio module built in. It doesn't have a WiFi chip, but it has 2.4 GHz onboard transceiver, and is able to communicate up to 4 dBm (2.5 mW) with other microbits, at a rate of 1-2 mbps. Just think of the possibilities. This seemed to awaken something inside me, and while I'm 33 years old and the microbit is designed for 11-12 year olds, 33 divided by 11 is 3, and you can't argue with maths, so I bought three more microbits and some more components.&lt;/p&gt;

&lt;p&gt;And today, today I started tinkering (more on that soon), and somehow 10:00 has turned into 20:21 and I've had a blast. We all need something from time to time to bring back the spark that made us excited all those years ago. So I'll probably be writing about my experiments, my forays into electronics, hardware, software, firmware, all the wares, and buying more wares, while the fire of passion burns.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GcddsKxv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://64.media.tumblr.com/e3572bbf6b3409e652e809268b7d147c/tumblr_oyhqttNmPW1trlzg7o1_540.gifv" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GcddsKxv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://64.media.tumblr.com/e3572bbf6b3409e652e809268b7d147c/tumblr_oyhqttNmPW1trlzg7o1_540.gifv" alt="I am on a curiosity voyage, and I need my paddles to travel" width="540" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's to curiosity voyages!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>watercooler</category>
      <category>writing</category>
      <category>hardware</category>
    </item>
    <item>
      <title>10 Tips For New Coders</title>
      <dc:creator>Adam K Dean</dc:creator>
      <pubDate>Fri, 07 Oct 2022 13:45:41 +0000</pubDate>
      <link>https://dev.to/adamkdean/10-tips-for-new-coders-2hl</link>
      <guid>https://dev.to/adamkdean/10-tips-for-new-coders-2hl</guid>
      <description>&lt;p&gt;I've been reading and writing code since the year 2000. Before we had Stack Overflow and Codeacademy, we had two real resources for learning: giant dusty tomes filled with scraps of paper, and websites like PlanetSourceCode where you could download zips of  entire projects.&lt;/p&gt;

&lt;p&gt;I didn't always have access to the internet back then, but I had an old computer, a collection of 3½-inch 1.44 MB floppy disks, and legs, so I'd walk to friends houses and download as much code as I could fit on the floppy disks then go home to take it all apart. When I got stuck, I'd call my mentor who had a seemingly endless supply of both enthusiasm and patience.&lt;/p&gt;

&lt;p&gt;I can't recall much of what I created during those formative years, but I do remember the process I followed. &lt;/p&gt;

&lt;p&gt;So here are 10 tips for new coders.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Don't be afraid to ask for help&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nobody knows everything and everyone was a beginner once. Asking for help is a perfectly normal and acceptable thing to do when coding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Start small&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Don’t try to tackle a huge project right off the bat. Start with something small and simple to get the hang of the basics. Building a proof of concept is much easier than building a product, so build small and build often.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Read other people's code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of the best ways to learn how to code is to read other people's code. Not only will you learn new techniques, but you'll also get a feel for how other people think about coding and problem solving.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Write your own code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Don't just copy and paste other people's code. Try to write your own code as much as possible. Not only will this help you learn, but you'll also develop your own style and approach to coding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Experiment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Coding is all about trial and error. Don't be afraid to experiment with different code to see what works and what doesn't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Take breaks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Coding can be mentally taxing. Make sure to take breaks every now and then to clear your head and come back refreshed. Here's an &lt;a href="https://www.science.org/content/article/mentally-exhausted-study-blames-buildup-key-chemical-brain"&gt;interesting article&lt;/a&gt; I read recently on why this may be.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Organise your code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Keep your code organised and tidy. Not only will this make it easier for you to find things, but it will also make it easier for others to read and understand your code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. Use comments&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use comments to explain what your code does. This will help you remember what you did later on, and it will also help others who read your code. Here are &lt;a href="https://dev.to/adamkdean/base64-encode-explained-3an0"&gt;two&lt;/a&gt; &lt;a href="https://dev.to/adamkdean/base64-decode-explained-5dd2"&gt;functions&lt;/a&gt; I wrote a while back, and without those comments, I'd be lost reading it now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;9. Version control&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Version control (also referred to as source code management) is important for keeping track of changes to your code. Make sure to use some form of version control (like Git) so you can easily revert back to previous versions if needed.&lt;/p&gt;

&lt;p&gt;In the old days, it was common to make changes locally and upload files via FTP and just &lt;em&gt;overwrite&lt;/em&gt; previous versions. It was wild.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;10. Have fun!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Coding can be challenging, but it can also be fun. Don't forget to enjoy the process and the satisfaction that comes with solving coding problems.&lt;/p&gt;

&lt;p&gt;If you enjoyed this, take a look at &lt;a href="https://dev.to/adamkdean"&gt;some of my other posts&lt;/a&gt; and follow me for more coding content.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>programming</category>
      <category>hacktoberfest</category>
      <category>codenewbie</category>
    </item>
    <item>
      <title>Slack Notifications with GitHub Actions</title>
      <dc:creator>Adam K Dean</dc:creator>
      <pubDate>Fri, 14 Aug 2020 09:30:49 +0000</pubDate>
      <link>https://dev.to/adamkdean/slack-notifications-with-github-actions-1lk5</link>
      <guid>https://dev.to/adamkdean/slack-notifications-with-github-actions-1lk5</guid>
      <description>&lt;h3&gt;
  
  
  My Workflow
&lt;/h3&gt;

&lt;p&gt;When I first started working with GitHub about a month ago, there were already quite a few Slack notification actions available, but I found each to either be cumbersome to use, or lacking what seemed like simple features such as status based messages. &lt;/p&gt;

&lt;p&gt;So I created &lt;code&gt;adamkdean/simple-slack-notify&lt;/code&gt; to try and address those issues, and this post will show you how to use it.&lt;/p&gt;

&lt;p&gt;The main features are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As simple or complex as you need it to be&lt;/li&gt;
&lt;li&gt;Status based messages meaning one step handles job successes, failures, and cancellations&lt;/li&gt;
&lt;li&gt;JavaScript strings for embedding environment variables or custom logic into notification strings&lt;/li&gt;
&lt;li&gt;Easy to add fields based on standard Slack JSON inputs&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
   Example usage
&lt;/h4&gt;

&lt;p&gt;The simplest use would consist of relying on the webhook's defaults and simply providing some text.&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="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;Simple notification&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;adamkdean/simple-slack-notify@master&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;text&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;This&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;simplest&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;notification'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Overriding the channel is sometimes needed, such as to separate out builds, deployments, and alerts perhaps.&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="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;Channel specific notification&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;adamkdean/simple-slack-notify@master&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;channel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#alerts'&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Something&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;happening&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;someone&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;should&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;probably&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;panic'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The above works well, but what would really make someone panic is if we make the alert red, right?&lt;/p&gt;

&lt;p&gt;You can use &lt;code&gt;danger&lt;/code&gt;, &lt;code&gt;warning&lt;/code&gt;, &lt;code&gt;good&lt;/code&gt;, or a hex code such as &lt;code&gt;#d90000&lt;/code&gt;.&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="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;Panic inducing notification&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;adamkdean/simple-slack-notify@master&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;channel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#alerts'&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Something&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;happening&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;someone&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;should&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;probably&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;panic'&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;danger'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Perhaps you also want to change the username?&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="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;Panic Bot notification&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;adamkdean/simple-slack-notify@master&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;channel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#alerts'&lt;/span&gt;
    &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Panic&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Bot'&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Something&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;happening&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;someone&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;should&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;probably&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;panic'&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;danger'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The action also supports fields, but due to the limitations of GitHub actions only passing in inputs as strings, we can't use yaml arrays. So, this is how you'd specify a field:&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="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;Specifying what to panic about notification&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;adamkdean/simple-slack-notify@master&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;channel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#alerts'&lt;/span&gt;
    &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Panic&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Bot'&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Something&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;happening&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;someone&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;should&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;probably&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;panic'&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;danger'&lt;/span&gt;
    &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;[{ "title": "Reason to panic", "value": "Deployed failed halfway through" }]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If there were multiple reasons to panic, you'd add more objects to the fields array:&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="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;Specifying what to panic about notification&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;adamkdean/simple-slack-notify@master&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;channel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#alerts'&lt;/span&gt;
    &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Panic&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Bot'&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Something&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;happening&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;someone&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;should&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;probably&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;panic'&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;danger'&lt;/span&gt;
    &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;[{ "title": "Reason to panic", "value": "Deployed failed halfway through", "short": true },&lt;/span&gt;
       &lt;span class="s"&gt;{ "title": "Timestamp", "value": "${Date.now()}", "short": true }]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Did you notice that some JavaScript snook in? Input strings are evaluated as a JavaScript strings, which means you can put environment variables into your messages, such as the &lt;code&gt;GITHUB_WORKFLOW&lt;/code&gt; variable or &lt;code&gt;GITHUB_RUN_NUMBER&lt;/code&gt; etc. The environment is stored within the &lt;code&gt;env&lt;/code&gt; variable so to access environment variables in your strings, you simply use &lt;code&gt;${env.GITHUB_REPOSITORY}&lt;/code&gt; etc. Here's an example:&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="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;Environment variable notification&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;adamkdean/simple-slack-notify@master&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;channel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#example'&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${env.GITHUB_WORKFLOW}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(${env.GITHUB_RUN_NUMBER})&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;has&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;finished'&lt;/span&gt;
    &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;[{ "title": "Repository", "value": "${env.GITHUB_REPOSITORY}", "short": true },&lt;/span&gt;
       &lt;span class="s"&gt;{ "title": "Branch", "value": "${env.BRANCH}", "short": true }]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now, each job has a status, which can be &lt;code&gt;success&lt;/code&gt;, &lt;code&gt;failed&lt;/code&gt;, or &lt;code&gt;cancelled&lt;/code&gt;. Most other notification plugins use multiple blocks with &lt;code&gt;if: success()&lt;/code&gt; and &lt;code&gt;if: failed()&lt;/code&gt; etc but we don't need to do that. We can simply pass in the status and set status specific text. We use &lt;code&gt;if: always()&lt;/code&gt; so that it runs regardless of whether the job is successful or not.&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="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 notification&lt;/span&gt;
  &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always()&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;adamkdean/simple-slack-notify@master&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;channel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#builds'&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ job.status }}&lt;/span&gt;
    &lt;span class="na"&gt;success_text&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${env.GITHUB_WORKFLOW}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(${env.GITHUB_RUN_NUMBER})&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;completed&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;successfully'&lt;/span&gt;
    &lt;span class="na"&gt;failure_text&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${env.GITHUB_WORKFLOW}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(${env.GITHUB_RUN_NUMBER})&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;failed'&lt;/span&gt;
    &lt;span class="na"&gt;cancelled_text&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${env.GITHUB_WORKFLOW}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(${env.GITHUB_RUN_NUMBER})&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;was&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;cancelled'&lt;/span&gt;
    &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;[{ "title": "Repository", "value": "${env.GITHUB_REPOSITORY}", "short": true },&lt;/span&gt;
       &lt;span class="s"&gt;{ "title": "Branch", "value": "${env.BRANCH}", "short": true }]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Below I show how I'm using this in an actual build &amp;amp; deployment workflow.&lt;/p&gt;
&lt;h3&gt;
  
  
  Submission Category:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Maintainer Must-Haves&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Yaml File or Link to Code
&lt;/h3&gt;

&lt;p&gt;The following example Yaml file shows &lt;code&gt;adamkdean/simple-slack-notify&lt;/code&gt; being integrated into a build &amp;amp; deploy pipeline.&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;Build &amp;amp; Deploy&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;push&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_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;SLACK_WEBHOOK_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SLACK_WEBHOOK_URL }}&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build project&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;appleboy/ssh-action@master&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;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DEPLOY_HOST }}&lt;/span&gt;
        &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DEPLOY_USER }}&lt;/span&gt;
        &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DEPLOY_SSH_KEY }}&lt;/span&gt;
        &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DEPLOY_PORT }}&lt;/span&gt;
        &lt;span class="na"&gt;script_stop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;# Your build commands here&lt;/span&gt;
          &lt;span class="s"&gt;example_build_project&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 notification&lt;/span&gt;
      &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always()&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;adamkdean/simple-slack-notify@master&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;channel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#builds'&lt;/span&gt;
        &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ job.status }}&lt;/span&gt;
        &lt;span class="na"&gt;success_text&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Build&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(#${env.GITHUB_RUN_NUMBER})&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;completed&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;successfully'&lt;/span&gt;
        &lt;span class="na"&gt;failure_text&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Build&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(#${env.GITHUB_RUN_NUMBER})&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;failed'&lt;/span&gt;
        &lt;span class="na"&gt;cancelled_text&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Build&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(#${env.GITHUB_RUN_NUMBER})&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;was&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;cancelled'&lt;/span&gt;
        &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;[{ "title": "Action URL", "value": "${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}/actions/runs/${env.GITHUB_RUN_ID}"}]&lt;/span&gt;

  &lt;span class="na"&gt;deploy&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;Deploy&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;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy project&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;appleboy/ssh-action@master&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;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DEPLOY_HOST }}&lt;/span&gt;
        &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DEPLOY_USER }}&lt;/span&gt;
        &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DEPLOY_SSH_KEY }}&lt;/span&gt;
        &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DEPLOY_PORT }}&lt;/span&gt;
        &lt;span class="na"&gt;script_stop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;# Your deploy commands here&lt;/span&gt;
          &lt;span class="s"&gt;example_deploy_project&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;Deploy notification&lt;/span&gt;
      &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always()&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;adamkdean/simple-slack-notify@master&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;channel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#deployments'&lt;/span&gt;
        &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ job.status }}&lt;/span&gt;
        &lt;span class="na"&gt;success_text&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Deployment&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(#${env.GITHUB_RUN_NUMBER})&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;completed&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;successfully'&lt;/span&gt;
        &lt;span class="na"&gt;failure_text&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Deployment&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(#${env.GITHUB_RUN_NUMBER})&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;failed'&lt;/span&gt;
        &lt;span class="na"&gt;cancelled_text&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Deployment&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(#${env.GITHUB_RUN_NUMBER})&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;was&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;cancelled'&lt;/span&gt;
        &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;[{ "title": "Host", "value": "${{ secrets.DEPLOY_HOST }}"},&lt;/span&gt;
           &lt;span class="s"&gt;{ "title": "Action URL", "value": "${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}/actions/runs/${env.GITHUB_RUN_ID}"}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Additional Resources / Info
&lt;/h3&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/adamkdean"&gt;
        adamkdean
      &lt;/a&gt; / &lt;a href="https://github.com/adamkdean/simple-slack-notify"&gt;
        simple-slack-notify
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Slack notification action that just works
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Simple Slack Notify&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/dac5e9e993b4d28cf5ee432d7fd51a6218b8257d122af091aa3f7571836c4158/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f762f72656c656173652f656467652f73696d706c652d736c61636b2d6e6f74696679"&gt;&lt;img src="https://camo.githubusercontent.com/dac5e9e993b4d28cf5ee432d7fd51a6218b8257d122af091aa3f7571836c4158/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f762f72656c656173652f656467652f73696d706c652d736c61636b2d6e6f74696679" alt="GitHub release (latest by date)"&gt;&lt;/a&gt; &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/c6eb08e94ed420bcde98b5cf6aaa9257069c2fa29ed021ad30e4ba464c65f13c/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f72656c656173652d646174652f656467652f73696d706c652d736c61636b2d6e6f74696679"&gt;&lt;img src="https://camo.githubusercontent.com/c6eb08e94ed420bcde98b5cf6aaa9257069c2fa29ed021ad30e4ba464c65f13c/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f72656c656173652d646174652f656467652f73696d706c652d736c61636b2d6e6f74696679" alt="GitHub Release Date"&gt;&lt;/a&gt; &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/d4b0d82e9dff28588c0096fe58e7c6d8c120824ac174aee593c87411750a5546/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f656467652f73696d706c652d736c61636b2d6e6f74696679"&gt;&lt;img src="https://camo.githubusercontent.com/d4b0d82e9dff28588c0096fe58e7c6d8c120824ac174aee593c87411750a5546/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f656467652f73696d706c652d736c61636b2d6e6f74696679" alt="License"&gt;&lt;/a&gt; &lt;a href="http://standardjs.com" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/1b4a7edec58f8cf70f51794c37bc1bc7aa37ce17d15d28c6cea7fa5a2af1981b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f636f64652532307374796c652d7374616e646172642d627269676874677265656e2e737667" alt="js-standard-style"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Slack notification action that just works&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;No longer maintained&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;Please note that this project is no longer maintained, and has been archived.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Introduction&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;We've attempted to use a few of the Slack notification actions that are currently available, but they all seem to have limitations or be quite verbose, so we set out to create a simple yet effective action that just does what you need and nothing else. In the examples below, we'll show a few different variations of how the action could be used.&lt;/p&gt;

&lt;p&gt;The main features are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Status based messages meaning one step handles job successes, failures, and cancellations&lt;/li&gt;
&lt;li&gt;JavaScript strings for embedding environment variables or custom logic into notification strings&lt;/li&gt;
&lt;li&gt;Easy to add fields based on standard Slack JSON inputs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Be sure that you set the &lt;code&gt;SLACK_WEBHOOK_URL&lt;/code&gt; environment variable, either in the job or in the step like this:&lt;/p&gt;

&lt;div class="highlight highlight-source-yaml notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;- &lt;span class="pl-ent"&gt;uses&lt;/span&gt;: &lt;span class="pl-s"&gt;edge/simple-slack-notify@master&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/adamkdean/simple-slack-notify"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;h4&gt;
  
  
  Notes
&lt;/h4&gt;

&lt;p&gt;I just wanted to point out for anyone new to GitHub Actions that while above I'm referring to the master branch (i.e. &lt;code&gt;adamkdean/simple-slack-notify@master&lt;/code&gt;), if you're using this in production, it's always wiser to use a version. As of writing, the latest version is &lt;code&gt;1.0.4&lt;/code&gt; so we would use &lt;code&gt;adamkdean/simple-slack-notify@1.0.4&lt;/code&gt;. &lt;/p&gt;

</description>
      <category>actionshackathon</category>
      <category>slack</category>
      <category>github</category>
      <category>actions</category>
    </item>
    <item>
      <title>Notes on Remote Working</title>
      <dc:creator>Adam K Dean</dc:creator>
      <pubDate>Fri, 27 Mar 2020 15:33:29 +0000</pubDate>
      <link>https://dev.to/adamkdean/notes-on-remote-working-nfa</link>
      <guid>https://dev.to/adamkdean/notes-on-remote-working-nfa</guid>
      <description>&lt;p&gt;So with the ongoing global crisis, a lot of governments and organisations are recommending that people, if they can, work from home. We're seeing memes flying around, new jokes being created, and also quite a few questions from people who haven't really worked remotely before.&lt;/p&gt;

&lt;p&gt;I've worked entirely remotely for almost half a decade, and prior to that, partially remotely for a couple of years. I've had many conversations with people over this time, some who wonder how I ever get work done, some who think I don't have a real job, and others who are envious of what seems like an easy job — I mean, surely I can just eat and watch TV all day?&lt;/p&gt;

&lt;p&gt;Over the last couple of weeks, I've been speaking with friends and colleagues who have worked remotely for years, about the challenges that they have faced and continue to face, and how they dealt with these challenges.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do you get up in the morning?
&lt;/h3&gt;

&lt;p&gt;I've tried all sorts of routines, from having an alarm to having no alarm, waking up early to get work done, to waking up at 10am fresh and ready for a day of &lt;del&gt;watching TV&lt;/del&gt; hard work. I've even tried working through the night and sleeping during the day.&lt;/p&gt;

&lt;p&gt;There are a few things which determine &lt;em&gt;how&lt;/em&gt; you get up in a morning, and one of the main determining factors is how your employer works with schedules. &lt;/p&gt;

&lt;p&gt;At Edge for example we follow an asynchronous workflow with a few scheduled calls throughout the week. This means you get your work done in your time, and you accept that your colleagues may not be able to respond to you right away. There are of course situations where you need to synchronise, such as releases and rollouts but these aren't set in stone and you tend to fall into a rhythm with your team. For example, in a morning I often call my CTO for a chitchat. With another team member, we tend to have catch ups through the week to make sure we're on track. None of this is planned.&lt;/p&gt;

&lt;p&gt;If your employer has a set schedule, requiring you to be online from 09:00—17:00 for example, then that settles it for you. Often this is called "working from home". But for companies which don't try to fit remote work into an office shaped hole, we call this "remote-first."&lt;/p&gt;

&lt;p&gt;So how do I get up in the morning? Currently I have Alexa set to tell me the news at sunrise before wishing me a Happy &lt;code&gt;&amp;lt;day&amp;gt;&lt;/code&gt; although recently that's been a little disturbing, I must admit. I ignore it sometimes, and unless there is a pressing need to get up early, some days I take an extra hour or two. But you have to be careful not to oversleep, or you risk being even more tired. Some biological quirk I guess.&lt;/p&gt;

&lt;h2&gt;
  
  
   Dealing with extended periods of isolation
&lt;/h2&gt;

&lt;p&gt;One of the things which most people will now be experiencing is the feeling of isolation from working from home. This isn't new, and has been something we remote workers have been dealing with for some time. It can be lonely, especially when you live alone. I'm told cats help.&lt;/p&gt;

&lt;p&gt;It's important to have a support network of friends and family (or other remote workers) that you can call whenever you're feeling lonely. Sometimes it's useful just to have a chat, so you shouldn't ever feel like you're bothering a colleague by calling. Just ask "got a minute to chat?"&lt;/p&gt;

&lt;p&gt;Socialising becomes more of an effort though, so arranging things in advance is useful. We recently started doing informal video chats (where previously we'd stick to audio) so that the team could let off a bit of steam, have a laugh, and not worry too much about talking shop and instead just talk about life. For example, we all laughed at a team member explaining how his government have stolen all the chairs from businesses. Where do the chairs go!?&lt;/p&gt;

&lt;p&gt;Another thing which can be a struggle is the lack of background noise. Yesterday we rolled out a large release of a number of systems, so I put together a call with half a dozen of my colleagues invited. It wasn't a meeting, just an open channel to discuss the release, talk, joke, and for some, to have some background noise and chatter. Even engineers who weren't directly involved joined to chat.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do you motivate yourself to shower?
&lt;/h3&gt;

&lt;p&gt;So this was a &lt;a href="https://twitter.com/thockin/status/1237393403656073229"&gt;question on Twitter&lt;/a&gt;, but I've been asked it in person as well. If you work remotely, specifically from home, why shower? &lt;/p&gt;

&lt;p&gt;It depends on why you shower, bathe, clean your teeth, brush your hair, etc. Are you doing these things for the sake of others, or is it because you have self-respect? Cleanliness is important regardless of whether you're around others.&lt;/p&gt;

&lt;p&gt;Now, just like how not everyone who works remotely wears trousers, neither does everyone who works remotely have a shower in the morning, but showering once a day is important. I generally find that if it seems like everyone smells no matter where I go, then it is in fact likely that I am the smell.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do you get work done and not watch TV?
&lt;/h3&gt;

&lt;p&gt;This applies to a number of activities, including eating the entire fridge, playing video games, reading great books (if you're a book worm like me), or even finding yourself on a 4 hour YouTube video binge, or in a Wikipedia rabbit hole. Ever more likely is the chance of being stuck in the news cycle, reading and watching the latest on this pandemic.&lt;/p&gt;

&lt;p&gt;The most important thing here is to timebox your consumption. Read the news, play a video game, watch some TV with your breakfast, but have a schedule and know when you need to switch it off and start working. Know how long you need to work for or what tasks you need to achieve. The hardest thing is to get started, so by setting arbitrary limits, you can more easily stop doing the easy stuff and get started with the hard stuff.&lt;/p&gt;

&lt;p&gt;If I remember correctly, a colleague of mine once announced that he played Dark Souls every morning before work so that no matter what happened, his day got better. That's wisdom.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don't you get sick of being stuck at home?
&lt;/h3&gt;

&lt;p&gt;Yes, yes we do, but a lot of remote workers still take the time to go for walks, or to go to coffee shops etc. That's obviously an issue right now in a lot of the world. In the UK, you're allowed to go for one walk, run, or cycle a day. I recommend you go for that walk. But hey, that's not always so easy.&lt;/p&gt;

&lt;p&gt;One thing a few of my colleagues told me was that it's hard to have the motivation to exercise when your daily commute is 14 seconds and it's so easy to sit down and start working. Forcing yourself to go for a walk as much as possible is important, but if you have the space, indoor exercise equipment is a lot more accessible and you're much more likely to use it.&lt;/p&gt;

&lt;p&gt;Before the lockdown, I'd often take a book and get a coffee from a drive through and just relax in the relative peace of the car, usually while the chaos of rush hour went on around me. Sometimes, it's just about a change of scenery.&lt;/p&gt;

&lt;h3&gt;
  
  
  Knowing when to stop working
&lt;/h3&gt;

&lt;p&gt;This is perhaps one of the biggest challenge I have personally faced. I enjoy the work I do, and blimey, there's a lot of it to do, so often I can find myself working right into the evening, suddenly becoming conscious of my surroundings, thirsty, cold, and in the dark. It's important to know when to stop working. If you live with a partner, it can be really helpful to synchronise your working day with theirs.&lt;/p&gt;

&lt;p&gt;In this ever more interconnected world we live in, where we're all isolated together, it can be hard to split work life from home life. Many of us engineers have to be available in the event of outages, but it's important to know the difference between an urgent issue and an asynchronous query. Learn where your line is and stick to it.&lt;/p&gt;

&lt;p&gt;Sometimes it can be useful to have a time that you finish work, and other times it can be useful to stop working after an event or piece of work is complete. Just as you may sometimes work until 18:30 to finish a deployment, it's also okay to call it a day at 16:00 after a deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Are you even working?
&lt;/h3&gt;

&lt;p&gt;One thing which many remote workers experience, especially in the beginning, is the question of whether they're actually working. For the older generations, it's hard to accept that someone is home but working. Are they playing on the computer? Aren't they just for games? This is a difficult problem to solve as it's all about comprehension. The best we can do is hope that some good comes of this currently global situation and that remote work is seen by many for the possibilities it holds.&lt;/p&gt;

&lt;p&gt;It's important to let family members know that you're working, that this is your livelihood, and that you're not available just because you're home, and that you shouldn't be interrupted as though you were relaxing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;Remote working is a wonderful thing, but it's not without its challenges. Most of those challenges stem from how we interact with others, so the solution also lies in how we interact with others. Let's support each other, have more calls, and be more understanding. &lt;/p&gt;

&lt;p&gt;And listen to your watch, it's time to stand up!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;P.S. This was my 200th blog post. Yay!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>career</category>
      <category>life</category>
      <category>leadership</category>
    </item>
    <item>
      <title>Setup your own persistent web IRC client</title>
      <dc:creator>Adam K Dean</dc:creator>
      <pubDate>Mon, 16 Mar 2020 14:28:05 +0000</pubDate>
      <link>https://dev.to/adamkdean/setup-your-own-persistent-web-irc-client-3678</link>
      <guid>https://dev.to/adamkdean/setup-your-own-persistent-web-irc-client-3678</guid>
      <description>&lt;p&gt;These days, Slack is all the rage, but in years gone by, whenever facing world-changing events, IRC has been the place to hangout. These days, you don't seem to hear much about it, and when you try and introduce people to it, the old and somewhat dated clients are hard to sell.&lt;/p&gt;

&lt;p&gt;I've used all manner of clients over the years. I think I started with mIRC back in the early 00s, and over time I've used bouncers (these are services that keep you connected to a network which you in turn connect to) and command line clients. The last client I used was an online cloud based IRC client named irccloud. It's great, but it's a few quid a month.&lt;/p&gt;

&lt;p&gt;Recently, an online acquaintance setup an IRC server, to help folks working remotely, to have somewhere outside of Twitter to discuss current affairs etc. World changing event in place, I needed an IRC client, a persistent one.&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%2Ft6rv03a9huxad1d66eal.png" 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%2Ft6rv03a9huxad1d66eal.png" alt="The Lounge"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I had a look around and found &lt;a href="https://thelounge.chat/" rel="noopener noreferrer"&gt;thelounge&lt;/a&gt;. The name doesn't do much for me. I'm not a fan of the word &lt;code&gt;the&lt;/code&gt; in a name, but it's okay, breath Adam. The easiest way to test something like this is to spin up an instance. So I went to &lt;a href="https://www.scaleway.com/en/" rel="noopener noreferrer"&gt;Scaleway&lt;/a&gt; and created a small machine for €2.99/mo, grabbed the IP and setup a domain, let's say &lt;code&gt;irc.example.com&lt;/code&gt;. (Setting up the domain simply means creating an &lt;code&gt;A&lt;/code&gt; record that points to the IP address of your server.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;On the server itself, the only setup we need is to install docker.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

# apt update
# apt install docker.io -y

... Done


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

&lt;/div&gt;

&lt;p&gt;Once we have docker, we'll spin up an automatic reverse proxy service along with an automatic LetsEncrypt certificate generation service. It's super simple, and to learn more, read this award winning article about it right here on dev: &lt;a href="https://dev.to/adamkdean/automatic-ssl-with-let-s-encrypt-nginx-4nfk"&gt;Automatic SSL with Let's Encrypt &amp;amp; Nginx&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First, the nginx proxy.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

docker run \
  --detach \
  --restart always \
  --publish 80:80 \
  --publish 443:443 \
  --name nginx-proxy \
  --network service-network \
  --volume /var/run/docker.sock:/tmp/docker.sock:ro \
  --volume nginx-certs:/etc/nginx/certs \
  --volume nginx-vhost:/etc/nginx/vhost.d \
  --volume nginx-html:/usr/share/nginx/html \
  jwilder/nginx-proxy


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

&lt;/div&gt;

&lt;p&gt;Then the letsencrypt companion service.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

docker run \
  --detach \
  --restart always \
  --name nginx-proxy-letsencrypt \
  --network service-network \
  --volume /var/run/docker.sock:/var/run/docker.sock:ro \
  --volume nginx-certs:/etc/nginx/certs \
  --volume nginx-vhost:/etc/nginx/vhost.d \
  --volume nginx-html:/usr/share/nginx/html \
  --env NGINX_PROXY_CONTAINER="nginx-proxy" \
  jrcs/letsencrypt-nginx-proxy-companion


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

&lt;/div&gt;

&lt;p&gt;Now we're ready to setup the IRC service. It's so easy.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

docker run \
  --detach \
  --restart always \
  --name irc \
  --network service-network \
  --env VIRTUAL_HOST=irc.example.com \
  --env LETSENCRYPT_HOST=irc.example.com \
  --env LETSENCRYPT_EMAIL="webmaster@example.com" \
  --volume ~/.thelounge:/var/opt/thelounge \
  thelounge/thelounge:latest


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;Well that was easy. Now we can head to &lt;code&gt;https://irc.example.com&lt;/code&gt; and be met with the initial sign in screen. But, first, we need to configure it and create a user. To do this, on the server, take a look inside the &lt;code&gt;~/.thelounge/&lt;/code&gt; directory (which maps into the container). You'll see a few files and directories in there. You can read more about the configuration in the &lt;a href="https://thelounge.chat/docs/configuration" rel="noopener noreferrer"&gt;thelounge documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can, if you like, modify the server settings. You do this by editing &lt;code&gt;config.js&lt;/code&gt;. The only option I changed here was the channel leave message &lt;code&gt;leaveMessage&lt;/code&gt; which defaults to &lt;code&gt;"The Lounge - https://thelounge.chat"&lt;/code&gt;, which I changed to &lt;code&gt;""&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The main thing to do is to create a user. For this, we need to use the &lt;code&gt;thelounge&lt;/code&gt; binary. Easiest way to do that is to hop into the container, so let's do that.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

# docker exec -ti 93a08 bash

root@93a08cb52985:/# 


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

&lt;/div&gt;

&lt;p&gt;Now to create a user we simply run &lt;code&gt;thelounge add yournamehere&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

root@93a08cb52985:/# thelounge add example

2020-03-16 13:52:12 [PROMPT] Enter password: *types dogsarebetterthancats*

2020-03-16 13:52:48 [INFO] User example created.
2020-03-16 13:52:48 [INFO] User file located at /var/opt/thelounge/users/example.json.


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

&lt;/div&gt;

&lt;p&gt;There may be some additional logging, some warnings, but don't worry about that. You can now login with those credentials at &lt;code&gt;https://irc.example.com&lt;/code&gt; and proceed to add networks, setup your IRC users, and so forth. When you open the site, you'll be able to browse your IRC networks. And when you leave, you'll remain connected via this service. This is important because with IRC, if you're not in the channel, you won't get messages.&lt;/p&gt;

&lt;p&gt;Now, that's done, welcome to the real social network(s).&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>beginners</category>
      <category>social</category>
      <category>docker</category>
    </item>
    <item>
      <title>Sorting objects with undefined values</title>
      <dc:creator>Adam K Dean</dc:creator>
      <pubDate>Thu, 12 Mar 2020 14:46:05 +0000</pubDate>
      <link>https://dev.to/adamkdean/sorting-objects-with-undefined-values-5fdj</link>
      <guid>https://dev.to/adamkdean/sorting-objects-with-undefined-values-5fdj</guid>
      <description>&lt;p&gt;So take a look at this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const a = { id: 'a', timestamp: 1570790874500 }
const b = { id: 'b' }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may have some data like this at some point, and you might try a comparison thinking that a defined value will always be higher and truthier than an undefined one.&lt;/p&gt;

&lt;p&gt;You might try and sort them, expecting the undefined timestamps to fall to the bottom. But they won't.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; const c = [b, a]
&amp;gt; c.sort((i, j) =&amp;gt; i.timestamp &amp;gt; j.timestamp)

(2) [{…}, {…}]
  0: {id: "b"}
  1: {id: "a", timestamp: 1570790874500}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's take a look at some comparisons which don't really help us.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; undefined &amp;gt; 1570790874500

false

&amp;gt; 1570790874500 &amp;gt; undefined

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

&lt;/div&gt;



&lt;p&gt;The best thing to do in this situation is to check the existence of the timestamp field within the sort predicate and only compare the values when the field exists. Depending on whether you want the objects with the undefined field first or last, you change which object you check for timestamp and return true when they are undefined.&lt;/p&gt;

&lt;p&gt;Let's create some data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; const list = [
    { id: 'a', timestamp: 1535090874500 },
    { id: 'b' },
    { id: 'c' },
    { id: 'd', timestamp: 1570790874500 },
    { id: 'e', timestamp: 1510790874500 }
  ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So for undefined last, you check the first object passed in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; list.sort((a, b) =&amp;gt; !!a.timestamp ? a.timestamp &amp;gt; b.timestamp : true)

[ { id: 'e', timestamp: 1510790874500 },
  { id: 'a', timestamp: 1535090874500 },
  { id: 'd', timestamp: 1570790874500 },
  { id: 'c' },
  { id: 'b' } ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And for undefined first, you check the second object passed in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; list.sort((a, b) =&amp;gt; !!b.timestamp ? a.timestamp &amp;gt; b.timestamp : true)

[ { id: 'b' },
  { id: 'c' },
  { id: 'e', timestamp: 1510790874500 },
  { id: 'a', timestamp: 1535090874500 },
  { id: 'd', timestamp: 1570790874500 } ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And of course, the comparison here, &lt;code&gt;a.timestamp &amp;gt; b.timestamp&lt;/code&gt; defines the sort order of the objects where the field &lt;em&gt;is&lt;/em&gt; present.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>weird</category>
    </item>
    <item>
      <title>Image Resolutions in CSS</title>
      <dc:creator>Adam K Dean</dc:creator>
      <pubDate>Thu, 05 Mar 2020 14:27:56 +0000</pubDate>
      <link>https://dev.to/adamkdean/image-resolutions-in-css-46o8</link>
      <guid>https://dev.to/adamkdean/image-resolutions-in-css-46o8</guid>
      <description>&lt;p&gt;This week's post will be a short one. The idea came about while offering suggestions for a dear friend's blog, mostly around typography and space. It's funny, I found it hard to describe typography with words, the irony, but while making suggestions and tweaking the layout, I had an idea. &lt;/p&gt;

&lt;p&gt;One of the images on his example blog post was taken during a recent business trip, but at &lt;code&gt;5184x3888&lt;/code&gt; it had an aspect ratio of &lt;code&gt;1.33:1&lt;/code&gt; and didn't fit very well within the text. I felt it was too... tall!&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%2Ftt7ozeh9zi3304789kms.jpg" 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%2Ftt7ozeh9zi3304789kms.jpg" alt="Original"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Object fit, 60% width
&lt;/h3&gt;

&lt;p&gt;So I thought, well, let's squash it a bit then, and set the height to &lt;code&gt;60%&lt;/code&gt; and used &lt;code&gt;object-fit&lt;/code&gt; to ensure it didn't distort. I found this made it fit better within the text, but then I had a further idea.&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%2Fc6hk1ahnpxo6k4q8ze2n.jpg" 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%2Fc6hk1ahnpxo6k4q8ze2n.jpg" alt="2.22:1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At &lt;code&gt;60%&lt;/code&gt; height, the &lt;code&gt;5184x3888&lt;/code&gt; (&lt;code&gt;1.33:1&lt;/code&gt;) image retains a width of &lt;code&gt;5184&lt;/code&gt; but then has a height of &lt;code&gt;2332&lt;/code&gt;. If we normalise this to make it easier to work with, we have an original image of &lt;code&gt;1000x750&lt;/code&gt;. Reducing the height by &lt;code&gt;40%&lt;/code&gt; gives us &lt;code&gt;450&lt;/code&gt;, resulting in an image of &lt;code&gt;1000x450&lt;/code&gt; and an aspect ratio of &lt;code&gt;2.22:1&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cinematic resolution
&lt;/h3&gt;

&lt;p&gt;I looked up the standard cinema resolution, which is &lt;code&gt;1.85:1&lt;/code&gt;. This means that if we want to have a cinematic feel to it, our image should ideally be &lt;code&gt;1000x540&lt;/code&gt; (&lt;code&gt;1.85:1&lt;/code&gt;). Note, &lt;code&gt;540&lt;/code&gt; is &lt;code&gt;72%&lt;/code&gt; of &lt;code&gt;750&lt;/code&gt;.&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%2Fgd5d8hhptl3bhia84unq.jpg" 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%2Fgd5d8hhptl3bhia84unq.jpg" alt="1.85:1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, at &lt;code&gt;72%&lt;/code&gt;, the image isn't ask skinny as the &lt;code&gt;60%&lt;/code&gt; version, but also, it doesn't really feel as cinematic, does it? It's time to add some bars!&lt;/p&gt;

&lt;h3&gt;
  
  
  Bars
&lt;/h3&gt;

&lt;p&gt;The first thing I tried was to take the original image resolution (&lt;code&gt;1.33:1&lt;/code&gt;) and place the &lt;code&gt;60%&lt;/code&gt; resolution (&lt;code&gt;2.22:1&lt;/code&gt;) on top of it, but alas, that didn't really give the right result.&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%2F3k1vr9v9fbrkvod8pw71.jpg" 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%2F3k1vr9v9fbrkvod8pw71.jpg" alt="1.33:1 bars with 2.22:1 image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

.image-1p33-60pc {
  display: flex;
  justify-content: center;
  flex-direction: column;
  background: black;
  margin: 1rem auto;

  // 1.33:1
  width: 52rem;
  height: 40rem;

  img {
    width: 100%;
    height: 60%;
    object-fit: cover;
  }
}


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

&lt;/div&gt;

&lt;p&gt;So next, I tried to slim down the bars by putting the &lt;code&gt;2.22:1&lt;/code&gt; image on top of the &lt;code&gt;1.85:1&lt;/code&gt; image, with a black background of course to create the bars, but this didn't look right either, it felt too skinny. This required some extra calculation.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;1.33:1&lt;/code&gt; is equal to &lt;code&gt;1000x750&lt;/code&gt;&lt;br&gt;
&lt;code&gt;1.85:1&lt;/code&gt; is equal to &lt;code&gt;1000x540&lt;/code&gt;&lt;br&gt;
&lt;code&gt;2.22:1&lt;/code&gt; is equal to &lt;code&gt;1000x450&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;So if we want to place a &lt;code&gt;1.33:1&lt;/code&gt; image into a &lt;code&gt;1.85:1&lt;/code&gt; container and have it assume the &lt;code&gt;2.22:1&lt;/code&gt; resolution, we have to divide the &lt;code&gt;2.22:1&lt;/code&gt; height (&lt;code&gt;450&lt;/code&gt;) by the &lt;code&gt;1.85:1&lt;/code&gt; height (&lt;code&gt;540&lt;/code&gt;) which gives us &lt;code&gt;0.83&lt;/code&gt;.&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%2Fcgow3y3dbpzn38rvkq7h.jpg" 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%2Fcgow3y3dbpzn38rvkq7h.jpg" alt="1.85:1 bars with 2.22:1 image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

.image-1p85-83pc {
  display: flex;
  justify-content: center;
  flex-direction: column;
  background: black;
  margin: 1rem auto;

  // 1.85:1
  width: 52rem;
  height: 28rem;

  img {
    width: 100%;
    height: 83%;
    object-fit: cover;
  }
}


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

&lt;/div&gt;

&lt;p&gt;Next I tried to put a &lt;code&gt;1.85:1&lt;/code&gt; image into a &lt;code&gt;1.33:1&lt;/code&gt; container:&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%2Ff8najy99h931jgjwc2uz.jpg" 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%2Ff8najy99h931jgjwc2uz.jpg" alt="1.33:1 bars with 1.85:1 image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

.image-1p33-72pc {
  display: flex;
  justify-content: center;
  flex-direction: column;
  background: black;
  margin: 1rem auto;

  // 1.33:1
  width: 52rem;
  height: 40rem;

  img {
    width: 100%;
    height: 72%;
    object-fit: cover;
  }
}


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

&lt;/div&gt;

&lt;p&gt;At this point, my friend had already taken my typographical feedback and made some changes and I was left here with a page full of scribbles and calculations, a headache, and a little bit confused as to what I was trying to achieve. &lt;/p&gt;

&lt;p&gt;But I also learned that &lt;a href="https://css-tricks.com/snippets/css/a-guide-to-flexbox/" rel="noopener noreferrer"&gt;flexbox&lt;/a&gt; is great for layouts, especially when you want to do something like add cinematic bars to an image.&lt;/p&gt;

</description>
      <category>css</category>
      <category>design</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Simple SCSS with 11ty</title>
      <dc:creator>Adam K Dean</dc:creator>
      <pubDate>Mon, 24 Feb 2020 19:32:03 +0000</pubDate>
      <link>https://dev.to/adamkdean/simple-scss-with-11ty-kmn</link>
      <guid>https://dev.to/adamkdean/simple-scss-with-11ty-kmn</guid>
      <description>&lt;p&gt;Since migrating my blog to DEV, I've been looking at rebuilding my website as a simple signpost to my various activities on the web, and with this I've been looking into static site generators, and focusing mostly on 11ty.&lt;/p&gt;

&lt;p&gt;I've been looking through &lt;a href="https://github.com/philhawksworth/eleventyone"&gt;philhawksworth/eleventyone&lt;/a&gt; which for the most part gives a nice overview of the workings of 11ty. But one thing it didn't include was SCSS preprocessing. Instead, Phil opted for standard css preprocessing.&lt;/p&gt;

&lt;p&gt;One thing I'm good at is searching the internet, but try as I might for almost 5 long minutes, I couldn't find a good solution out there for this. Phil created a gulp-powered SCSS preprocessor, but I didn't really want to add gulp into the mix. He wrote about how as you add in SCSS your complexity increases but I don't think this &lt;em&gt;has&lt;/em&gt; to be the case. I carried on looking, and I found someone who was preprocessing SCSS and writing it to a file. I didn't want to generate files before I generate files either, that xzibits¹ too much recursion for my liking.&lt;/p&gt;

&lt;p&gt;After looking at everything I'd seen so far, and deciding to do some thinking of my own, I came up with a way that you can easily add SCSS preprocessing to an 11ty project.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;As I understand it, by having a file with a &lt;code&gt;.11ty.js&lt;/code&gt; suffix, you're able to introduce some functionality into an endpoint, the path of which you can define in the &lt;code&gt;permalink&lt;/code&gt; field of the &lt;code&gt;data()&lt;/code&gt; method in the class you return. You can then define the output of that endpoint by returning a string from the &lt;code&gt;render()&lt;/code&gt; methods of that class.&lt;/p&gt;

&lt;p&gt;Well, once you realise this, it's quite simple really. Assuming we have a directory structure like so, with the scss files within the includes directory and a JavaScript file within the css directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;site/
site/_includes/
site/_includes/scss/
site/_includes/scss/main.scss
site/css/
site/css/style.11ty.js
site/index.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All we need to do is use a scss-to-css module (in this case &lt;code&gt;node-sass-promise&lt;/code&gt;), render the scss files into css, and then for good measure, clean it before we send it out.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node-sass-promise&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CleanCSS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;clean-css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inputFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../_includes/scss/main.scss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;outputFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../css/style.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;comment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`/* This is an example comment */`&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="err"&gt;{
  &lt;/span&gt;&lt;span class="nc"&gt;data&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="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;permalink&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;css/style.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;eleventyExcludeFromCollections&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sass&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inputFile&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CleanCSS&lt;/span&gt;&lt;span class="p"&gt;({}).&lt;/span&gt;&lt;span class="nf"&gt;minify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;css&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="nx"&gt;styles&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&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;p&gt;Now, I'm &lt;em&gt;notorious&lt;/em&gt; for adding comments to the beginning of every source file. I'm old school, and also, I feel old. But when I work with a file that doesn't have a banner comment I don't feel right. With this in mind, I've also added the ability to place a header comment in while removing all other chaff from the scss and placing the clean, processed css afterwards.&lt;/p&gt;

&lt;p&gt;This all results in the endpoint &lt;code&gt;css/style.css&lt;/code&gt; being generated via the &lt;code&gt;render()&lt;/code&gt; method and outputting something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* This is an example comment */&lt;/span&gt;

&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;3rem&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So to summarise, you create a file &lt;code&gt;site/css/style.11ty.js&lt;/code&gt; in which you read from an input file &lt;code&gt;../_includes/scss/main.scss&lt;/code&gt;, run it through some npm packages (sass, clean-css etc) and then return it as a string so that the 11ty generator can store it somewhere useful.&lt;/p&gt;




&lt;p&gt;On the subject of banner comments, I much prefer fancier versions that a simple line of text, such as &lt;em&gt;catsofinternet.org&lt;/em&gt;...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            _              __ _       _                       _                    
           | |            / _(_)     | |                     | |                   
   ___ __ _| |_ ___  ___ | |_ _ _ __ | |_ ___ _ __ _ __   ___| |_   ___  _ __ __ _ 
  / __/ _` | __/ __|/ _ \|  _| | '_ \| __/ _ \ '__| '_ \ / _ \ __| / _ \| '__/ _` |
 | (_| (_| | |_\__ \ (_) | | | | | | | ||  __/ |  | | | |  __/ |_ | (_) | | | (_| |
  \___\__,_|\__|___/\___/|_| |_|_| |_|\__\___|_|  |_| |_|\___|\__(_)___/|_|  \__, |
                                                                              __/ |
                                                                             |___/ 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or &lt;em&gt;internetdogs.com&lt;/em&gt;...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

      .-.          /                            /   
      `-'.  .-.---/---.-.  ).--..  .-.   .-.---/--- 
     /    )/   ) /  ./.-'_/      )/   )./.-'_ /     
  _.(__. '/   ( /   (__.'/      '/   ( (__.' /      
          .    `-                     `-            
         /                                          
    .-../ .-._..-.    .      .-.  .-._..  .-. .-.   
   (   / (   )(   )  / \ .-.(    (   )  )/   )   )  
    `-'-..`-'  `-/-'/ ._)`-' `---'`-'  '/   /   (   
             -._/  /                             `-'

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

&lt;/div&gt;



&lt;p&gt;Or &lt;em&gt;httprefs.org&lt;/em&gt;...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 __ __  ______  ______  ____  ____     ___  _____  _____      ___   ____    ____ 
|  |  ||      ||      ||    \|    \   /  _]|     |/ ___/     /   \ |    \  /    |
|  |  ||      ||      ||  o  )  D  ) /  [_ |   __(   \_     |     ||  D  )|   __|
|  _  ||_|  |_||_|  |_||   _/|    / |    _]|  |_  \__  |    |  O  ||    / |  |  |
|  |  |  |  |    |  |  |  |  |    \ |   [_ |   _] /  \ | __ |     ||    \ |  |_ |
|  |  |  |  |    |  |  |  |  |  .  \|     ||  |   \    ||  ||     ||  .  \|     |
|__|__|  |__|    |__|  |__|  |__|\_||_____||__|    \___||__| \___/ |__|\_||___,_|

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

&lt;/div&gt;



&lt;p&gt;But I mean, that's not really important is it? If you &lt;em&gt;do&lt;/em&gt; like banner comments like this, the &lt;a href="http://patorjk.com/software/taag/"&gt;patorjk taag&lt;/a&gt; site is super useful and has been for many years. &lt;/p&gt;

&lt;p&gt;Check it out.&lt;/p&gt;




&lt;p&gt;¹ xzibit joke not typo&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>design</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Debugging Docker Containers</title>
      <dc:creator>Adam K Dean</dc:creator>
      <pubDate>Thu, 20 Feb 2020 11:43:00 +0000</pubDate>
      <link>https://dev.to/adamkdean/debugging-docker-containers-17fh</link>
      <guid>https://dev.to/adamkdean/debugging-docker-containers-17fh</guid>
      <description>&lt;p&gt;Since my post about &lt;a href="https://dev.to/adamkdean/automatic-ssl-with-let-s-encrypt-nginx-4nfk"&gt;docker and SSL&lt;/a&gt; made the top 7 last week, I've been thinking about things which I may possibly know but take for granted, things which may be useful to other people. There are several things I could discuss like the inner workings of containers, the more practical side of using docker, how docker elbows its way into your iptables etc, but I think for this week's post I'm going to stick to something simple, though powerful and super easy: debugging docker containers.&lt;/p&gt;

&lt;p&gt;One thing I've noticed while observing new users of docker over the years is that for all intents and purposes, containers appear to be these little black boxes connected together via all manner of tubes and pipes, with nothing to indicate what's going on other than a couple of lights and a lack of fire.&lt;/p&gt;

&lt;p&gt;I imagine it looks something a little bit like this.&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%2Fiaoygmhabbq89f4no5i5.png" 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%2Fiaoygmhabbq89f4no5i5.png" alt="My artistic rendering of containers through the lens of a newbie who doesn't really understand what is really going on"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, the first you can do to find out what's going on is to check the logs of a container. Most folks know this, but you can do that using the &lt;code&gt;docker logs&lt;/code&gt; command, like this.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ docker logs website

[WEBSITE] Connecting to API...
[WEBSITE] Metal Tube connected to API
[WEBSITE] Loading blog posts via metal tube from API
[WEBSITE] Blog posts loaded
[WEBSITE] Listening on port 80


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

&lt;/div&gt;

&lt;p&gt;Sometimes, things take a bit longer. You kinda just have to wait for it, but that's fine, we can watch and wait using the &lt;code&gt;-f&lt;/code&gt; or follow flag.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ docker logs -f api

[API] Connecting to database...
[API] Connection established via glass pipe
[API] Request received from website, loading data... 
[API] Data loaded [ 4%]
[API] Data loaded [11%]
[API] Data loaded [19%]
[API] Data loaded [24%]


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

&lt;/div&gt;

&lt;p&gt;This is great for logs that are piped to the standard output (&lt;code&gt;stdout&lt;/code&gt;) but you know, sometimes people containerise the strangest of things. I've seen all sorts, including containers within containers.&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%2Fdj4jly94ahy9wmh5epqt.png" 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%2Fdj4jly94ahy9wmh5epqt.png" alt="One time someone took a container and they put the container into a container and then they did it again, and again, and eventually they had containers full of containers and used these containers to build a container system, called Docker"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sometimes an application will write to a log file, so &lt;code&gt;docker logs&lt;/code&gt; won't really help you. Maybe you have a database that isn't really doing anything. Perhaps it's running really slowly. The container is running...&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ docker ps

CONTAINER ID    IMAGE         STATUS          NAMES
1e98dd08aee8    project/web   Up 10 minutes   website
b76207f35778    project/api   Up 10 minutes   api
35f9ba3ed4c8    project/db    Up 10 minutes   database


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

&lt;/div&gt;

&lt;p&gt;...but for some reason when we check the container logs, there is nothing apparent going on, and we have to delve deeper.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ docker logs database

[DATABASE] Loading...
[DATABASE] Listening on port 21000


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

&lt;/div&gt;

&lt;p&gt;We need to find the hidden logs. To find them, we must enter the &lt;del&gt;haunted labyrinth&lt;/del&gt; running container, which we can do easily by using the &lt;code&gt;docker exec&lt;/code&gt; command. The way it works is that you specify a command and a container to run it in, and docker executes that command within the container for you. If we run an interactive shell then we can have a poke around the container. Psst, here's a link so you can read more about &lt;a href="https://docs.docker.com/engine/reference/commandline/exec/" rel="noopener noreferrer"&gt;docker exec&lt;/a&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ docker exec -ti database bash

root@35f9ba3ed4c8:/#


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

&lt;/div&gt;

&lt;p&gt;We're in. You'll notice that we used the &lt;code&gt;-ti&lt;/code&gt; flags. According to the docker docs, &lt;code&gt;-t&lt;/code&gt; or &lt;code&gt;--tty&lt;/code&gt; allocates a pseudo-TTY and &lt;code&gt;-i&lt;/code&gt; or &lt;code&gt;--interactive&lt;/code&gt; keeps stdin open even if not attached. All we need to know is that it hooks us up to the container so we can have ourselves a little look around, and find those pesky logs.&lt;/p&gt;

&lt;p&gt;This is a really powerful trick, and I've used it so many times over the years. Sometimes you need to look at an application that is running, or find some logs, or perhaps you need to *gulp* edit some code while an application is running without going through the standard build process. &lt;/p&gt;

&lt;p&gt;Once you've attached to the container and spawned a shell, you're free to look around, and that's so useful. You can easily read logs.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ docker exec -ti database bash

root@35f9ba3ed4c8:/# tail -f /var/log/database.log

[MyDB][0.0001] started spooling database contraption
[MyDB][0.0004] reading tables from an ancient .dat file
[MyDB][0.0012] unable to read data, tables.dat corrupted...


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

&lt;/div&gt;

&lt;p&gt;Bingo. &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%2Fq37dbaqmyfqetc9z76jl.png" 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%2Fq37dbaqmyfqetc9z76jl.png" alt="Breakfast: juice, lunch: juice, dinner: juice"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sometimes, you might not be able to spawn a bash shell, especially if the container is based on a slimmed down image that's been on a juice detox, but you tend to always be able to rely on &lt;code&gt;sh&lt;/code&gt; being available.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ docker exec -ti database sh

# ps -p $$
  PID TTY          TIME CMD
   56 pts/0    00:00:00 sh


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

&lt;/div&gt;

&lt;p&gt;There are a few other useful tricks as well. You might not use these often but every now and then they'll come in handy. First up, &lt;code&gt;docker top&lt;/code&gt;. This is like the &lt;code&gt;top&lt;/code&gt; we all know well, except it runs within a container. Containers usually only have one process running, but that's not always the case.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ docker top database

PID                 USER                TIME                COMMAND
2157                999                 0:35                mydb --bind_ip_all


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

&lt;/div&gt;

&lt;p&gt;Then you have the &lt;code&gt;docker stats&lt;/code&gt; command. You can either run it against a specific container or view all running containers. It's useful when you want to monitor containers under load, or if you think you may have a memory leak somewhere. I've had to shorten the output a little bit, but you get the idea.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ docker stats api

ID      NAME  CPU %   MEM USAGE / LIMIT     MEM %   NET I/O       BLOCK I/O
b76207  api   0.31%   44.52MiB / 15.64GiB   0.28%   1.75kB / 0B   1.64MB / 14.2MB


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

&lt;/div&gt;

&lt;p&gt;Now, when we run &lt;code&gt;docker ps&lt;/code&gt; we see some information about a container: the image it's running, its status, when it was created etc, however containers actually have a lot more information behind the scenes such as where their filesystems are stored, what IP address they have assigned, and we can view all of this via the &lt;code&gt;docker inspect&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;The actual output is much larger, so I've truncated it to give you a rough idea of what it looks like. This is perhaps 10% of the information actually available.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ docker inspect website

[
    {
        "Id": "1e98dd08aee8b1c6e79eccf0551f4af88299a52fec567a8b27ad4711fe2ac287",
        "Created": "2020-02-19T15:25:13.582120171Z",
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false
        },
        "Image": "sha256:6d5319f761c08cb3053c676a274ce6500f31c98fb1c1fcab8ab736e39968a2fe",
        "LogPath": "/var/lib/docker/containers/1e1933e26402e3b062051c63c3fbc6d327641059213192ab6b94d1afb36484ca/1e1933e26402e3b062051c63c3fbc6d327641059213192ab6b94d1afb36484ca-json.log",
        "Name": "/website",
        "RestartCount": 0,
        "Driver": "overlay2",
        "Platform": "linux"
    }
]


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

&lt;/div&gt;

&lt;p&gt;If you know what you're looking for, it's easier to extract that information than it is to sift through the entire document. You can do that using the &lt;code&gt;--format&lt;/code&gt; argument. For example, getting the IP address of a container.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ docker inspect --format '{{.NetworkSettings.IPAddress}}' api

172.17.0.3


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

&lt;/div&gt;

&lt;p&gt;The last trick I want to show you is less about docker, but it's still super useful. Sometimes you may have running services in containers but you're not able to connect to them. Is it a firewall issue? A networking issue? Is it an application issue? Well, whenever we have issues the best thing to do is a binary search of possible causes, that is to say, half things, and then half them again. If you can connect two processes over a network then you can rule out a network issue, for example.&lt;/p&gt;

&lt;p&gt;We can do this using netcat, or &lt;code&gt;nc&lt;/code&gt;. It is perhaps one of the most versatile and powerful tools that you can put in your digital toolbox. The concept is simple, on one end you create a listener, and on the other end you create a connector.&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%2Fg6y5p15mj7f30u6t95g5.png" 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%2Fg6y5p15mj7f30u6t95g5.png" alt="Can I hear you now?"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's create a bridge network and two containers to demonstrate: one alpine, one ubuntu. Sometimes &lt;code&gt;nc&lt;/code&gt; is installed (alpine) and sometimes we have to install it (ubuntu) so that's what we'll do.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ docker network create metal-tube

7507bfb685510ae7e93f808cdc2392bedf25b2912cc77abc3801d6c575795d30

--------------------------------------------------------------------

$ docker run --rm -ti --network metal-tube --name jeff alpine

jeff#

--------------------------------------------------------------------

$ docker run --rm -ti --network metal-tube --name alan ubuntu

alan# apt update -qq &amp;amp;&amp;amp; apt install netcat -qqy

alan#


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

&lt;/div&gt;

&lt;p&gt;Okay, we're ready. So we have two containers on the same network, which we've called &lt;code&gt;metal-tube&lt;/code&gt;. We've given them the names &lt;code&gt;jeff&lt;/code&gt; and &lt;code&gt;alan&lt;/code&gt;. I've changed the terminal prompts to &lt;code&gt;jeff#&lt;/code&gt; and &lt;code&gt;alan#&lt;/code&gt; to make this easier to read.&lt;/p&gt;

&lt;p&gt;I've previously talked about &lt;a href="https://docs.docker.com/network/bridge/" rel="noopener noreferrer"&gt;user defined bridge networks&lt;/a&gt; in my post &lt;a href="https://dev.to/adamkdean/automatic-ssl-with-let-s-encrypt-nginx-4nfk"&gt;Automatic SSL with Let's Encrypt &amp;amp; Nginx&lt;/a&gt;, but the main thing to know is that they have built in DNS servers that allow you to resolve containers by their names. Don't believe me? Let's ping Alan from Jeff.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

jeff# ping alan

PING alan (172.21.0.3): 56 data bytes
64 bytes from 172.21.0.3: seq=0 ttl=64 time=0.247 ms


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

&lt;/div&gt;

&lt;p&gt;So now, let's see if we can connect from Alan to Jeff. For this, we first setup a listener on Alan with netcat using the &lt;code&gt;-l&lt;/code&gt; listen flag, the &lt;code&gt;-p&lt;/code&gt; local port flag, and, so that we know what's going on, the &lt;code&gt;-vv&lt;/code&gt; very verbose flag.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

alan# nc -vvlp 9000

listening on [any] 9000 ...


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

&lt;/div&gt;

&lt;p&gt;Now this will hang until it receives data. Over on Jeff, we can attempt to send some data via netcat. The way we'll do this is to echo some data into the netcat process via a &lt;code&gt;|&lt;/code&gt; pipe. We'll be using the &lt;code&gt;-vv&lt;/code&gt; very verbose flag again.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

jeff# echo "hello" | nc -vv alan 9000

alan (172.21.0.3:9000) open
sent 6, rcvd 0


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

&lt;/div&gt;

&lt;p&gt;Great, it seems to have worked. If it didn't, we may have have seen something like &lt;code&gt;nc: alan (172.21.0.3:9000): Connection refused&lt;/code&gt;. Run the command again and you'll see. Once a connection to a netcat listener is closed, the listener process exits. But let's look at Alan now.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

alan# nc -vvlp 9000

listening on [any] 9000 ...
connect to [172.21.0.3] from jeff.metal-tube [172.21.0.2] 39993
hello
 sent 0, rcvd 6


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

&lt;/div&gt;

&lt;p&gt;It worked, therefore we know that TCP connections between these two containers are working fine. Now, locally, they're going to work fine, but sometimes you may have services on different physical servers, and this is often a great way to test connectivity between machines. You don't always need to setup a netcat listener either, you can use netcat on it's own to connect to a service to check that it's up and accepting connections. That's super useful too.&lt;/p&gt;

&lt;p&gt;There are a number of other tricks you can use to debug containers, such as overriding entry points, mapping volumes to keep an eye on the filesystem, and more, but the above are the ones I use 95% of the time. If you have some cool tricks, why not share them in the comments below?&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%2Fo91k35utconxya67a2hq.png" 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%2Fo91k35utconxya67a2hq.png" alt="T..h..a..n..k....y..o..u....f..o..r..r..e..a..d..i..n..g"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>debugging</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Automatic SSL with Let's Encrypt &amp; Nginx</title>
      <dc:creator>Adam K Dean</dc:creator>
      <pubDate>Thu, 13 Feb 2020 14:34:11 +0000</pubDate>
      <link>https://dev.to/adamkdean/automatic-ssl-with-let-s-encrypt-nginx-4nfk</link>
      <guid>https://dev.to/adamkdean/automatic-ssl-with-let-s-encrypt-nginx-4nfk</guid>
      <description>&lt;p&gt;&lt;em&gt;See update summary at bottom of post for changelog.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Note: December 2020 saw the release of v2 of the &lt;code&gt;letsencrypt-nginx-proxy-companion&lt;/code&gt; project. I've updated this article to reflect that but will leave the old v1 code in the footer. If you need to upgrade your existing machines in situ, please refer to the &lt;a href="https://github.com/nginx-proxy/docker-letsencrypt-nginx-proxy-companion"&gt;nginx-proxy/docker-letsencrypt-nginx-proxy-companion&lt;/a&gt; repository.&lt;/p&gt;




&lt;p&gt;Since 2016, certificate authority Let's Encrypt have offered free SSL/TLS certificates in a bid to make encrypted communications on the web ubiquitous. If you've ever bought a certificate, you'll know they're usually quite expensive, the process for verifying them is a pain in the gluteus maximus, and then they expire while you're on holiday causing an outage.&lt;/p&gt;

&lt;p&gt;With Let's Encrypt, all of these problems fade away, thanks to the Automated Certificate Management Environment (ACME) protocol that enables you to automate of the verification and deployment of certificates, saving you money and time. ACME is an interesting topic in it's own right, and you can read more about the various verification methods (called challenges) &lt;a href="https://letsencrypt.org/docs/challenge-types/"&gt;here&lt;/a&gt;, but today I'm going to show you how to easily setup a reverse proxy with automagical certificate generation, verification, and deployment.&lt;/p&gt;

&lt;p&gt;The first thing we're going to do is create a &lt;a href="https://docs.docker.com/network/bridge/"&gt;user defined bridge network&lt;/a&gt; called &lt;code&gt;service-network&lt;/code&gt;. I'm bad at naming things but this seems applicable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker network create --subnet 10.10.0.0/24 service-network
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgr0zmlvqurzgisagb1pq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgr0zmlvqurzgisagb1pq.png" alt="Fig A" width="800" height="653"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we'll setup a reverse proxy. A reverse proxy simply accepts requests and proxies them onto another service based on routing rules such as which hostnames should go to which service containers. I imagine it looks something a little bit like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fc2200do7rwus153u181q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fc2200do7rwus153u181q.png" alt="Reverse Proxy (as I imagine it)" width="700" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We'll use an image by Jason Wilder, &lt;a href="https://github.com/node-proxy/nginx-proxy"&gt;jwilder/nginx-proxy&lt;/a&gt;. This is a well maintained project with a lot of documentation. We map the ports 80 and 443 into the container so that we can handle both HTTP and HTTPS connections. We have a number of volumes too, three are standard docker volumes, and one is the docker daemon UNIX socket.&lt;/p&gt;

&lt;p&gt;You might notice that I often use long-form arguments in scripts. While we could easily save space and have this on one line, it doesn't help others who may have to maintain scripts in the future.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run \
  --detach \
  --restart always \
  --publish 80:80 \
  --publish 443:443 \
  --name nginx-proxy \
  --network service-network \
  --volume /var/run/docker.sock:/tmp/docker.sock:ro \
  --volume nginx-certs:/etc/nginx/certs \
  --volume nginx-vhost:/etc/nginx/vhost.d \
  --volume nginx-html:/usr/share/nginx/html \
  jwilder/nginx-proxy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxmt3x6ztifhwyun8ok1q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxmt3x6ztifhwyun8ok1q.png" alt="Fig B" width="800" height="653"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ok, so now we have our reverse proxy, next we need to setup the Let's Encrypt companion, for which we'll be using Yves Blusseau's image &lt;a href="https://github.com/node-proxy/docker-letsencrypt-nginx-proxy-companion"&gt;jrcs/letsencrypt-nginx-proxy-companion&lt;/a&gt;. I've been using this flawlessly now for almost a year.&lt;/p&gt;

&lt;p&gt;We map the same volumes to this container, though no ports are published. We set the &lt;code&gt;NGINX_PROXY_CONTAINER&lt;/code&gt; environment variable to match the name of our proxy container, and that's about it. This container will run a process whenever new service containers are detected, generating certificates and keeping them up to date.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run \
  --detach \
  --restart always \
  --name nginx-proxy-letsencrypt \
  --network service-network \
  --volumes-from nginx-proxy \
  --volume /var/run/docker.sock:/var/run/docker.sock:ro \
  --volume /etc/acme.sh \
  --env "DEFAULT_EMAIL=mail@yourdomain.tld" \
  jrcs/letsencrypt-nginx-proxy-companion
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcu7e9v9gmuwdrilgo50r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcu7e9v9gmuwdrilgo50r.png" alt="Fig C" width="800" height="653"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, it's time to add a service container. We'll use the &lt;code&gt;tutum/hello-world&lt;/code&gt; image, quite a popular one for testing. For this we're going to need a hostname with the DNS configured. We'll pretend to use &lt;code&gt;hello-world.example.com&lt;/code&gt; and pretend that we've setup an &lt;code&gt;A&lt;/code&gt; record pointing to the IP address of this server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run \
  --detach \
  --restart always \
  --name hello-world \
  --network service-network \
  --env VIRTUAL_HOST=hello-world.example.com \
  --env LETSENCRYPT_HOST=hello-world.example.com \
  --env LETSENCRYPT_EMAIL="youremail@example.com" \
  tutum/hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftexet0ds25wdv3er9vxy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftexet0ds25wdv3er9vxy.png" alt="Fig D" width="800" height="653"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So let's take a look at what's going on here. We set up a proxy container which listens on ports 80 and 443. We then set up an ACME certificate container which will manage certs for us. Then we created a hello world container. We set the environment variable &lt;code&gt;VIRTUAL_HOST&lt;/code&gt; to our hostname, and this is picked up by the proxy in order to route requests through to this container. The requests are routed through our user defined bridge &lt;code&gt;service-network&lt;/code&gt;. We also set the environment variables &lt;code&gt;LETSENCRYPT_HOST&lt;/code&gt; and &lt;code&gt;LETSENCRYPT_EMAIL&lt;/code&gt; which are picked up by the ACME container and used when acquiring the certificate. FYI, the two hostnames will always have to match (&lt;code&gt;VIRTUAL_HOST&lt;/code&gt; and &lt;code&gt;LETSENCRYPT_HOST&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;All we have to do is add these three variables to a container, and it'll be detected by the proxy and ACME containers and in short order, it'll work.&lt;/p&gt;

&lt;p&gt;Now a few things to note. Port discovery — how does the proxy know which port to use? The hello-world image we use exposes a port in the Dockerfile with &lt;code&gt;EXPOSE 80&lt;/code&gt;. This always takes precedence, so if the image has an exposed port, this will be used. But if your Dockerfile doesn't define an exposed port, or if you perhaps configure the port at runtime with an environment variable, e.g. &lt;code&gt;HTTP_PORT=8000&lt;/code&gt;, then you can use the &lt;code&gt;--expose&lt;/code&gt; argument to let the proxy know which port to use. If there are multiple ports available, you can specify which to use with the environment variable &lt;code&gt;VIRTUAL_PORT&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Just to reiterate, Dockerfile &lt;code&gt;EXPOSE&lt;/code&gt; takes precedence over &lt;code&gt;--expose&lt;/code&gt; argument.&lt;/p&gt;

&lt;p&gt;So now, when you hit &lt;code&gt;https://hello-world.example.com&lt;/code&gt;, the DNS server returns an A record with your public IP, the browser then connects to this IP address on port 443 and in turn, the host routes that connection through to the &lt;code&gt;nginx-proxy&lt;/code&gt; container. This terminates the SSL and proxies the request through to the &lt;code&gt;hello-world&lt;/code&gt; container as a plain HTTP request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmrjiz2gamxqox9fwzvbp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmrjiz2gamxqox9fwzvbp.png" alt="Fig E" width="800" height="653"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, there is another thing we can do. The proxy handles upgrading from HTTP to HTTPS, which is great, but sometimes you want to handle www/apex domain redirects. You can configure this in some domain registrar control panels, but then you end up with a different IP address for your hostname.&lt;/p&gt;

&lt;p&gt;There is a better solution, and this time the image we'll use this time is actually one of mine, &lt;a href="https://github.com/adamkdean/redirect"&gt;adamkdean/redirect&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The way it works is super simple. You simply setup another container with the &lt;code&gt;VIRTUAL_HOST&lt;/code&gt; set to the domain you want &lt;em&gt;to redirect away from&lt;/em&gt; and configure it's destination. Take for example we have &lt;code&gt;example.com&lt;/code&gt; and we want it to always redirect to &lt;code&gt;www.example.com&lt;/code&gt; because we love the www.&lt;/p&gt;

&lt;p&gt;We have our main image like so, bound to &lt;code&gt;www.example.com&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run \
  --detach \
  --restart always \
  --name example-website \
  --network service-network \
  --env VIRTUAL_HOST=www.example.com \
  example/website
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We then create the redirect companion container, bound to &lt;code&gt;example.com&lt;/code&gt;, with the environment variable &lt;code&gt;REDIRECT_LOCATION&lt;/code&gt; being set to our preferred destination and &lt;code&gt;REDIRECT_STATUS_CODE&lt;/code&gt; set as applicable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run \
  --detach \
  --restart always \
  --name example-redirect \
  --network service-network \
  --env VIRTUAL_HOST=example.com \
  --env REDIRECT_LOCATION="http://www.example.com" \
  --env REDIRECT_STATUS_CODE=301 \
  adamkdean/redirect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens now is requests for &lt;code&gt;example.com&lt;/code&gt; hit this redirect container, which responds with &lt;code&gt;REDIRECT_STATUS_CODE&lt;/code&gt; and the &lt;code&gt;REDIRECT_LOCATION&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We can of course use Let's Encrypt with these.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run \
  --detach \
  --restart always \
  --name example-website \
  --network service-network \
  --env VIRTUAL_HOST=www.example.com \
  --env LETSENCRYPT_HOST=www.example.com \
  --env LETSENCRYPT_EMAIL="youremail@example.com" \
  example/website

docker run \
  --detach \
  --restart always \
  --name example-redirect \
  --network service-network \
  --env VIRTUAL_HOST=example.com \
  --env LETSENCRYPT_HOST=example.com \
  --env LETSENCRYPT_EMAIL="youremail@example.com" \
  --env REDIRECT_LOCATION="https://www.example.com" \
  --env REDIRECT_STATUS_CODE=301 \
  adamkdean/redirect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, the initial request for &lt;code&gt;example.com&lt;/code&gt; hits the proxy, which terminates the SSL and proxies it through to the &lt;code&gt;example-redirect&lt;/code&gt; container, which responds with a 301 to &lt;code&gt;www.example.com&lt;/code&gt;. The next request which is now for &lt;code&gt;www.example.com&lt;/code&gt; hits the proxy and is proxied through to the &lt;code&gt;example-website&lt;/code&gt; container, something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0mc7ybhn4qe68d9crym0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0mc7ybhn4qe68d9crym0.png" alt="Fig F" width="800" height="653"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope this helps you. This setup can work for single sites or for a number of sites. I use this in situations where setting up a large container platform is a little bit overkill (kubernetes for a blog is fine, right guys?)&lt;/p&gt;

&lt;p&gt;It's super easy to update and deploy, and having used it for over a year now, it's working great. Thanks for reading.&lt;/p&gt;




&lt;p&gt;Update (Feb 21, 2020): as requested, here is a &lt;code&gt;docker-compose.yml&lt;/code&gt; version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: "3"

services:
  web:
    image: example/website
    expose:
     - 8000
    environment:
      HTTP_PORT: 8000
      VIRTUAL_HOST: www.example.com
      LETSENCRYPT_HOST: www.example.com
      LETSENCRYPT_EMAIL: "example@example.com"
    networks:
      service_network:

  web-redirect:
    image: adamkdean/redirect
    environment:
      VIRTUAL_HOST: example.com
      LETSENCRYPT_HOST: example.com
      LETSENCRYPT_EMAIL: "example@example.com"
      REDIRECT_LOCATION: "https://www.example.com"
    networks:
      service_network:

  nginx-proxy:
    image: jwilder/nginx-proxy
    ports:
      - 80:80
      - 443:443
    container_name: nginx-proxy
    networks:
      service_network:
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - nginx-certs:/etc/nginx/certs
      - nginx-vhost:/etc/nginx/vhost.d
      - nginx-html:/usr/share/nginx/html

  nginx-proxy-letsencrypt:
    image: jrcs/letsencrypt-nginx-proxy-companion
    environment:
      NGINX_PROXY_CONTAINER: "nginx-proxy"
    networks:
      service_network:
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - nginx-certs:/etc/nginx/certs
      - nginx-vhost:/etc/nginx/vhost.d
      - nginx-html:/usr/share/nginx/html

networks:
  service_network:

volumes:
  nginx-certs:
  nginx-vhost:
  nginx-html:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Update (Feb 28, 2020): &lt;/p&gt;

&lt;p&gt;I realised that I made a mistake when talking about &lt;code&gt;VIRTUAL_PORT&lt;/code&gt;. I've updated the document but just to clarify, you tell the proxy which port to use with the &lt;code&gt;EXPOSE&lt;/code&gt; command in a Dockerfile and the &lt;code&gt;--expose&lt;/code&gt; argument on the Docker command line interface. If there are multiple ports exposed, you can then use the &lt;code&gt;VIRTUAL_PORT&lt;/code&gt; environment variable to signal &lt;em&gt;which&lt;/em&gt; exposed port to use.&lt;/p&gt;

&lt;p&gt;Sorry about the mix up!&lt;/p&gt;




&lt;p&gt;Update (Apr 22, 2020): &lt;/p&gt;

&lt;p&gt;Added some missing backslashes to the secure redirect snippets. Oops!&lt;/p&gt;




&lt;p&gt;Update (Dec 11, 2020):&lt;/p&gt;

&lt;p&gt;The v1 code for letsencrypt-nginx-proxy-companion.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run \
  --detach \
  --restart always \
  --name nginx-proxy-letsencrypt \
  --network service-network \
  --volume /var/run/docker.sock:/var/run/docker.sock:ro \
  --volume nginx-certs:/etc/nginx/certs \
  --volume nginx-vhost:/etc/nginx/vhost.d \
  --volume nginx-html:/usr/share/nginx/html \
  --env NGINX_PROXY_CONTAINER="nginx-proxy" \
  jrcs/letsencrypt-nginx-proxy-companion:v1.13.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;If you enjoyed this article, you might enjoy my next one &lt;a href="https://dev.to/adamkdean/debugging-docker-containers-17fh"&gt;Debugging Docker Containers&lt;/a&gt;, where we delve into a few ways that you can work out just what is going on inside these black boxes we call containers.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A note on injecting docker.sock: the docker daemon UNIX socket (/var/run/docker.sock) gives access to docker, which in other words, is root access. Be sure you know what you're giving this too whenever you're running third party images.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>security</category>
      <category>architecture</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>SPI with MCP3008 ADC chip &amp; TMP36 analogue temp sensor</title>
      <dc:creator>Adam K Dean</dc:creator>
      <pubDate>Tue, 04 Feb 2020 12:16:20 +0000</pubDate>
      <link>https://dev.to/adamkdean/spi-with-mcp3008-adc-chip-tmp36-analogue-temp-sensor-b3c</link>
      <guid>https://dev.to/adamkdean/spi-with-mcp3008-adc-chip-tmp36-analogue-temp-sensor-b3c</guid>
      <description>&lt;p&gt;I wrote this a while ago (on 15 Dec 2014, according to GitHub) but for some reason I never wrote about it or shared it really. It's a solution for using SPI with the &lt;a href="https://cdn-shop.adafruit.com/datasheets/MCP3008.pdf"&gt;MCP3008 ADC chip&lt;/a&gt; &amp;amp; a &lt;a href="https://www.analog.com/media/en/technical-documentation/data-sheets/TMP35_36_37.pdf"&gt;TMP36 analogue temperature sensor&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I wanted to find a way to use analogue readings (such as from temperature sensors, light dependent resistors etc) but digitally, from within a node application for example. Specifically, I wanted to cut out the Arduino step that most of these projects tend to take.&lt;/p&gt;

&lt;p&gt;That's where the MCP3008 ADC chip comes in. The MCP3008 is an analogue–serial convert with an SPI or &lt;a href="https://en.wikipedia.org/wiki/Serial_Peripheral_Interface"&gt;Serial Peripheral Interface&lt;/a&gt;. The code below shows how you'd make use of this with node.&lt;/p&gt;

&lt;p&gt;Unfortunately, I can't locate a photo of the circuit but from what I recall, it was fairly straight forward. The code has this note:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You are required to have an SPI-able machine, such as a Raspberry Pi. For this test, I have two TMP36 chips wired up to feed data into CH0 and CH1 of a MCP3008 ADC chip.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I think programming is fun, but I find the intangibility of the things we create frustrating at times. For 20 years now, I've mostly authored digital creations, but I think the knowledge we have as software engineers can so easily be meshed with the physical world. That's why I'm sharing this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use strict";

var util = require('util'),
    fs = require('fs'),
    SPI = require('spi');

var device = '/dev/spidev0.0',
    spi;

if (!fs.existsSync(device)) {
    throw 'Error, SPI is not activated';
}

function read(channel, callback) {
    if (spi === undefined) return;

    // to select the channel, we need to compute a mode (4 bits)
    // a mode consists of a single/diff bit and three selection bits (d2, d1, d0)
    // if we want the input configuration to be single-ended, we use 1, for differential, use 0
    // for the channel, if we add that to 8 (which is 0000 1000), we should get the right value
    // ch0 = 1000, ch1 = 1001, ch2 = 1010, ch3 = 1011
    // ch4 = 1100, ch5 = 1101, ch6 = 1110, ch7 = 1111
    // now we need to pad this with 4 bits, to give us a byte:
    // ch0 = 1000 &amp;lt;&amp;lt; 4 = 1000 0000
    var mode = (8 + channel) &amp;lt;&amp;lt; 4;

    var tx = new Buffer([1, mode, 0]);
    var rx = new Buffer([0, 0, 0]);

    spi.transfer(tx, rx, function(dev, buffer) {
        // logic explained:

        // the buffer will hold 3 8-bit bytes (24 bits) but we only want the last 10 bits
        // this is the last byte and the last 2 bits from the second byte, we ignore the first byte

        // |   0   | |   1   | |   2   |
        // 0000 0000 0000 0000 0000 0000
        //                 ^^^ ^^^^ ^^^^

        // step 1.
        // we take the second byte and bitwise AND it with 3 (0000 0011) to extract the last two bits
        //   1010 0010 (162)  let's say the byte has some junk data and then two at the end
        // &amp;amp; 0000 0011 (3)    we and it with three
        // = 0000 0010 (2)    and we get the value two

        // step 2.
        // we now want to shift these bits 8 to the left to make space for the third byte
        // byte 1 = 0000 0010 (2)  &amp;lt;- 8 = 10 0000 0000 (512)
        // byte 2 = 0000 1111 (15)           | space |

        // step 3.
        // we can now add them together to get two bytes equaling our value:
        // 0000 0010 0000 1111 (527)

        var value = ((buffer[1] &amp;amp; 3) &amp;lt;&amp;lt; 8) + buffer[2];
        callback(value);
    })
}

function tmp36_temp(value) {
    // approx 25 C = 750 mV (0.75 V)
    // 25 / 0.75 = 33.33 C/V
    // I think? http://www.analog.com/static/imported-files/data_sheets/TMP35_36_37.pdf

    var volts = (value * 3.3) / 1023;
    var temp = volts * 33.333;
    return temp.toFixed(2);
}

function get_volts(value) {
    var volts = (value * 3.3) / 1023;
    return volts.toFixed(2);
}

spi = new SPI.Spi(device, [], function(s) {
    s.open();
});

// read from ch0
read(0, function(value) {
    console.log('Sensor 1 is %s C (%s - %s v)', tmp36_temp(value), value, get_volts(value));
})

// read from ch1
read(1, function(value) {
    console.log('Sensor 2 is %s C (%s - %s v)', tmp36_temp(value), value, get_volts(value));
})

spi.close();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>electronics</category>
      <category>node</category>
      <category>spi</category>
    </item>
    <item>
      <title>Blog migration complete</title>
      <dc:creator>Adam K Dean</dc:creator>
      <pubDate>Mon, 03 Feb 2020 18:17:53 +0000</pubDate>
      <link>https://dev.to/adamkdean/blog-migration-complete-5e8n</link>
      <guid>https://dev.to/adamkdean/blog-migration-complete-5e8n</guid>
      <description>&lt;p&gt;Since I first discovered dev.to as a viable blogging platform (or I as I like to think of it, MySpace for Programmers), I've been &lt;a href="https://dev.to/adamkdean/migrating-to-dev-to-190"&gt;migrating my blog posts&lt;/a&gt;. Initially, and until today, that looked like this. &lt;/p&gt;

&lt;p&gt;First, I take an old blog post, such as this one &lt;a href="https://dev.to/adamkdean/melatonin-busting-glasses-3mdc"&gt;Melatonin Busting Glasses&lt;/a&gt;. I'd copy the markdown into this editor, set the title and tags, add an image, and then remove the frontmatter (a YAML header often used for metadata) and save the post.&lt;/p&gt;

&lt;p&gt;Each day, I'd do a few, finding new header images, and each time adding a little note saying "Originally posted on x, 2013" etc, with a link to a post explaining it. A lot of the posts were outdated, but, I keep them with me as I move from blog to blog, ten years of writing.&lt;/p&gt;

&lt;p&gt;Then, tonight, I forgot to remove the frontmatter from a blog post, and WHAM! the blog post had the date set as 2013. Not only did I find a way to set the date to the original posting date, but I could also make my life easier by creating a generic cover image for every post!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fy9hxcwi5y92j6zfcxbjx.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fy9hxcwi5y92j6zfcxbjx.jpg" alt="Archived post" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, for the last hour or so, I've just been copy pasting markdown files with the cover image added, one by one, into the write post. I was worried I was spamming the feed but I think actually that's not the case.&lt;/p&gt;

&lt;p&gt;For the few posts which I already posted, I can't seem to now change the date of those, so I'll leave them as they are as they have comments. But, it's fair to say now, that at 191 posts and what felt like 74 hours of copy pasting manually, I've now migrated my blog to dev.to!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/k4vY4FcPuqKWc/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/k4vY4FcPuqKWc/giphy.gif" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

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