<?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>Forem: lukeojones</title>
    <description>The latest articles on Forem by lukeojones (@lukeojones).</description>
    <link>https://forem.com/lukeojones</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%2F387738%2Fb989d2d1-08d6-4f64-b0ad-a24979fd7d4f.jpg</url>
      <title>Forem: lukeojones</title>
      <link>https://forem.com/lukeojones</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/lukeojones"/>
    <language>en</language>
    <item>
      <title>1UP your Zsh abilities by autoloading your own functions</title>
      <dc:creator>lukeojones</dc:creator>
      <pubDate>Sun, 24 May 2020 12:15:59 +0000</pubDate>
      <link>https://forem.com/lukeojones/1up-your-zsh-abilities-by-autoloading-your-own-functions-2ngp</link>
      <guid>https://forem.com/lukeojones/1up-your-zsh-abilities-by-autoloading-your-own-functions-2ngp</guid>
      <description>&lt;p&gt;Zsh is quickly becoming the shell of choice for software developers and, if you want to maximise your productivity at the CLI, you should know how to create and autoload your own Zsh functions.&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%2Fcdn-images-1.medium.com%2Fmax%2F11520%2F0%2AMcipBU_CqmKDJf7V" 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%2Fcdn-images-1.medium.com%2Fmax%2F11520%2F0%2AMcipBU_CqmKDJf7V" alt="Photo by [Markus Spiske](https://unsplash.com/@markusspiske?utm_source=medium&amp;amp;utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even novice CLI users will have added the occasional &lt;em&gt;alias&lt;/em&gt; in order to avoid repeatedly typing out a verbose command. Whilst aliases are great, they aren’t well suited to the more complicated, logical code snippets that you may want to run on a regular basis.&lt;/p&gt;

&lt;p&gt;In this whistle-stop tour, I’ll show you how to create basic Zsh functions and integrate them with Zsh’s autoloading system so they’re available during interactive shell sessions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating our function
&lt;/h3&gt;

&lt;p&gt;After reading this article, I’m sure you’ll be inspired to write lots and lots of functions but, before you get carried away, it’s good practice to create a directory to store them all. I usually create a hidden subdirectory in my home directory called &lt;em&gt;.zshfn&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;By convention, each function is stored in a file with the same name; for the purposes of keeping this guide snappy, I’m going to create a very simple &lt;em&gt;clock&lt;/em&gt; function that just displays the current time and so I’ll create a file called &lt;em&gt;clock&lt;/em&gt; (no file extension) to put my function code in.&lt;/p&gt;

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

&lt;span class="nb"&gt;mkdir&lt;/span&gt; ~/.zshfn
&lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="nv"&gt;$_&lt;/span&gt;/clock


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

&lt;/div&gt;

&lt;p&gt;Now edit the &lt;em&gt;clock&lt;/em&gt; file and add the following snippet:&lt;/p&gt;

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

&lt;span class="c"&gt;# Initialisation Code&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Initialising Clock"&lt;/span&gt;

&lt;span class="c"&gt;# Display simple clock every second&lt;/span&gt;
clock &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=========="&lt;/span&gt;
    &lt;span class="nb"&gt;date&lt;/span&gt; +&lt;span class="s2"&gt;"%r"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=========="&lt;/span&gt;
    &lt;span class="nb"&gt;sleep &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt;
    clear&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;done&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Clear the three lines from the terminal&lt;/span&gt;
clear&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;1..3&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;do
      &lt;/span&gt;tput cuu1
      tput el
  &lt;span class="k"&gt;done&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

clock


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

&lt;/div&gt;

&lt;p&gt;We’ll come back to the function later — for now just save the &lt;em&gt;clock&lt;/em&gt; file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring our &lt;em&gt;.zshrc&lt;/em&gt; file
&lt;/h3&gt;

&lt;p&gt;In order to make our function accessible whenever we startup a new terminal, we need to make a couple of modifications to the &lt;em&gt;.zshrc&lt;/em&gt; file.&lt;/p&gt;

&lt;p&gt;First we need to ensure we add our &lt;em&gt;.zshfn&lt;/em&gt; function directory to the Zsh &lt;em&gt;file search path&lt;/em&gt; (a.k.a &lt;em&gt;fpath&lt;/em&gt;) so that Zsh knows it should look in our new directory for functions. As you might expect, the &lt;em&gt;fpath&lt;/em&gt; is traversed from left-to-right like other unix path variables.&lt;/p&gt;

&lt;p&gt;Secondly, we need to tell Zsh about the &lt;em&gt;clock&lt;/em&gt; function we just created — this is achieved via the &lt;em&gt;autoload&lt;/em&gt; command. The &lt;em&gt;autoload&lt;/em&gt; command essentially instructs Zsh to register a function (called &lt;em&gt;clock&lt;/em&gt;) using the contents of the &lt;em&gt;clock&lt;/em&gt; file that is located in a directory somewhere along the &lt;em&gt;fpath&lt;/em&gt;.&lt;/p&gt;

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

&lt;span class="c"&gt;# Bottom of .zshrc file&lt;/span&gt;
&lt;span class="nv"&gt;fpath&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt; ~/.zshfn &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;fpath&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;)&lt;/span&gt;
autoload &lt;span class="nt"&gt;-Uz&lt;/span&gt; clock


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

&lt;/div&gt;

&lt;p&gt;As you add more functions, you’ll find it becomes cumbersome to append each one to the end of the autoload command and so (if you want to be super cool) you can choose to autoload all files in your &lt;em&gt;.zshfn&lt;/em&gt; folder using the following alternative:&lt;/p&gt;

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

&lt;span class="c"&gt;# Bottom of .zshrc file&lt;/span&gt;
&lt;span class="nv"&gt;fpath&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt; ~/.zshfn &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;fpath&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;)&lt;/span&gt;
autoload &lt;span class="nt"&gt;-Uz&lt;/span&gt; &lt;span class="nv"&gt;$fpath&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;1]/&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;.:t&lt;span class="o"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;If you’re wondering what the &lt;em&gt;-Uz&lt;/em&gt; options do, they simply ensure that autoloading will do the right thing regardless of what other options you may have configured (i.e. enforces disabled alias-expansion and zsh style autoloading).&lt;/p&gt;

&lt;h3&gt;
  
  
  Running our function
&lt;/h3&gt;

&lt;p&gt;After saving our &lt;em&gt;clock&lt;/em&gt; function and the &lt;em&gt;.zshrc&lt;/em&gt; file we are now ready to test the function out. In your terminal begin typing &lt;em&gt;clock&lt;/em&gt; — you should be able to tab-complete the function and if you have syntax highlighting turned on you should see that kick in too.&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%2Fcdn-images-1.medium.com%2Fmax%2F2272%2F1%2AVZAZMtDZzEaZjgRDm7Swng.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%2Fcdn-images-1.medium.com%2Fmax%2F2272%2F1%2AVZAZMtDZzEaZjgRDm7Swng.png" alt="Running like clockwork"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hit return and you should see the following output:&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%2Fcdn-images-1.medium.com%2Fmax%2F2272%2F1%2AMpYbbPFyYUFZFgm790DG0Q.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%2Fcdn-images-1.medium.com%2Fmax%2F2272%2F1%2AMpYbbPFyYUFZFgm790DG0Q.png" alt="Running the clock file for the first time"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Remember that as far as Zsh is concerned, before running the &lt;em&gt;clock&lt;/em&gt; function, the &lt;strong&gt;entire contents of the &lt;em&gt;clock&lt;/em&gt; file are used as the function definition&lt;/strong&gt;. This is why the first time you run &lt;em&gt;clock&lt;/em&gt;, you see the initialisation code being executed.&lt;/p&gt;

&lt;p&gt;Kill the function (&lt;em&gt;Ctrl-c&lt;/em&gt;) and run the function again — you should see the following output the second time around:&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%2Fcdn-images-1.medium.com%2Fmax%2F2272%2F1%2AqYxaqvwXgHRJDpH8fhx-9Q.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%2Fcdn-images-1.medium.com%2Fmax%2F2272%2F1%2AqYxaqvwXgHRJDpH8fhx-9Q.png" alt="Second execution uses redefined function"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that in the second execution, &lt;strong&gt;the initialisation code does not run&lt;/strong&gt;— this is because the first time we ran the function, the &lt;em&gt;clock&lt;/em&gt; function was redefined to use the actual &lt;em&gt;clock()&lt;/em&gt; function defined in our file. This can be a really nice pattern to use if you have some sort of one-off initialisation you need to accomplish.&lt;/p&gt;

&lt;p&gt;At this point you might be wondering why the &lt;em&gt;clock()&lt;/em&gt; method is even needed? Well actually, it isn’t. You could replace the &lt;em&gt;clock&lt;/em&gt; file with the following contents and it would work just fine.&lt;/p&gt;

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

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Initialising Clock"&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=========="&lt;/span&gt;
  &lt;span class="nb"&gt;date&lt;/span&gt; +&lt;span class="s2"&gt;"%r"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=========="&lt;/span&gt;
  &lt;span class="nb"&gt;sleep &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;1..3&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;do
      &lt;/span&gt;tput cuu1
      tput el
  &lt;span class="k"&gt;done
done&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;If you now execute this version of the &lt;em&gt;clock&lt;/em&gt; function twice in a row you’ll see that, because the &lt;em&gt;clock&lt;/em&gt; function is not redefined, the initialisation code runs both times.&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%2Fcdn-images-1.medium.com%2Fmax%2F2272%2F1%2AlzEwDshZwcY-RH0rO9cEhw.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%2Fcdn-images-1.medium.com%2Fmax%2F2272%2F1%2AlzEwDshZwcY-RH0rO9cEhw.png" alt="Repeated executions all run the initialisation code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Which approach you use is largely down to your use-case and stylistic preferences. I tend to prefer the first option (i.e. encapsulate my &lt;em&gt;clock&lt;/em&gt; code in a &lt;em&gt;clock()&lt;/em&gt; function block) but in many cases it is completely superfluous.&lt;/p&gt;

&lt;p&gt;I hope you learned something from this quick write-up — there is a lot more to Zsh functions than covered here and if you want more detailed information, I’d recommend you check out &lt;a href="http://zsh.sourceforge.net/Doc/Release/Functions.html" rel="noopener noreferrer"&gt;this resource&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In any case, functions are a great way to optimise your CLI experience and it’s certainly worthwhile knowing how to use them 😎👍&lt;/p&gt;

</description>
      <category>bash</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Organisational tools to help make the most of your dev side-projects</title>
      <dc:creator>lukeojones</dc:creator>
      <pubDate>Mon, 18 May 2020 17:46:54 +0000</pubDate>
      <link>https://forem.com/lukeojones/productivity-hacks-to-help-you-focus-on-your-side-projects-and-get-stuff-done-2i27</link>
      <guid>https://forem.com/lukeojones/productivity-hacks-to-help-you-focus-on-your-side-projects-and-get-stuff-done-2i27</guid>
      <description>&lt;p&gt;Recently, as part of a migration to Github, I decided to rid my Gitlab account of old repositories. Before starting on that task, if you had asked me how many stale repositories I had in there, I would have said no more than five — I removed over 20!&lt;/p&gt;

&lt;p&gt;Working through this list and deleting old projects was a nostalgic walk down memory lane but, as I scanned each project, the pattern that started to emerge was that most of these projects had started out with a real buzz, dedication and enthusiasm only to be abandoned a few weeks (or even days) later in favour of another one (now also deleted).&lt;/p&gt;

&lt;p&gt;By the end of this spring cleaning operation, the walk down memory lane had transformed into a depressing jaunt through an abandoned museum — there were some interesting exhibits but no-one was around any more to tell you how they worked or what they were used for. I had little idea about the state of the project and how &lt;em&gt;incomplete&lt;/em&gt; it actually was.&lt;/p&gt;

&lt;p&gt;Of course the answer to all this was obvious — I needed another new side project! (Dark Kermit strikes again) At this point it was more than clear that I would need to change my approach and ensure I avoided another half-baked project clogging up my Github account.&lt;/p&gt;

&lt;h4&gt;
  
  
  Break it down
&lt;/h4&gt;

&lt;p&gt;Side projects should be fun, challenging and rewarding but sometimes it’s difficult to distribute those factors over the lifetime of the work.&lt;/p&gt;

&lt;p&gt;In particular, when you first devise your new idea, it’s especially fun and rewarding because you’re getting to investigate all the new tools and concepts that might be relevant to the implementation.&lt;/p&gt;

&lt;p&gt;After that initial stage, it can often be hard to stay motivated as you start to realise the scale of the project and realise those new tools aren’t as super simple to use as you expected 😫&lt;/p&gt;

&lt;p&gt;For me, the best way to deal with this is to break the workload down into small (and hopefully enjoyable) subtasks. We do this in our 9–5 jobs all the time using tools like &lt;em&gt;Jira&lt;/em&gt; and &lt;em&gt;Youtrack&lt;/em&gt; (OK, maybe it’s not always &lt;em&gt;enjoyable&lt;/em&gt;) but it’s easily overlooked when you’re just working on something in your free time and there is no client or customer chasing you for the goods.&lt;/p&gt;

&lt;p&gt;Breaking down these ‘awesome ideas’ into smaller tasks has a number of small benefits that compound over time to give you a better result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mental Offloading:&lt;/strong&gt; You don’t need to keep everything in your head since it’s been transferred into pen/pencil/pixels.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Progress:&lt;/strong&gt; It’s easy to see how much effort is required to hit the next milestone. If you’re just working on one big, nebulous concept it’s hard to keep perspective. It’s also easier to resume working on the project after a small break.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;On-boarding:&lt;/strong&gt; If you decide to team up with mates or colleagues, you’ve got an instant starting point to discuss the remaining work and what has been done to date.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Code Context:&lt;/strong&gt; Although your code should be the canonical documentation of your project, having a proper space for documentation or issue tracking can be really useful especially when looking back.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Rewarding:&lt;/strong&gt; Completing tasks is rewarding and after finishing the first few tasks, you start to form positive habits that will keep you on track — just ask Pavlov’s 🐶.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this in mind, I’m going to quickly describe how I’ve used some free tools to finally get a little side-project off the ground — &lt;a href="https://response.dev"&gt;response.dev&lt;/a&gt; — but you should use whatever works for you.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Trello board
&lt;/h4&gt;

&lt;p&gt;I certainly didn’t want to make my side-project feel exactly like work — where’s the fun in that? I just wanted a lightweight alternative to Jira that provided:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The ability to document tasks quickly and move them between different states without too much config or admin.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A concise way of referring to tasks e.g. a numeric identifier&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A way of linking issues to commits, PRs, branches etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Everything I needed for free! 💸&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Luckily, a tool I’ve used for a number of years — Trello — can be configured to meet all the criteria above. I’ve documented some of the key features I’m using right now but the setup is quite fluid and because this is just for side-project work, I don’t have to get buy-in to change anything 😁&lt;/p&gt;

&lt;h4&gt;
  
  
  Columns
&lt;/h4&gt;

&lt;p&gt;I’ve experimented with various column setups but this seems to be working quite nicely although YMMV.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Todo:&lt;/strong&gt; Things that need implementing&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Doing:&lt;/strong&gt; What I’m working on currently. This helps keep me focused so I don’t juggle too many things at once.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Done&lt;/strong&gt;: Moving things to &lt;em&gt;Done&lt;/em&gt; provides some positive feedback and builds a feeling of progress and momentum. It also serves as a knowledge base for previous tasks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Nice to Have:&lt;/strong&gt; Things I’d like to implement but aren’t essential.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;To Learn:&lt;/strong&gt; The whole reason I like side-projects is that it gives me a chance to learn and pick up new skills. Sometimes, the list of things I’d like to learn is overwhelming so I add them here and revisit later when I’m commuting or waiting for the kettle to boil etc.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GEp9lXqi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/5732/1%2AgGFs-PIFV_n5NWydrnNLIw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GEp9lXqi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/5732/1%2AgGFs-PIFV_n5NWydrnNLIw.png" alt="Five columns just happens to fit nicely on my MBP screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Issue Identifiers
&lt;/h4&gt;

&lt;p&gt;Trello uses Card IDs under the hood but they aren’t visible by default so you only see the description. I like to have an ID so that I can refer to it more easily in conversation, other issues, PRs or commit messages etc.&lt;/p&gt;

&lt;p&gt;You can install &lt;a href="https://chrome.google.com/webstore/detail/trello-card-numbers/kadpkdielickimifpinkknemjdipghaf?hl=en"&gt;this Chrome extension&lt;/a&gt; to expose the Card IDs without using up a valuable Power-up. If you are using the paid version of Trello and don’t need to worry about Power-up allowance, then I’d recommend using &lt;a href="https://trello.com/power-ups/59c3d177178a761767b49278/card-numbers-by-reenhanced"&gt;this power-up from Reenhanced&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4l_p-Bi5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AxZuKzQ4L0CjnlaPnxEf6dQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4l_p-Bi5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AxZuKzQ4L0CjnlaPnxEf6dQ.png" alt="After some research I found out it was “Joff-tchoff-tchoffo-tchoffo-tchoff!”"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Github Integration
&lt;/h4&gt;

&lt;p&gt;As a developer working with Trello regularly, it is really worth looking into this &lt;a href="https://trello.com/power-ups/55a5d916446f517774210004/github"&gt;fantastic Github Power-up&lt;/a&gt; so that you can easily link your Trello cards to your code.&lt;/p&gt;

&lt;p&gt;If you’re using the unpaid version of Trello then this is a great way to make the most of your Power-up allowance (just make sure you use the Chrome extension to display the Card IDs in the previous section).&lt;/p&gt;

&lt;p&gt;Once you’ve enabled the Power-up, you just need to authenticate Trello using your Github credentials and you’ll be able to add branches, PRs and commits directly to your Cards as shown here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AZjTE1Ly--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2ABEreCBM3BytxNbBGOSjaOg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AZjTE1Ly--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2ABEreCBM3BytxNbBGOSjaOg.png" alt="Github data being pulled straight into Trello makes your life way easier."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I generally attach the PR once the code has been written but will also attach specific commits in some cases e.g. when identifying the commit which introduced a bug.&lt;/p&gt;

&lt;p&gt;At the board level, it’s really nice to be able to see which issues have been merged in and you can even use badges to show whether certain checks have been passed e.g. Tests, Linting etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FVgX3cTk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2Asnfl3yD6uRbGezdmlKChfw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FVgX3cTk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2Asnfl3yD6uRbGezdmlKChfw.png" alt="Board level view shows basic Github info"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The association is two-way too so you can also see a link to the relevant Trello Cards from within Github — a huge timesaver.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hRVvdHqx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AKlGmgdHsDZH9d6lOE-Uwcw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hRVvdHqx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AKlGmgdHsDZH9d6lOE-Uwcw.png" alt="It’s so beautiful."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is way more you can do but I’ve found that Card IDs and a nice link between Trello and Github is all that I need right now to keep me moving at pace. Making the process as pain free as possible means that I’m actually sticking to the process and I can move between my issues and code really easily.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;I haven’t introduced anything ground-breaking in this article but that’s because it’s probably a concept we’re all already familiar with from our day jobs. Obviously, you don’t &lt;em&gt;need&lt;/em&gt; to finish your side projects — they shouldn’t be a chore or source of stress — &lt;em&gt;but&lt;/em&gt; if you’re a little bit fed up of constantly abandoning your projects mid-way through then I really encourage you to formalise your approach a bit more and see how it works out.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>github</category>
      <category>sideprojects</category>
      <category>motivation</category>
    </item>
    <item>
      <title>Host a secure website in S3 using your own custom domain — Part II</title>
      <dc:creator>lukeojones</dc:creator>
      <pubDate>Sun, 17 May 2020 08:17:06 +0000</pubDate>
      <link>https://forem.com/lukeojones/host-a-secure-website-in-s3-using-your-own-custom-domain-part-ii-44ia</link>
      <guid>https://forem.com/lukeojones/host-a-secure-website-in-s3-using-your-own-custom-domain-part-ii-44ia</guid>
      <description>&lt;p&gt;&lt;strong&gt;Fact:&lt;/strong&gt; Studies show that having a custom domain name for your site makes you fitter, funnier and more interesting at dinner parties.&lt;/p&gt;

&lt;p&gt;OK, so like last time — I’m sort of making these up. That being said, there are advantages to setting up a custom domain name e.g. better SEO, quicker AdSense approvals and, more generally, higher trust amongst your visitors. If none of those seem tempting then what about having a more unique email address e.g. &lt;em&gt;&lt;a href="mailto:luke@connectr.com"&gt;luke@connectr.com&lt;/a&gt;&lt;/em&gt; — maybe this could actually make you more interesting at (really lame) dinner parties? P.S. I don't use that email.&lt;/p&gt;

&lt;p&gt;If you hadn’t guessed from the ‘Part II’ in the title, this article is the follow-up to &lt;a href="https://dev.to/lukeojones/host-a-secure-website-in-s3-using-your-own-custom-domain-part-i-19k5"&gt;Part I&lt;/a&gt; which stepped through how to setup an S3 bucket for static site hosting and details on configuring the CloudFront CDN.&lt;/p&gt;

&lt;p&gt;Again, like last time, I’m going to use the &lt;em&gt;.tech&lt;/em&gt; domain I purchased from GoDaddy — &lt;em&gt;connectr.tech&lt;/em&gt;. I have no major plans for this domain so if you’d like to make me a generous offer to buy it, I’m sure we can come to an agreement 🤑&lt;/p&gt;

&lt;p&gt;By the end of this article you will have transferred your custom domain to Amazon’s Route 53 service, setup a custom SSL certificate and made your S3 static side available at your custom domain. Let’s get going.&lt;/p&gt;

&lt;h4&gt;
  
  
  Part IIA — Transferring your domain name to Amazon Route 53
&lt;/h4&gt;

&lt;p&gt;Route 53 is Amazon’s highly available and scalable DNS service — I’ve dealt with other DNS services before and in my opinion the usability of Route 53 is far superior, especially if you’re primarily building out AWS infrastructure (like we are here).&lt;/p&gt;

&lt;p&gt;In this guide, I’ll be &lt;strong&gt;migrating away from GoDaddy&lt;/strong&gt; which is great because, quite frankly, using GoDaddy to manage DNS entries is like trying to drive whilst someone throws vinegar in your eyes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Login to the AWS console and open up &lt;em&gt;Route 53&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Create a new &lt;em&gt;Hosted Zone&lt;/em&gt; and complete the panel on the right by filling in your naked domain name (without the &lt;em&gt;www&lt;/em&gt;), an identifying comment and a type of &lt;em&gt;Public Hosted Zone&lt;/em&gt;.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yErnRkd6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3jxu8pc9u6p88gp034v1.png" alt="Alt Text"&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;em&gt;Create&lt;/em&gt; and you should see that a number of DNS records have been created — the &lt;em&gt;NS&lt;/em&gt; record lists a number of name servers and the &lt;em&gt;SOA&lt;/em&gt; record indicates the host name of the primary name server.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2v5__KmH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7c0x2ri84vle98pvi0m6.png" alt="Alt Text"&gt;
&lt;/li&gt;
&lt;li&gt;Now we need to export the &lt;em&gt;Zone File&lt;/em&gt; (in unix format) from the existing provider (in my case GoDaddy) so that we can import these additional records into the Route 53 record set. Your DNS provider should provide documentation on how to do this so I won’t repeat that here.&lt;/li&gt;
&lt;li&gt;Ensure that the &lt;em&gt;A&lt;/em&gt; record in your &lt;em&gt;Zone File&lt;/em&gt; includes a valid IP (typically the IP of your site) before importing. For example if your website is still ‘Parked’ then you won’t be able to use it until you update the value to an IP — we’ll be changing this record later so don’t worry about the exact value too much.&lt;/li&gt;
&lt;li&gt;Once you have your valid &lt;em&gt;Zone File&lt;/em&gt; you need to import this into Route 53 using the &lt;em&gt;Import Zone File&lt;/em&gt; (visible in the image above). Simply copy the contents of your &lt;em&gt;.txt&lt;/em&gt; file into the text field and press &lt;em&gt;Import&lt;/em&gt; to import the additional DNS entries.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PfdK4GPU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ahixfdp44u9vch3xwjsc.png" alt="Alt Text"&gt;
&lt;/li&gt;
&lt;li&gt;You now need to repoint the name servers on your existing provider (e.g. GoDaddy) to the new name servers provided in Route 53. You’ll want to check your providers documentation on this but essentially you need to add a name server for each of the four lines in the NS record within Route 53 (e.g. &lt;em&gt;ns-1446.awsdns-52.org.&lt;/em&gt; and the other three)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Part IIB — Setting up a new SSL certificate for your custom domain
&lt;/h4&gt;

&lt;p&gt;In order to provide secure access over https using your custom domain, we’ll need to create a new SSL certificate and associate it with our domain. The Amazon Certificate Manager (ACM) makes this quite simple.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open up the ACM service in the AWS console and &lt;strong&gt;change to the us-east-1 region&lt;/strong&gt; — currently, certificates created in other regions cannot be associated with CloudFront distributions correctly.&lt;/li&gt;
&lt;li&gt;Press &lt;em&gt;Request a Certificate&lt;/em&gt; and then choose to &lt;em&gt;Request a public certificate&lt;/em&gt; in step 1 of the wizard.&lt;/li&gt;
&lt;li&gt;You will now need to add your custom domain to the certificate — I would recommend adding both the naked domain e.g. &lt;em&gt;connectr.tech&lt;/em&gt; and a wildcard entry for all subdomains (e.g. &lt;em&gt;*.connectr.tech&lt;/em&gt;) so that &lt;em&gt;&lt;a href="http://www.connectr.tech"&gt;www.connectr.tech&lt;/a&gt;&lt;/em&gt; and &lt;em&gt;api.connectr.tech&lt;/em&gt; will be protected.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--W9OfFSOa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/yl7f9lcxk926hi7i9q4i.png" alt="Alt Text"&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;em&gt;Next&lt;/em&gt; and choose &lt;em&gt;DNS validation&lt;/em&gt;. Add a tag if you wish, then click &lt;em&gt;Review&lt;/em&gt; and finally &lt;em&gt;Confirm and Request&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Before ACM will provision your certificate, you need to prove you own the domain. Fortunately, because we’re now using Route 53 to manage our DNS records, it’s a simple case of expanding each entry in the validation table and clicking the button &lt;em&gt;Create Record in Route 53&lt;/em&gt;.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FGsDWXu9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/zchdjm4t7qccommzepjn.png" alt="Alt Text"&gt;
&lt;/li&gt;
&lt;li&gt;Once you’ve done this for both domains, press &lt;em&gt;Continue&lt;/em&gt; and then you’ll need to wait (up to 30 minutes) for the DNS changes to propagate. Once propagated, ACM will know that you are the true overlord of this domain, and allow you to generate the certificate. Drink a beer or maybe attempt the &lt;a href="https://www.hitc.com/en-gb/2020/04/09/the-topless-handstand-challenge-what-is-it-and-who-is-involved/"&gt;t-shirt handstand challenge&lt;/a&gt; and then come back to check the status.&lt;/li&gt;
&lt;li&gt;Eventually, your certificates should move to the &lt;em&gt;Issued&lt;/em&gt; status 😎
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c_iv_Ib6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3o9e9u37p276eyhttnt7.png" alt="Alt Text"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Part IIC — Configuring CloudFront to use your custom domain and cert
&lt;/h4&gt;

&lt;p&gt;Currently, our CloudFront distribution is associated with the S3 provided endpoint and the default CloudFront certificate — we need to amend the distribution settings to sort this out.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open up the &lt;a href="https://console.aws.amazon.com/cloudfront/home?region=us-east-1"&gt;CloudFront service&lt;/a&gt; in the AWS Console and select your distribution from the list. Press &lt;em&gt;Distribution Settings&lt;/em&gt; to see the current config.&lt;/li&gt;
&lt;li&gt;Press &lt;em&gt;Edit&lt;/em&gt; so we can start tweaking things.&lt;/li&gt;
&lt;li&gt;Under &lt;em&gt;Alternate Domain Names&lt;/em&gt; you will want to add any domains that you want your users to be able to use to access your site (e.g. &lt;em&gt;&lt;a href="http://www.connectr.tech"&gt;www.connectr.tech&lt;/a&gt;&lt;/em&gt; and &lt;em&gt;connectr.tech&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;Under &lt;em&gt;SSL Certificate&lt;/em&gt; you will need to select the &lt;em&gt;Custom SSL Certificate&lt;/em&gt; option and then select the certificate you created in Part IIB.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--te2nrpJM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/w15nmvq1kuxkt660wo2m.png" alt="Alt Text"&gt;
&lt;/li&gt;
&lt;li&gt;Save your changes and start getting ready for your next attempt at the t-shirt challenge — we’re almost done.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Add DNS aliases to direct visitors to the CloudFront Distro
&lt;/h4&gt;

&lt;p&gt;Now all we need to do is add a couple of DNS records that will ensure visitors to your site will be directed to the CloudFront distribution.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make a note of the Domain Name of your CloudFront distribution (e.g. &lt;em&gt;d32gg49xh8c4xx.cloudfront.net&lt;/em&gt;) as we’ll need this later.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Xckxku4Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/phldfj1u6sgxjj2jd2iy.png" alt="Alt Text"&gt;
&lt;/li&gt;
&lt;li&gt;Swap to the Route 53 service and select the &lt;em&gt;Hosted Zone&lt;/em&gt; for your site.&lt;/li&gt;
&lt;li&gt;Select the &lt;em&gt;A Record&lt;/em&gt; for the naked domain from the existing &lt;em&gt;Record Set&lt;/em&gt;. In the &lt;em&gt;Edit Record Set&lt;/em&gt; pane on the right, change the entry to &lt;em&gt;Alias: Yes&lt;/em&gt; so that we can use a domain name rather than an IP. Change the &lt;em&gt;Alias Target&lt;/em&gt; to the domain name of your CloudFront distribution and save the record set.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lcpkw3qF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/d8fdyt98q5ph8cttgv8f.png" alt="Alt Text"&gt;
&lt;/li&gt;
&lt;li&gt;You will almost certainly want to add another &lt;em&gt;A Record&lt;/em&gt; to direct the &lt;em&gt;www&lt;/em&gt; subdomain to the same CloudFront distribution using an Alias. You can do this by pressing &lt;em&gt;Create Record Set&lt;/em&gt; at the top of the page and then following the exact same instructions as you did for the naked domain. In some cases you may first need to remove the CNAME entry with the same name.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Great, only 27 more steps to go…only joking — that’s it. You may need to wait a while for some of the DNS settings to propagate but pretty soon you should be able to visit your new domain name and see your site, served over https using your custom SSL certificate! 😎&lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pM2HRLWn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/hnhe1zt6n1yl52jbs7kn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pM2HRLWn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/hnhe1zt6n1yl52jbs7kn.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for following along and well done for persevering. If you managed to perfect the t-shirt-handstand challenge whilst reading through this then you have my utmost respect and admiration — if you didn’t then at least you’ve got your custom domain up and running!&lt;/p&gt;

&lt;p&gt;Next time, I’ll be looking at how you can use a couple of free tools to setup a CI/CD pipeline for a simple React site 👍&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Host a secure website in S3 using your own custom domain — Part I</title>
      <dc:creator>lukeojones</dc:creator>
      <pubDate>Sun, 17 May 2020 06:27:47 +0000</pubDate>
      <link>https://forem.com/lukeojones/host-a-secure-website-in-s3-using-your-own-custom-domain-part-i-19k5</link>
      <guid>https://forem.com/lukeojones/host-a-secure-website-in-s3-using-your-own-custom-domain-part-i-19k5</guid>
      <description>&lt;p&gt;&lt;strong&gt;Fact:&lt;/strong&gt; Studies show that developers hosting their sites in S3 are healthier, wealthier and have better hair than their peers.&lt;/p&gt;

&lt;p&gt;If you weren’t convinced by the above then at least know that the combination of Amazon S3 and CloudFront offers a simple, cheap and performant way to host static sites and serve them over https. In this two-part series, we’ll spend a little time going over the steps required to get your site live and using a custom domain. We’ll cover the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Part I&lt;/strong&gt; — Configuring an S3 bucket and CloudFront distribution to host and deliver your content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part II&lt;/strong&gt; — Transferring your domain to Amazon Route 53 and configuring an SSL certificate using Amazon Certificate Manager.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each part will be pretty short — so don’t be put off by the breakdown above — the whole series can be completed in well under an hour.&lt;/p&gt;

&lt;p&gt;For the purposes of this guide, I’m going to use a &lt;em&gt;.tech&lt;/em&gt; domain I purchased from GoDaddy. I decided to go with &lt;em&gt;connectr.tech&lt;/em&gt; as it was cheap, relatively non-specific and sufficiently hipster to be used in a dev.to article 🧐&lt;/p&gt;

&lt;h4&gt;
  
  
  Part IA— Creating and configuring an S3 bucket to host your site
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Login to the AWS console and navigate to the &lt;a href="https://s3.console.aws.amazon.com/s3/bucket/create"&gt;S3 bucket creation page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a new bucket e.g. &lt;em&gt;connectr-site&lt;/em&gt; and temporarily remove all public-access blocks as shown below&lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TTHdh_kj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5y2bb31au58wewr7imz9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TTHdh_kj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5y2bb31au58wewr7imz9.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select your newly created bucket and then head to &lt;em&gt;Properties : Static Website Hosting&lt;/em&gt; to configure the static hosting options.&lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Vyh3c70p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/uacevk49im11he68dqnq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Vyh3c70p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/uacevk49im11he68dqnq.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configure the &lt;em&gt;index&lt;/em&gt; and &lt;em&gt;error&lt;/em&gt; pages as &lt;em&gt;index.html&lt;/em&gt; and &lt;em&gt;error.html&lt;/em&gt;. &lt;strong&gt;You’ll also see the public endpoint of your soon-to-be site!&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Copy the endpoint and open it up in another tab — you should receive a 403 (forbidden) response because the bucket policy has not yet been configured. To sort this out, go to the &lt;em&gt;Permissions&lt;/em&gt; tab (next to &lt;em&gt;Properties&lt;/em&gt;) and add the following &lt;em&gt;Bucket Policy&lt;/em&gt; using the &lt;em&gt;Bucket Policy Editor&lt;/em&gt;.&lt;br&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Save the bucket policy and try hitting your new public endpoint again — this time you should receive a 404 as S3 tries to serve the non-existent index.html and error.html pages. This is what we want at this stage 👍&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create an &lt;em&gt;index.html&lt;/em&gt; and &lt;em&gt;error.html&lt;/em&gt; file that you are happy to share with the world. For example, I created some ‘awesome’ pages below…&lt;br&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Whilst still in S3, select the &lt;em&gt;Overview&lt;/em&gt; tab and then upload the &lt;em&gt;error.html&lt;/em&gt; file to your S3 bucket. Make sure you provide public read access on the second page of the upload wizard before pressing &lt;em&gt;Upload&lt;/em&gt;.&lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--r2yZMZN0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5c3kkjfiuxbpmfshg21e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--r2yZMZN0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5c3kkjfiuxbpmfshg21e.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Once the upload is finished, navigate to your site’s endpoint again and your custom error page should be displayed.&lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MBrkx6Ri--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/is71l4vciln3l92evqa0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MBrkx6Ri--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/is71l4vciln3l92evqa0.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Repeat the process for your &lt;em&gt;index.html&lt;/em&gt; — when you navigate to the site again, you should see your lovingly crafted html! (notice the connection is plain http and not secure)&lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--asPzlFx---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/tocsfj059fed8bo6wju6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--asPzlFx---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/tocsfj059fed8bo6wju6.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, to improve security and make sure we’re following best practices, we can lock down the bucket policy a little more. Head back to the &lt;em&gt;Permissions&lt;/em&gt; tab for your bucket and block public access for ACLs and new bucket policies as shown:&lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8SUoTQW6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/p6cdsl9sgsfhnlswdf4z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8SUoTQW6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/p6cdsl9sgsfhnlswdf4z.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Double check you can still access your &lt;em&gt;index.html&lt;/em&gt; page and that’s it — you’re good to go.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Part IB— Configuring a CloudFront distribution for content delivery
&lt;/h4&gt;

&lt;p&gt;To improve how your users access your content, you’ll want to make use of a CDN — in this case CloudFront — to distribute your content using a global network of proxy servers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In AWS, navigate to the CloudFront service and select &lt;em&gt;Create Distribution&lt;/em&gt; to get started. When asked to choose a delivery method, choose &lt;em&gt;Web&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Under &lt;em&gt;Origin Settings&lt;/em&gt; you should attach the S3 domain name that was created in Part IA e.g. &lt;em&gt;connectr-site.s3.amazonaws.com&lt;/em&gt; 
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8rSB66wy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/f5fhhs0tm0d7in2i98qh.png" alt="Alt Text"&gt;
&lt;/li&gt;
&lt;li&gt;Secondly, you’ll want to redirect all &lt;em&gt;http&lt;/em&gt; traffic over &lt;em&gt;https&lt;/em&gt; by selecting Redirect &lt;em&gt;HTTP to HTTPS&lt;/em&gt; as the &lt;em&gt;Viewer Protocol Policy&lt;/em&gt; option
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--p5CYR7-I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/55558mapto1mfg4eifbt.png" alt="Alt Text"&gt;
&lt;/li&gt;
&lt;li&gt;Now set the &lt;em&gt;Default Root Object&lt;/em&gt; to &lt;em&gt;index.html&lt;/em&gt; (or whatever site root you may have decided to configure).
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--p9NaiYed--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/lxlt4hly9pxl7gc8jy43.png" alt="Alt Text"&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now, create the distribution and wait for it to show the status &lt;em&gt;Deployed&lt;/em&gt; in the Distributions overview table (this can take 5–10 minutes so find a cat video on Youtube). &lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OGq_YlNd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7aw24sdudk30nn2z1i6s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OGq_YlNd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7aw24sdudk30nn2z1i6s.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copy the CloudFront domain name e.g. &lt;em&gt;d32gg49xh8c4xx.cloudfront.net&lt;/em&gt; (catchy right?) and paste it into a new browser tab. If everything has gone to plan — you should see your lovingly crafted html again — this time served over the CloudFront CDN and transferred using the https protocol!&lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k1gc4veR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/j606lc3arkyxe1sbmj6o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k1gc4veR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/j606lc3arkyxe1sbmj6o.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And that’s it — you’re done for Part I. Hopefully that was relatively painless and you were able to follow along at a decent pace.&lt;br&gt;&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/lukeojones/host-a-secure-website-in-s3-using-your-own-custom-domain-part-ii-44ia"&gt;Part II&lt;/a&gt;, I’ll go through the process of transferring your custom domain to Route 53, creating an individual SSL certificate and associating these both with the CloudFront distribution we’ve just created here.&lt;br&gt;&lt;/p&gt;

&lt;p&gt;Good work! Grab a refreshing drink and smile smugly for a few minutes…&lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pMohtRwt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/onwtoyf5h6ippgizrpic.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pMohtRwt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/onwtoyf5h6ippgizrpic.jpeg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
